# Project export: Teach It

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Teach It aims to bridge the gap between teachers and students and provide learners with the opportunity to study using methods that work best for them.
- Devpost: https://devpost.com/software/teach-it-bpi794
- GitHub: https://github.com/GiwinEdwin09/TeachIt
- Video: https://www.youtube.com/embed/uNHLW3mBexA?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — GiwinEdwin (5 commits), Elizabeth Pardhe (5 commits)

## Devpost submission (written by the team)

### Inspiration

As a multi-disciplinary team, each team member preferred different learning styles. As we discussed project ideas, we shared a common thread: often, we'd struggle with a class or concept not because it was hard, but because we struggled with the teaching style. Our inspiration was bringing the "Aha!" moment to students across the world and personalizing their experiences to improve their learning through AI and technology.

### What it does

Our platform provides the necessary support to foster a growth mindset while promoting full engagement in education for both students and instructors. Teach It allows teachers to upload a range of resources based on their syllabi topics. Each of these resources gets sorted based on the tag the teacher assigns them: "auditory", "visual", or "interactive." Next, their students get to pick their preferred learning style and see the resources the teacher provided and labeled. They are free to pick more than one, or all three if so inclined, and can change their preference as the semester goes on. Finally, our AI summarizes the documents provided by the teacher and creates quizzes that are standardized across learning methods. This ensures each student is being tested on the same level as their peers.

### How we built it

We used Reflex, one of the CalHacks sponsors' products, and coded both the front and back-end in Python. We also used OpenAI to create a quizzing structure to test the students.

### Challenges we ran into

We ran into a few issues initially when creating our team, as one of our members left early on, but we were able to come up with our idea and start working on it soon after. One of our team members had technical issues when trying to run the website, but thanks to the CalHacks mentors and the sponsors tabling for Reflex, we were able to get past it!

### Accomplishments we're proud of

This is our first hackathon, and we're proud of all the effort we put into this! Even though we had a rough start, we were able to build something we're all passionate about. :)

### What we learned

We learned how to use Reflex, push and pull Git code from the VSCode terminal, use LLMs, and how to do front-end and back-end.

### What's next

After this hackathon, we want to continue working on this project and improving it! By providing it to peers, we can get a better idea how we can make it better and better help students.

## README (from the GitHub repository)

Note: DO NOT RUN 'reflex init'. It will create a new reflex.dev project inside and mess up the whole project. In the directory with rxconfig.py, run 'reflex run'.
Also when pushing a new change, DO NOT have any OpenAI keys or any other keys. This will prevent it from being pushed and is a security problem.

Presentation Link: https://www.canva.com/design/DAGUHEmuAEE/_RoJgMNQnHCybfh5zI09nQ/edit?utm_content=DAGUHEmuAEE&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton


## Detected evidence (automated analysis)

Indexed codebase: 12 recognized source files, 24 KB.
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- HTML (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (14 of 14)

```
.gitignore
CalHacksProject/__init__.py
CalHacksProject/CalHacksProject.py
CalHacksProject/components/navbar.py
CalHacksProject/pages/about.py
CalHacksProject/pages/contact.py
CalHacksProject/pages/learn.py
CalHacksProject/pages/practice.py
CalHacksProject/pages/student.py
CalHacksProject/pages/studentLearn.py
CalHacksProject/pages/teacher.py
README.md
requirements.txt
rxconfig.py
```

### Dependencies

- requirements.txt: beautifulsoup4@==4.12.3, chromadb@==0.4.24, google-cloud-aiplatform@==1.70.0, gptcache@==0.1.44, grpcio-status@==1.62.3, h2@==4.1.0, httptools@==0.6.4, langchain-cohere@==0.3.1, openai@==1.52.0, pdfplumber@==0.11.4, pip-check@==2.9, pypdf@==5.0.1, pysbd@==0.3.4, qdrant-client@==1.12.0, schema@==0.7.7, tiktoken@==0.7.0, uvloop@==0.21.0, watchfiles@==0.24.0

### Recent commits (newest first)

- Update README.md
- Update README.md
- final cal hacks version
- data base teacher works
- Merge pull request #3 from GiwinEdwin09/Elizabeth-Pardhe-patch-2-aboutfile-in-pages
- Delete contact.py
- Delete about.py
- Merge pull request #2 from GiwinEdwin09/Elizabeth-Pardhe-patch-2-about-page
- Add files via upload
- Add files via upload
- Merge pull request #1 from GiwinEdwin09/Elizabeth-Pardhe-patch-1
- Add files via upload
- add backend
- Teacher
- Update README.md
- Create README.md
- New Cal Hack Project

## Key source files (fetched from GitHub, selected and truncated for size)

### requirements.txt

```
beautifulsoup4==4.12.3
chromadb==0.4.24
google-cloud-aiplatform==1.70.0
gptcache==0.1.44
grpcio-status==1.62.3
h2==4.1.0
httptools==0.6.4
langchain-cohere==0.3.1
openai==1.52.0
pdfplumber==0.11.4
pip-check==2.9
pypdf==5.0.1
pysbd==0.3.4
qdrant-client==1.12.0
schema==0.7.7
tiktoken==0.7.0
uvloop==0.21.0
watchfiles==0.24.0

```

### rxconfig.py

```python
import reflex as rx

config = rx.Config(
    app_name="CalHacksProject",
    db_url="sqlite:///reflex.db"
)
```

### CalHacksProject/CalHacksProject.py

```python
import reflex as rx
from .components.navbar import Navbar
from rxconfig import config
from .pages import(student, teacher, studentLearn, practice)

__all__ = ["student", "teacher", "studentLearn", "practice"]

class State(rx.State):
    """The app state."""


def info() -> rx.Component:
    return rx.container(
        rx.box(
            "Learn it your Way!",

            text_align = "center",
            text_wrap = "pretty",
            font_size="2em",
            padding="1em",
        ),
        rx.box(
            "Use TeachIt to succeed in class!",

            text_align = "center",
            text_wrap = "pretty",
            font_size="2em",
            padding="1em",
        ),
    )

def options() -> rx.Component:
    return rx.container(
        rx.box(
            rx.hstack(
                rx.text("Are you a...", font_size="2em"),
                
                
                rx.link(
                    rx.button(
                        "Student", 
                        size="lg", 
                        variant="solid", 
                        radius="full", 
                        font_size="1em",
                        padding_x="1.5em",
                        padding_y="0.5em",
                    ), 
                    href="/student"
                ),
                
                
                rx.link(
                    rx.button(
                        "Teacher", 
                        size="lg", 
                        variant="solid", 
                        radius="full", 
                        font_size="1em",
                        padding_x="1.5em",
                        padding_y="0.5em",
                    ), 
                    href="/teacher"
                ),
                
                spacing="20px",  
                align_items="center",
                justify = "center"
            ),
            text_align="center",  
            font_size="1.5em",
            padding="2em",
            border_radius="10px",
            width="70%",
            margin="auto",
        )
    )




def index() -> rx.Component:
    # Welcome Page (Index)
    return rx.container(
        Navbar(),
        info(),
        options(),
    )

app = rx.App()
app.add_page(index)
```

### CalHacksProject/pages/studentLearn.py

```python
import reflex as rx
from ..components.navbar import Navbar


def practiceOrQuiz() -> rx.Component:
    return rx.vstack(
        rx.text("Choose Activity", font_size="3em"),
        rx.hstack(
            rx.button(
                "Learn", 
                size="xl", 
                variant="solid", 
                radius="full", 
                font_size="1.5em",
                padding_x="2em",
                padding_y="1em",
            ),
            rx.link(
            rx.button(
                "Practice", 
                size="xl",
                variant="solid", 
                radius="full", 
                font_size="1.5em",
                padding_x="2em",
                padding_y="1em",
            ),href = "/practice"),
            spacing="20px",  
            align_items="center", 
            justify_content="center",
        ),
        text_align="center",  
        font_size="1.5em",
        padding="6em",
        border_radius="10px",
        width="70%",
        margin="auto",
        justify_content="center",
        align_items="center",
    )



@rx.page(route="/studentLearn")
def studentLearn():
    return rx.container(
        Navbar(),
        practiceOrQuiz(),
    )
```

### CalHacksProject/pages/about.py

```python
import reflex as rx
from components.navbar import Navbar


import reflex as rx
font_style = ("Helvetica", 16, "bold"),

# Create a label with custom font
    
app = rx.App(
    stylesheets=[
        "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap",
    ],
)



def about_page() -> rx.Component:
    return rx.box(rx.vstack(
        rx.center(rx.text("About Us", font_family="IBM Plex Mono", font_size="3em", width="200px")),
        # rx.text("Contact us here!", font_family="IBM Plex Mono", font_size="2em"),
        # You can style the section here if needed
        # style={"background": "#f9fafb", "padding": "6rem 0", "position": "relative"},
        rx.text("Teach It aims to bridge the gap between teachers and students and provide learners with the opportunity to study using methods that work best for them. ", font_family="IBM Plex Mono", font_size="2em", width="800px"),
        rx.text("Ever felt the 'aha!' moment when a concept makes sense when presented in a different way? Teach It is deisgned to help teachers bring those priceless moments to students across the world through our platform.", font_family="IBM Plex Mono", font_size="2em", width="800px"),
        #rx.text("", font_family="IBM Plex Mono", font_size="2em", width="800px")
    ),
    text_align="center"
    )
#rx.flex(
    rx.center(rx.text("Example"), bg="lightblue"),
    rx.spacer(),

@rx.page(route="/contact")
def about_page():
    return rx.container(
        Navbar(),
        about_page()
    )

```

### CalHacksProject/components/navbar.py

```python
import reflex as rx
from rxconfig import config

def navbar_link(text: str, url: str) -> rx.Component:
    return rx.link(
        rx.text(text, size="4", weight="medium"), href=url
    )


def Navbar() -> rx.Component:
    return rx.box(
        rx.desktop_only(
            rx.hstack(
                rx.hstack(
                    rx.heading(
                        "TeachIt", size="7", weight="bold"
                    ),
                    align_items="center",
                ),
                rx.hstack(
                    navbar_link("Home", "/"),
                    navbar_link("About", "/#"),
                    navbar_link("Contact", "/#"),
                    spacing="5",
                ),
                rx.hstack(
                    rx.button(
                        "Sign Up",
                        size="3",
                        variant="outline",
                    ),
                    rx.button("Log In", size="3"),
                    spacing="4",
                    justify="end",
                ),
                justify="between",
                align_items="center",
            ),
        ),
        rx.mobile_and_tablet(
            rx.hstack(
                rx.hstack(
                    rx.heading(
                        "Reflex", size="6", weight="bold"
                    ),
                    align_items="center",
                ),
                rx.menu.root(
                    rx.menu.trigger(
                        rx.icon("menu", size=30)
                    ),
                    rx.menu.content(
                        rx.menu.item("Home"),
                        rx.menu.item("About"),
                        rx.menu.item("Pricing"),
                        rx.menu.item("Contact"),
                        rx.menu.separator(),
                        rx.menu.item("Log in"),
                        rx.menu.item("Sign up"),
                    ),
                    justify="end",
                ),
                justify="between",
                align_items="center",
            ),
        ),
        bg=rx.color("accent", 3),
        padding="1em",
        # position="fixed",
        # top="0px",
        # z_index="5",
        width="100%",
        border_radius="20px",
    )
```

### CalHacksProject/pages/student.py

```python
import reflex as rx
from ..components.navbar import Navbar

class StudentState(rx.State):
    option1: str = ""
    option2: str = ""
    option3: str = ""

    visTrue: bool = False
    AudTrue: bool = False
    HandTrue: bool = False

    def submit(self):
        if self.option1 == "yes":
            self.visTrue = True
        if self.option2 == "yes":
            self.AudTrue = True
        if self.option3 == "yes":
            self.HandTrue = True

        return rx.redirect('/studentLearn')

    def set_option1(self, value: str):
        self.option1 = value

    def set_option2(self, value: str):
        self.option2 = value

    def set_option3(self, value: str):
        self.option3 = value


def learningMethods() -> rx.Component:
    return rx.vstack(
        rx.text("Select Preferred Learning Method", font_size="3em"),
        rx.vstack(
            rx.hstack(
                rx.text("Visual", font_size="2em", width="150px"),
                rx.select.root(
                    rx.select.trigger(placeholder="Choose Yes or No"),
                    rx.select.content(
                        rx.select.item("Yes", value="yes"),
                        rx.select.item("No", value="no"),
                    ),
                    on_change=StudentState.set_option1,
                ),
                align = "center",
                justify = "start",
                spacing = "4"
            ),
            rx.hstack(
                rx.text("Audio", font_size="2em", width="150px"),
                rx.select.root(
                    rx.select.trigger(placeholder="Choose Yes or No"),
                    rx.select.content(
                        rx.select.item("Yes", value="yes"),
                        rx.select.item("No", value="no"),
                    ),
                    on_change=StudentState.set_option2,
                ),
                align = "center",
                justify = "start",
                spacing = "4"
            ),
            rx.hstack(
                rx.text("Interactive", font_size="2em", width="150px"),
                rx.select.root(
                    rx.select.trigger(placeholder="Choose Yes or No"),
                    rx.select.content(
                        rx.select.item("Yes", value="yes"),
                        rx.select.item("No", value="no"),
                    ),
                    on_change=StudentState.set_option3,
                ),
                align = "center",
                justify = "start",
                spacing = "4"
            ),
            spacing = "20px"
        ),
        rx.button("Submit", on_click=StudentState.submit, size="4"),
        spacing = "30px",
        justify = "center",
        align= "center",
        min_height = "80vh",
    )

@rx.page(route="/student")
def student():
    return rx.container(
        Navbar(),
        learningMethods(),
    )
```

### CalHacksProject/pages/contact.py

```python
import reflex as rx
from components.navbar import Navbar

# class MyHandler(SimpleHTTPRequestHandler):
#     def do_GET(self):
#         """Serve the form for the contact page."""
#         if self.path == "/": 
#             self.path = "/templates/index.html"
#         else:
#             self.path = self.path
        
#         return SimpleHTTPRequestHandler.do_GET(self)

#     def do_POST(self):
#         """Handle the form submission."""
#         if self.path == "/submit_form":
#             content_length = int(self.headers['Content-Length'])
#             post_data = self.rfile.read(content_length).decode('utf-8')
#             post_params = parse.parse_qs(post_data)
            
#             # Extract form data
#             name = post_params.get('name', [''])[0]
#             email = post_params.get('email', [''])[0]
#             message = post_params.get('message', [''])[0]

#             # Display the submitted information
#             self.send_response(200)
#             self.send_header('Content-type', 'text/html')
#             self.end_headers()

#             # Render the display page with the user's input
#             with open("templates/display.html", "r") as f:
#                 display_page = f.read()
            
#             # Inject user data into the display page
#             display_page = display_page.format(name=name, email=email, message=message)
#             self.wfile.write(display_page.encode())

# def run(server_class=HTTPServer, handler_class=MyHandler):
#     """Run the HTTP server."""
#     server_address = (HOST, PORT)
#     httpd = server_class(server_address, handler_class)
#     print(f"Serving on {HOST}:{PORT}")
#     httpd.serve_forever()

# if __name__ == "__main__":
#     run()

import reflex as rx
font_style = ("Helvetica", 16, "bold"),

# Create a label with custom font
   
app = rx.App(
    stylesheets=[
        "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap",
    ],
)



def contact_page() -> rx.Component:
    return rx.box(rx.vstack(
        rx.center(rx.text("Contact", font_family="IBM Plex Mono", font_size="3em", width="200px")),
        # rx.text("Contact us here!", font_family="IBM Plex Mono", font_size="2em"),
        # You can style the section here if needed
        # style={"background": "#f9fafb", "padding": "6rem 0", "position": "relative"},
        rx.text("Questions? Feel free to reach out!", font_family="IBM Plex Mono", font_size="2em", width="800px"),
        rx.text("epardhe@asu.edu", font_family="IBM Plex Mono", font_size="2em", width="800px"),
        rx.text("edwin.giwin@gmail.com", font_family="IBM Plex Mono", font_size="2em", width="800px")
    ),
    text_align="center"
    )
#rx.flex(
    rx.center(rx.text("Example"), bg="lightblue"),
    rx.spacer(),

@rx.page(route="/contact")
def contact():
    return rx.container(
        Navbar(),
        contact_page()
    )

```

### CalHacksProject/pages/practice.py

```python
import reflex as rx
from ..components.navbar import Navbar
from .student import StudentState
from sqlmodel import select
from .teacher import QuizModel 

class QuizQuestion(rx.Base):
    fileName: str
    questNum: int
    quest: str
    answOne: str
    answTwo: str
    answThree: str
    answFour: str
    ansInd: int
    typeOf: str

class PracticeState(rx.State):
    questions_data: list[QuizQuestion] = []
    current_question: int = 0
    selected_answer: str | None = None
    score: int = 0

    async def load_questions(self):
        student_state = await self.get_state(StudentState)

        query = select(QuizModel).where(
            (QuizModel.typeOf == 'Visual' if student_state.visTrue else False) |
            (QuizModel.typeOf == 'Audio' if student_state.AudTrue else False) |
            (QuizModel.typeOf == 'Hands On' if student_state.HandTrue else False)
        ).limit(10)

        with rx.session() as session:
            self.questions_data = [QuizQuestion(**q.dict()) for q in session.exec(query)]

    def submit_answer(self, answer):
        if self.current_question < len(self.questions_data):
            if answer == self.questions_data[self.current_question].ansInd:
                self.score += 1
            self.current_question += 1

    async def reset_quiz(self):
        self.current_question = 0
        self.selected_answer = None
        self.score = 0
        await self.load_questions()

    @rx.var
    def quiz_completed(self) -> bool:
        return self.current_question >= len(self.questions_data)


@rx.page(route="/practice", on_load=PracticeState.load_questions)
def practice():
    return rx.container(
        Navbar(),
        rx.vstack(
            rx.cond(
                PracticeState.questions_data.length() > 0,
                rx.cond(
                    PracticeState.quiz_completed,
                    rx.vstack(
                        rx.heading("Quiz Completed"),
                        rx.text(f"Your score: {PracticeState.score} / {PracticeState.questions_data.length()}"),
                    ),
                    rx.vstack(
                        rx.heading(f"Question {PracticeState.current_question + 1}"),
                        rx.text(PracticeState.questions_data[PracticeState.current_question].quest),
                        rx.button(
                            PracticeState.questions_data[PracticeState.current_question].answOne,
                            on_click=PracticeState.submit_answer(1),
                        ),
                        rx.button(
                            PracticeState.questions_data[PracticeState.current_question].answTwo,
                            on_click=PracticeState.submit_answer(2),
                        ),
                        rx.button(
                            PracticeState.questions_data[PracticeState.current_question].answThree,
                            on_click=PracticeState.submit_answer(3),
                        ),
                        rx.button(
                            PracticeState.questions_data[PracticeState.current_question].answFour,
                            on_click=PracticeState.submit_answer(4),
                        ),
                    ),
                ),
            ),
            justify="center",
            align="center",
            height="100vh",
            spacing="2em",
        ),
    )
```

### CalHacksProject/pages/teacher.py

```python
import reflex as rx
from ..components.navbar import Navbar
import openai
import pdfplumber
from pathlib import Path
from pydantic import BaseModel
import re
import json
from sqlmodel import select
# Set your OpenAI API key
openai.api_key = ""

class QuizModel(rx.Model, table =  True):
    fileName: str
    questNum: int
    quest: str
    answOne: str
    answTwo: str
    answThree: str
    answFour: str
    ansInd:int
    typeOf: str

class QuizFormat(BaseModel):
    question_num: int
    question:str
    answers: list[str]
    answerIndex: int

class Quiz(BaseModel):
    fullQuiz: list[QuizFormat]


class TeacherState(rx.State):
    img: list[str] = []
    ai_result: list[tuple[int, str, list[str], int]] = []
    resultFinal: str = ""
    file_learning_styles: dict[str, str] = {}
    show_alert: bool = False

    def show_success_alert(self):
        self.show_alert = True

    def hide_success_alert(self):
        self.show_alert = False

    def set_learning_style(self, filename: str, style: str):
        self.file_learning_styles[filename] = style


    def get_ai_result(self):
        return self.ai_result

    async def handle_upload(self, files: list[rx.UploadFile]):
        print("Begin")
        for file in files:
            upload_data = await file.read()
            outfile = rx.get_upload_dir() / file.filename

            # Save the uploaded file.
            with outfile.open("wb") as file_object:
                file_object.write(upload_data)

            # Update the image list (file names).
            self.img.append(file.filename)

            # Extract text from the PDF using pdfplumber
            extracted_text = self.extract_text_from_pdf(outfile)

            # Query OpenAI with the extracted text and specific prompt
            result = await self.query_openai(extracted_text, "Make a 10 question multiple-choice quiz based on the content of the pdf file. Each question should have 4 choices.")
            
            # Parse the result into a structured format
            self.ai_result = result
            
            quiz_data = json.loads(result)
            output_string = ""
            for question in quiz_data['fullQuiz']:
                question_num = question['question_num']
                question_text = question['question']
                answers = question['answers']
                correct_answer = answers[question['answerIndex']]
                with rx.session() as session:
                    db_entry = QuizModel(
                        fileName = file.filename,
                        questNum = question_num,
                        quest = question_text,
                        answOne = answers[0],
                        answTwo = answers[1],
                        answThree = answers[2],
                        answFour = answers[3],
                        ansInd = question['answerIndex'],
                        typeOf = self.file_learning_styles[file.filename]
                    )
                    session.add(db_entry)
                    session.commit()
                    
                    
                # Create a formatted string for each question
                output_string += f"Question {question_num}: {question_text}\n"
                for i, answer in enumerate(answers):
                    # Convert index to corresponding letter (a, b, c, d)
                    letter = chr(97 + i)  # 97 is the ASCII code for 'a'
                    output_string += f"  {letter}. {answer}\n"
                output_string += f"Correct Answer: {correct_answer}\n\n"


            self.resultFinal = output_string
            print("end")
            self.show_success_alert()

    def extract_text_from_pdf(self, pdf_path: Path) -> str:
        """Extracts text from the given PDF file using pdfplumber."""
        with pdfplumber.open(pdf_path) as pdf:
            full_text = ""
            for page in pdf.pages:
                full_text += page.extract_text() or ""
        return full_text

    async def query_openai(self, pdf_text: str, prompt: str) -> str:
        """Sends the PDF text to OpenAI with the specified prompt."""
        response = openai.beta.chat.completions.parse(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": "You are an assistant that creates quizzes from documents. It should have 10 questions and be multiple choice."},
                {"role": "user", "content": f"{prompt}\n\nDocument content:\n{pdf_text}"}
            ],
            response_format=Quiz,
        )
        return response.choices[0].message.content
    


# Define the learningMaterials component
def learningMaterials() -> rx.Component:
    return rx.vstack(
        rx.text("Submit teaching material", font_size="2em"),
        rx.upload(
            rx.text("Drag and drop files here or click to select files"),
            id="upload1",
            multiple=True,
            accept={
                "application/pdf": [".pdf"],
                "text": [".txt"],
            },
            max_files=20,
        ),
        rx.vstack(
            rx.foreach(
                rx.selected_files("upload1"),
                lambda file: rx.hstack(
                    rx.text(file),
                    rx.select(
                        ["Visual", "Audio", "Hands On"],
                        placeholder="Select learning style",
                        on_change=lambda value: TeacherState.set_learning_style(file, value),
                    ),
                )
            ),
            justify="center",
            align="center"
        ),
        rx.cond(
            rx.selected_files("upload1").length() > 0,
            rx.button(
                "Upload",
                on_click=TeacherState.handle_upload(rx.upload_files(upload_id="upload1")),
                size="4"
            ),
            rx.text("Please select at least one file to upload.", color="white"),
        ),
        rx.button(
          
[truncated — 1100 more characters]
```