Spaces:
Sleeping
Sleeping
my pull
#1
by
puzan789
- opened
- .env.example +0 -2
- .github/workflows/lint.yml +0 -23
- .gitignore +0 -9
- Dockerfile +0 -20
- app/api/__init__.py +0 -1
- app/api/routes.py +0 -56
- app/core/__init__.py +0 -0
- app/core/config.py +0 -10
- app/core/exceptions.py +0 -15
- app/core/logging.py +0 -11
- app/schemas/__init__.py +0 -1
- app/schemas/schemas.py +0 -18
- app/services/__init__.py +0 -0
- app/services/gemini.py +0 -50
- app/templates/index.html +0 -361
- app/utils/__init__.py +0 -0
- main.py +0 -10
- requirements.txt +0 -51
.env.example
DELETED
@@ -1,2 +0,0 @@
|
|
1 |
-
GOOGLE_API_KEY=<google_api_key>
|
2 |
-
DATABASE_URL=postgresql+asyncpg://<username>:<password>@<host>:<port>/<database_name>
|
|
|
|
|
|
.github/workflows/lint.yml
DELETED
@@ -1,23 +0,0 @@
|
|
1 |
-
name: Ruff lint
|
2 |
-
|
3 |
-
on: [push, pull_request]
|
4 |
-
|
5 |
-
jobs:
|
6 |
-
ruff:
|
7 |
-
runs-on: ubuntu-latest
|
8 |
-
steps:
|
9 |
-
- name: Checkout code
|
10 |
-
uses: actions/checkout@v2
|
11 |
-
|
12 |
-
- name: Set up Python
|
13 |
-
uses: actions/setup-python@v2
|
14 |
-
with:
|
15 |
-
python-version: '3.10'
|
16 |
-
|
17 |
-
- name: Install dependencies
|
18 |
-
run: |
|
19 |
-
python -m pip install --upgrade pip
|
20 |
-
pip install ruff
|
21 |
-
|
22 |
-
- name: Run Ruff
|
23 |
-
run: ruff check .
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
__pycache__/
|
2 |
-
.env
|
3 |
-
|
4 |
-
venv/
|
5 |
-
|
6 |
-
*.log
|
7 |
-
|
8 |
-
app.log
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
DELETED
@@ -1,20 +0,0 @@
|
|
1 |
-
# Use official Python image
|
2 |
-
FROM python:3.10-slim
|
3 |
-
|
4 |
-
# Set work directory
|
5 |
-
WORKDIR /app
|
6 |
-
|
7 |
-
# Copy requirements
|
8 |
-
COPY requirements.txt .
|
9 |
-
|
10 |
-
# Install dependencies
|
11 |
-
RUN pip install --no-cache-dir -r requirements.txt
|
12 |
-
|
13 |
-
# Copy app code
|
14 |
-
COPY . .
|
15 |
-
|
16 |
-
# Expose port (Hugging Face Spaces expects 7860 or 8000, but FastAPI default is 8000)
|
17 |
-
EXPOSE 7860
|
18 |
-
|
19 |
-
# Start FastAPI with uvicorn on port 7860 for Hugging Face Spaces
|
20 |
-
CMD ["uvicorn", "app.api.routes:router", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/__init__.py
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
from .routes import router #noqa
|
|
|
|
app/api/routes.py
DELETED
@@ -1,56 +0,0 @@
|
|
1 |
-
from fastapi import APIRouter,status, Query, Request
|
2 |
-
from fastapi.responses import HTMLResponse
|
3 |
-
from app.services.gemini import GenerateAnswer
|
4 |
-
from app.schemas import SOLOLevel, MCQResponse
|
5 |
-
from app.core.exceptions import CustomException
|
6 |
-
from fastapi.templating import Jinja2Templates
|
7 |
-
from app.core.logging import logger
|
8 |
-
|
9 |
-
templates = Jinja2Templates(directory="app/templates")
|
10 |
-
|
11 |
-
answer_service = GenerateAnswer()
|
12 |
-
|
13 |
-
router = APIRouter()
|
14 |
-
|
15 |
-
|
16 |
-
@router.get("/generate_mcq", response_class=HTMLResponse)
|
17 |
-
|
18 |
-
async def generate_mcq(
|
19 |
-
request: Request,
|
20 |
-
topic: str = Query(default=None),
|
21 |
-
solo_level: SOLOLevel = Query(default=None),
|
22 |
-
):
|
23 |
-
"""
|
24 |
-
Generate a multiple-choice question (MCQ) based on the provided topic and SOLO level in jinja template.
|
25 |
-
"""
|
26 |
-
if topic and solo_level:
|
27 |
-
mcq = await answer_service.generate_mcq(
|
28 |
-
topic=topic, solo_level=solo_level
|
29 |
-
) # generates mcq
|
30 |
-
if not mcq:
|
31 |
-
raise CustomException(detail="No MCQ generated.")
|
32 |
-
return templates.TemplateResponse(
|
33 |
-
"index.html", {"request": request, "mcq": mcq}
|
34 |
-
)
|
35 |
-
return templates.TemplateResponse("index.html", {"request": request})
|
36 |
-
|
37 |
-
|
38 |
-
@router.get(
|
39 |
-
"/api/generate_mcq", status_code=status.HTTP_200_OK, tags=["MCQ Generation "]
|
40 |
-
)
|
41 |
-
async def generate_mcq_api(
|
42 |
-
topic: str,
|
43 |
-
solo_level: SOLOLevel = Query(...),
|
44 |
-
) -> MCQResponse:
|
45 |
-
"""
|
46 |
-
Generate a multiple-choice question (MCQ) based on the provided topic and SOLO level.
|
47 |
-
"""
|
48 |
-
try:
|
49 |
-
mcq = await answer_service.generate_mcq(topic=topic, solo_level=solo_level)
|
50 |
-
if not mcq:
|
51 |
-
raise CustomException(detail="No MCQ generated.")
|
52 |
-
return mcq
|
53 |
-
|
54 |
-
except Exception:
|
55 |
-
logger.exception("Unexpected error while generating MCQ.")
|
56 |
-
raise CustomException(detail="Internal server error during MCQ generation.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/core/__init__.py
DELETED
File without changes
|
app/core/config.py
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
2 |
-
|
3 |
-
class Settings(BaseSettings):
|
4 |
-
GOOGLE_API_KEY: str
|
5 |
-
|
6 |
-
model_config=SettingsConfigDict(
|
7 |
-
env_file=".env"
|
8 |
-
)
|
9 |
-
|
10 |
-
settings = Settings()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/core/exceptions.py
DELETED
@@ -1,15 +0,0 @@
|
|
1 |
-
from http import HTTPStatus
|
2 |
-
from fastapi import HTTPException, status
|
3 |
-
|
4 |
-
|
5 |
-
class CustomException(HTTPException):
|
6 |
-
def __init__(
|
7 |
-
self,
|
8 |
-
status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR,
|
9 |
-
detail: str | None = None,
|
10 |
-
):
|
11 |
-
if not detail:
|
12 |
-
detail = HTTPStatus(status_code).description
|
13 |
-
|
14 |
-
super().__init__(status_code=status_code, detail=detail)
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/core/logging.py
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
import logging
|
2 |
-
|
3 |
-
def logger_setup():
|
4 |
-
logging.basicConfig(
|
5 |
-
level=logging.INFO,
|
6 |
-
format="%(asctime)s - %(levelname)s - %(message)s"
|
7 |
-
)
|
8 |
-
logger = logging.getLogger(__name__)
|
9 |
-
return logger
|
10 |
-
|
11 |
-
logger = logger_setup()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/schemas/__init__.py
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
from .schemas import * #noqa
|
|
|
|
app/schemas/schemas.py
DELETED
@@ -1,18 +0,0 @@
|
|
1 |
-
from enum import Enum
|
2 |
-
from pydantic import BaseModel, Field
|
3 |
-
|
4 |
-
class SOLOLevel(str,Enum):
|
5 |
-
"""
|
6 |
-
Enum for SOLO levels.
|
7 |
-
"""
|
8 |
-
UNISTRUCTURAL = "unistructural"
|
9 |
-
MULTISTRUCTURAL = "multistructural"
|
10 |
-
|
11 |
-
|
12 |
-
class MCQResponse(BaseModel):
|
13 |
-
"""
|
14 |
-
Response model for MCQ generation.
|
15 |
-
"""
|
16 |
-
question_text: str = Field(..., description="The generated question text.")
|
17 |
-
options: list[str] = Field(..., description="List of answer options.")
|
18 |
-
correct_answer: str = Field(..., description="The correct answer from the options.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/services/__init__.py
DELETED
File without changes
|
app/services/gemini.py
DELETED
@@ -1,50 +0,0 @@
|
|
1 |
-
from langchain_google_genai import ChatGoogleGenerativeAI
|
2 |
-
from langchain_core.prompts import PromptTemplate
|
3 |
-
from app.core.config import settings
|
4 |
-
from app.schemas import MCQResponse
|
5 |
-
|
6 |
-
|
7 |
-
class GenerateAnswer:
|
8 |
-
"""
|
9 |
-
Class to generate answers using Google Gemini API.
|
10 |
-
"""
|
11 |
-
|
12 |
-
def __init__(self):
|
13 |
-
self.llm = ChatGoogleGenerativeAI(
|
14 |
-
model="gemini-2.0-flash",
|
15 |
-
temperature=0.6,
|
16 |
-
api_key=settings.GOOGLE_API_KEY,
|
17 |
-
)
|
18 |
-
|
19 |
-
async def generate_mcq(self, topic: str, solo_level: str):
|
20 |
-
"""
|
21 |
-
Generate an answer to the given question using Google Gemini API.
|
22 |
-
"""
|
23 |
-
prompt = PromptTemplate(
|
24 |
-
template="""You are an AI tutor. Based on the SOLO taxonomy level and the content snippet provided, generate a single multiple-choice question (MCQ) that matches the SOLO level.
|
25 |
-
|
26 |
-
Content Snippet:
|
27 |
-
\"\"\"
|
28 |
-
Photosynthesis is the process by which plants use sunlight, water, and carbon dioxide to create glucose and oxygen. Chlorophyll absorbs sunlight.
|
29 |
-
\"\"\"
|
30 |
-
|
31 |
-
|
32 |
-
SOLO Level: {solo_level}
|
33 |
-
You should be based on this Topic: {topic}
|
34 |
-
|
35 |
-
SOLO Level Consideration:
|
36 |
-
- Unistructural: Focus on recalling a single piece of information from the content_snippet.
|
37 |
-
- Multistructural: Focus on recalling several pieces of information from the content_snippet.
|
38 |
-
|
39 |
-
Generate one MCQ with:
|
40 |
-
- "question_text": A single question aligned to the SOLO level
|
41 |
-
- "options": 3–4 plausible answer choices
|
42 |
-
- "correct_answer": The correct answer (must match one of the options)""",
|
43 |
-
input_variables=["topic", "solo_level"],
|
44 |
-
)
|
45 |
-
model=self.llm.with_structured_output(MCQResponse)
|
46 |
-
chain=prompt | model
|
47 |
-
response= await chain.ainvoke({"topic": topic, "solo_level": solo_level})
|
48 |
-
return response
|
49 |
-
|
50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/templates/index.html
DELETED
@@ -1,361 +0,0 @@
|
|
1 |
-
<!DOCTYPE html>
|
2 |
-
<html>
|
3 |
-
|
4 |
-
<head>
|
5 |
-
<title>MCQ Generator</title>
|
6 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
-
<style>
|
8 |
-
:root {
|
9 |
-
--primary: #4a6fa5;
|
10 |
-
--secondary: #6a98d8;
|
11 |
-
--accent: #ff6b6b;
|
12 |
-
--light: #f8f9fa;
|
13 |
-
--dark: #343a40;
|
14 |
-
--success: #28a745;
|
15 |
-
--danger: #dc3545;
|
16 |
-
}
|
17 |
-
|
18 |
-
* {
|
19 |
-
margin: 0;
|
20 |
-
padding: 0;
|
21 |
-
box-sizing: border-box;
|
22 |
-
transition: all 0.3s ease;
|
23 |
-
}
|
24 |
-
|
25 |
-
body {
|
26 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
27 |
-
line-height: 1.6;
|
28 |
-
color: var(--dark);
|
29 |
-
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
30 |
-
min-height: 100vh;
|
31 |
-
padding: 0;
|
32 |
-
}
|
33 |
-
|
34 |
-
.container {
|
35 |
-
max-width: 800px;
|
36 |
-
margin: 0 auto;
|
37 |
-
padding: 20px;
|
38 |
-
}
|
39 |
-
|
40 |
-
header {
|
41 |
-
background-color: var(--primary);
|
42 |
-
color: white;
|
43 |
-
padding: 20px 0;
|
44 |
-
border-radius: 0 0 10px 10px;
|
45 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
46 |
-
text-align: center;
|
47 |
-
margin-bottom: 30px;
|
48 |
-
}
|
49 |
-
|
50 |
-
header h1 {
|
51 |
-
margin: 0;
|
52 |
-
font-size: 2.5rem;
|
53 |
-
font-weight: 700;
|
54 |
-
}
|
55 |
-
|
56 |
-
header p {
|
57 |
-
margin-top: 10px;
|
58 |
-
font-size: 1.2rem;
|
59 |
-
opacity: 0.9;
|
60 |
-
}
|
61 |
-
|
62 |
-
.card {
|
63 |
-
background: white;
|
64 |
-
border-radius: 12px;
|
65 |
-
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
66 |
-
padding: 30px;
|
67 |
-
margin-bottom: 30px;
|
68 |
-
position: relative;
|
69 |
-
overflow: hidden;
|
70 |
-
}
|
71 |
-
|
72 |
-
.card::before {
|
73 |
-
content: '';
|
74 |
-
position: absolute;
|
75 |
-
top: 0;
|
76 |
-
left: 0;
|
77 |
-
width: 100%;
|
78 |
-
height: 5px;
|
79 |
-
background: linear-gradient(90deg, var(--primary), var(--secondary), var(--accent));
|
80 |
-
}
|
81 |
-
|
82 |
-
.form-group {
|
83 |
-
margin-bottom: 20px;
|
84 |
-
}
|
85 |
-
|
86 |
-
label {
|
87 |
-
display: block;
|
88 |
-
margin-bottom: 8px;
|
89 |
-
font-weight: 600;
|
90 |
-
color: var(--dark);
|
91 |
-
}
|
92 |
-
|
93 |
-
input,
|
94 |
-
select {
|
95 |
-
width: 100%;
|
96 |
-
padding: 12px 15px;
|
97 |
-
border: 2px solid #dee2e6;
|
98 |
-
border-radius: 8px;
|
99 |
-
font-size: 16px;
|
100 |
-
color: var(--dark);
|
101 |
-
}
|
102 |
-
|
103 |
-
input:focus,
|
104 |
-
select:focus {
|
105 |
-
border-color: var(--secondary);
|
106 |
-
box-shadow: 0 0 0 3px rgba(106, 152, 216, 0.25);
|
107 |
-
outline: none;
|
108 |
-
}
|
109 |
-
|
110 |
-
.btn {
|
111 |
-
display: inline-block;
|
112 |
-
font-weight: 600;
|
113 |
-
text-align: center;
|
114 |
-
white-space: nowrap;
|
115 |
-
vertical-align: middle;
|
116 |
-
user-select: none;
|
117 |
-
border: none;
|
118 |
-
padding: 12px 24px;
|
119 |
-
font-size: 16px;
|
120 |
-
line-height: 1.5;
|
121 |
-
border-radius: 8px;
|
122 |
-
cursor: pointer;
|
123 |
-
transition: all 0.3s ease;
|
124 |
-
}
|
125 |
-
|
126 |
-
.btn-primary {
|
127 |
-
color: white;
|
128 |
-
background: linear-gradient(45deg, var(--primary), var(--secondary));
|
129 |
-
box-shadow: 0 4px 6px rgba(74, 111, 165, 0.2);
|
130 |
-
}
|
131 |
-
|
132 |
-
.btn-primary:hover {
|
133 |
-
transform: translateY(-2px);
|
134 |
-
box-shadow: 0 6px 8px rgba(74, 111, 165, 0.3);
|
135 |
-
}
|
136 |
-
|
137 |
-
.btn-block {
|
138 |
-
display: block;
|
139 |
-
width: 100%;
|
140 |
-
}
|
141 |
-
|
142 |
-
.question {
|
143 |
-
background: white;
|
144 |
-
border-radius: 12px;
|
145 |
-
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
146 |
-
padding: 25px;
|
147 |
-
margin-top: 30px;
|
148 |
-
position: relative;
|
149 |
-
border-left: 5px solid var(--primary);
|
150 |
-
}
|
151 |
-
|
152 |
-
.question strong {
|
153 |
-
color: var(--primary);
|
154 |
-
font-size: 1.2rem;
|
155 |
-
margin-bottom: 15px;
|
156 |
-
display: block;
|
157 |
-
}
|
158 |
-
|
159 |
-
.question ul {
|
160 |
-
list-style-type: none;
|
161 |
-
padding: 0;
|
162 |
-
margin: 20px 0;
|
163 |
-
}
|
164 |
-
|
165 |
-
.question li {
|
166 |
-
padding: 10px 15px;
|
167 |
-
border: 1px solid #dee2e6;
|
168 |
-
border-radius: 8px;
|
169 |
-
margin-bottom: 10px;
|
170 |
-
transition: all 0.2s ease;
|
171 |
-
cursor: pointer;
|
172 |
-
}
|
173 |
-
|
174 |
-
.question li:hover {
|
175 |
-
background-color: #f8f9fa;
|
176 |
-
transform: translateX(5px);
|
177 |
-
}
|
178 |
-
|
179 |
-
.correct-answer {
|
180 |
-
margin-top: 20px;
|
181 |
-
padding: 15px;
|
182 |
-
background-color: rgba(40, 167, 69, 0.1);
|
183 |
-
border-left: 3px solid var(--success);
|
184 |
-
border-radius: 8px;
|
185 |
-
display: none;
|
186 |
-
}
|
187 |
-
|
188 |
-
.correct-answer strong {
|
189 |
-
color: var(--success);
|
190 |
-
font-size: 1.1rem;
|
191 |
-
margin-bottom: 5px;
|
192 |
-
display: block;
|
193 |
-
}
|
194 |
-
|
195 |
-
.action-buttons {
|
196 |
-
margin-top: 15px;
|
197 |
-
display: none;
|
198 |
-
}
|
199 |
-
|
200 |
-
.btn-retry {
|
201 |
-
background-color: var(--primary);
|
202 |
-
color: white;
|
203 |
-
margin-right: 10px;
|
204 |
-
}
|
205 |
-
|
206 |
-
.btn-show-answer {
|
207 |
-
background-color: var(--secondary);
|
208 |
-
color: white;
|
209 |
-
}
|
210 |
-
|
211 |
-
footer {
|
212 |
-
text-align: center;
|
213 |
-
padding: 20px;
|
214 |
-
margin-top: 30px;
|
215 |
-
color: var(--dark);
|
216 |
-
font-size: 0.9rem;
|
217 |
-
}
|
218 |
-
|
219 |
-
/* Responsive design */
|
220 |
-
@media (max-width: 768px) {
|
221 |
-
.container {
|
222 |
-
padding: 10px;
|
223 |
-
}
|
224 |
-
|
225 |
-
header h1 {
|
226 |
-
font-size: 2rem;
|
227 |
-
}
|
228 |
-
|
229 |
-
.card {
|
230 |
-
padding: 20px;
|
231 |
-
}
|
232 |
-
|
233 |
-
.btn {
|
234 |
-
padding: 10px 20px;
|
235 |
-
}
|
236 |
-
}
|
237 |
-
</style>
|
238 |
-
</head>
|
239 |
-
|
240 |
-
<body>
|
241 |
-
<div class="container">
|
242 |
-
<header>
|
243 |
-
<h1>MCQ Generator</h1>
|
244 |
-
<p>Create custom multiple-choice questions </p>
|
245 |
-
</header>
|
246 |
-
|
247 |
-
<div class="card">
|
248 |
-
<form method="get" action="/generate_mcq">
|
249 |
-
<div class="form-group">
|
250 |
-
<label for="topic">Topic:</label>
|
251 |
-
<input type="text" id="topic" name="topic" placeholder="Enter a subject or topic" required>
|
252 |
-
</div>
|
253 |
-
|
254 |
-
<div class="form-group">
|
255 |
-
<label for="solo_level">SOLO Level:</label>
|
256 |
-
<select id="solo_level" name="solo_level" required>
|
257 |
-
<option value="" disabled selected>Select a level</option>
|
258 |
-
<option value="unistructural">Unistructural (Basic facts)</option>
|
259 |
-
<option value="multistructural">Multistructural (Multiple concepts)</option>
|
260 |
-
|
261 |
-
</select>
|
262 |
-
</div>
|
263 |
-
|
264 |
-
<button type="submit" class="btn btn-primary btn-block">Generate Question</button>
|
265 |
-
</form>
|
266 |
-
</div>
|
267 |
-
|
268 |
-
{% if mcq %}
|
269 |
-
<div class="question">
|
270 |
-
<strong>Question:</strong> {{ mcq.question_text }}
|
271 |
-
|
272 |
-
<ul id="options-list">
|
273 |
-
{% for option in mcq.options %}
|
274 |
-
<li data-correct="{{ 'true' if option == mcq.correct_answer else 'false' }}"
|
275 |
-
onclick="checkAnswer(this)">{{ option }}</li>
|
276 |
-
{% endfor %}
|
277 |
-
</ul>
|
278 |
-
|
279 |
-
<div class="action-buttons" id="action-buttons">
|
280 |
-
<button class="btn btn-retry" onclick="resetQuiz()">Try Again</button>
|
281 |
-
<button class="btn btn-show-answer" onclick="showAnswer()">Show Answer</button>
|
282 |
-
</div>
|
283 |
-
|
284 |
-
<div class="correct-answer" id="feedback">
|
285 |
-
<strong>✅ Correct Answer:</strong> {{ mcq.correct_answer }}
|
286 |
-
</div>
|
287 |
-
</div>
|
288 |
-
{% endif %}
|
289 |
-
|
290 |
-
<footer>
|
291 |
-
<p>MCQ Generator</p>
|
292 |
-
</footer>
|
293 |
-
</div>
|
294 |
-
|
295 |
-
<script>
|
296 |
-
// Add some simple interactivity
|
297 |
-
document.addEventListener('DOMContentLoaded', function () {
|
298 |
-
// Highlight form fields on focus
|
299 |
-
const formElements = document.querySelectorAll('input, select');
|
300 |
-
formElements.forEach(element => {
|
301 |
-
element.addEventListener('focus', function () {
|
302 |
-
this.style.transform = 'translateY(-2px)';
|
303 |
-
});
|
304 |
-
|
305 |
-
element.addEventListener('blur', function () {
|
306 |
-
this.style.transform = 'translateY(0)';
|
307 |
-
});
|
308 |
-
});
|
309 |
-
});
|
310 |
-
|
311 |
-
// MCQ interaction functions
|
312 |
-
function checkAnswer(selectedLi) {
|
313 |
-
const isCorrect = selectedLi.dataset.correct === 'true';
|
314 |
-
const options = document.querySelectorAll('#options-list li');
|
315 |
-
|
316 |
-
// Disable all options
|
317 |
-
options.forEach(opt => {
|
318 |
-
opt.onclick = null;
|
319 |
-
opt.style.cursor = 'default';
|
320 |
-
});
|
321 |
-
|
322 |
-
if (isCorrect) {
|
323 |
-
selectedLi.style.backgroundColor = 'rgba(40, 167, 69, 0.1)';
|
324 |
-
selectedLi.style.borderColor = 'var(--success)';
|
325 |
-
document.getElementById('feedback').style.display = 'block';
|
326 |
-
} else {
|
327 |
-
selectedLi.style.backgroundColor = 'rgba(220, 53, 69, 0.1)';
|
328 |
-
selectedLi.style.borderColor = 'var(--danger)';
|
329 |
-
document.getElementById('action-buttons').style.display = 'block';
|
330 |
-
}
|
331 |
-
}
|
332 |
-
|
333 |
-
function resetQuiz() {
|
334 |
-
const options = document.querySelectorAll('#options-list li');
|
335 |
-
options.forEach(opt => {
|
336 |
-
opt.style.backgroundColor = '';
|
337 |
-
opt.style.borderColor = '#dee2e6';
|
338 |
-
opt.onclick = function () { checkAnswer(this); };
|
339 |
-
opt.style.cursor = 'pointer';
|
340 |
-
});
|
341 |
-
|
342 |
-
document.getElementById('action-buttons').style.display = 'none';
|
343 |
-
document.getElementById('feedback').style.display = 'none';
|
344 |
-
}
|
345 |
-
|
346 |
-
function showAnswer() {
|
347 |
-
const options = document.querySelectorAll('#options-list li');
|
348 |
-
options.forEach(opt => {
|
349 |
-
if (opt.dataset.correct === 'true') {
|
350 |
-
opt.style.backgroundColor = 'rgba(40, 167, 69, 0.1)';
|
351 |
-
opt.style.borderColor = 'var(--success)';
|
352 |
-
}
|
353 |
-
});
|
354 |
-
|
355 |
-
document.getElementById('feedback').style.display = 'block';
|
356 |
-
document.getElementById('action-buttons').style.display = 'none';
|
357 |
-
}
|
358 |
-
</script>
|
359 |
-
</body>
|
360 |
-
|
361 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/utils/__init__.py
DELETED
File without changes
|
main.py
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
from fastapi import FastAPI
|
2 |
-
from app.api import router
|
3 |
-
import uvicorn
|
4 |
-
|
5 |
-
app=FastAPI()
|
6 |
-
app.include_router(router)
|
7 |
-
|
8 |
-
|
9 |
-
if __name__=="__main__":
|
10 |
-
uvicorn.run(app, host="0.0.0.0", port=3000)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
DELETED
@@ -1,51 +0,0 @@
|
|
1 |
-
annotated-types==0.7.0
|
2 |
-
anyio==4.9.0
|
3 |
-
cachetools==5.5.2
|
4 |
-
certifi==2025.4.26
|
5 |
-
charset-normalizer==3.4.2
|
6 |
-
click==8.1.8
|
7 |
-
fastapi==0.115.12
|
8 |
-
filetype==1.2.0
|
9 |
-
google-ai-generativelanguage==0.6.18
|
10 |
-
google-api-core==2.24.2
|
11 |
-
google-auth==2.40.1
|
12 |
-
googleapis-common-protos==1.70.0
|
13 |
-
greenlet==3.2.1
|
14 |
-
grpcio==1.71.0
|
15 |
-
grpcio-status==1.71.0
|
16 |
-
h11==0.16.0
|
17 |
-
httpcore==1.0.9
|
18 |
-
httpx==0.28.1
|
19 |
-
idna==3.10
|
20 |
-
Jinja2==3.1.6
|
21 |
-
jsonpatch==1.33
|
22 |
-
jsonpointer==3.0.0
|
23 |
-
langchain-core==0.3.59
|
24 |
-
langchain-google-genai==2.1.4
|
25 |
-
langsmith==0.3.42
|
26 |
-
Mako==1.3.10
|
27 |
-
MarkupSafe==3.0.2
|
28 |
-
orjson==3.10.18
|
29 |
-
packaging==24.2
|
30 |
-
proto-plus==1.26.1
|
31 |
-
protobuf==5.29.4
|
32 |
-
pyasn1==0.6.1
|
33 |
-
pyasn1_modules==0.4.2
|
34 |
-
pydantic==2.11.4
|
35 |
-
pydantic-settings==2.9.1
|
36 |
-
pydantic_core==2.33.2
|
37 |
-
python-dotenv==1.1.0
|
38 |
-
PyYAML==6.0.2
|
39 |
-
requests==2.32.3
|
40 |
-
requests-toolbelt==1.0.0
|
41 |
-
rsa==4.9.1
|
42 |
-
ruff==0.11.8
|
43 |
-
sniffio==1.3.1
|
44 |
-
SQLAlchemy==2.0.40
|
45 |
-
starlette==0.46.2
|
46 |
-
tenacity==9.1.2
|
47 |
-
typing-inspection==0.4.0
|
48 |
-
typing_extensions==4.13.2
|
49 |
-
urllib3==2.4.0
|
50 |
-
uvicorn==0.34.2
|
51 |
-
zstandard==0.23.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|