ayush2917 commited on
Commit
acac435
·
verified ·
1 Parent(s): fc19b87

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +166 -0
app/main.py CHANGED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/main.py
2
+ from fastapi import FastAPI, Depends, HTTPException, status
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.responses import FileResponse
5
+ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
6
+ from sqlalchemy.orm import Session
7
+ from app.db.database import Base, engine, SessionLocal
8
+ from app.db import crud
9
+ from app.models.request_models import GenerateRequest, ChatRequest, AuthRequest
10
+ from app.services.orchestrator.build_question_set import build_question_set
11
+ from app.services.scheduler import start_scheduler
12
+ from app.services.chatbot_service import ask_chatbot
13
+ from app.utils import auth as auth_utils
14
+ from app.db import models as dbmodels
15
+ import json, datetime, os
16
+
17
+ # create DB tables
18
+ Base.metadata.create_all(bind=engine)
19
+
20
+ app = FastAPI(title="GATE Chemical Engineering Multi-Model")
21
+
22
+ # serve frontend static
23
+ app.mount("/static", StaticFiles(directory="frontend"), name="static")
24
+
25
+ def get_db():
26
+ db = SessionLocal()
27
+ try:
28
+ yield db
29
+ finally:
30
+ db.close()
31
+
32
+ bearer = HTTPBearer()
33
+
34
+ def get_current_user(creds: HTTPAuthorizationCredentials = Depends(bearer), db: Session = Depends(get_db)):
35
+ token = creds.credentials
36
+ payload = auth_utils.decode_access_token(token)
37
+ if not payload:
38
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
39
+ username = payload.get("username")
40
+ uid = payload.get("user_id")
41
+ user = crud.get_user_by_username(db, username)
42
+ if not user or user.id != uid:
43
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
44
+ return user
45
+
46
+ @app.get("/")
47
+ def ui():
48
+ return FileResponse("frontend/index.html")
49
+
50
+ # Auth
51
+ @app.post("/register")
52
+ def register(payload: dict, db: Session = Depends(get_db)):
53
+ username = payload.get("username")
54
+ email = payload.get("email")
55
+ password = payload.get("password")
56
+ if not username or not email or not password:
57
+ raise HTTPException(status_code=400, detail="username/email/password required")
58
+ if crud.get_user_by_username(db, username):
59
+ raise HTTPException(status_code=400, detail="username taken")
60
+ if crud.get_user_by_email(db, email):
61
+ raise HTTPException(status_code=400, detail="email taken")
62
+ user = crud.create_user(db, username, email, password)
63
+ token = auth_utils.create_access_token({"user_id": user.id, "username": user.username})
64
+ return {"access_token": token, "token_type": "bearer", "user": {"id": user.id, "username": user.username}}
65
+
66
+ @app.post("/login")
67
+ def login(payload: dict, db: Session = Depends(get_db)):
68
+ username = payload.get("username")
69
+ password = payload.get("password")
70
+ if not username or not password:
71
+ raise HTTPException(status_code=400, detail="username/password required")
72
+ user = crud.get_user_by_username(db, username)
73
+ if not user or not crud.verify_password(password, user.hashed_password):
74
+ raise HTTPException(status_code=401, detail="invalid credentials")
75
+ token = auth_utils.create_access_token({"user_id": user.id, "username": user.username})
76
+ return {"access_token": token, "token_type": "bearer", "user": {"id": user.id, "username": user.username}}
77
+
78
+ # Generate questions (multi-model orchestrator)
79
+ @app.post("/generate")
80
+ async def generate(req: GenerateRequest):
81
+ topic = getattr(req, "topic", "General")
82
+ num = getattr(req, "num_questions", 10)
83
+ qset = await build_question_set(topic=topic, num=num)
84
+ return qset
85
+
86
+ @app.get("/daily")
87
+ def get_daily():
88
+ path = "app/data/daily_questions.json"
89
+ if os.path.exists(path):
90
+ with open(path, "r") as f:
91
+ return json.load(f)
92
+ return {"error": "Daily not ready"}
93
+
94
+ # Save quiz (auth required)
95
+ @app.post("/save-quiz")
96
+ def save_quiz(payload: dict, user: dbmodels.User = Depends(get_current_user), db: Session = Depends(get_db)):
97
+ attempt_id = crud.save_quiz_attempt(db, user.id, payload)
98
+ return {"status": "saved", "attempt_id": attempt_id}
99
+
100
+ @app.get("/attempts")
101
+ def list_attempts(subject: str = None, start: str = None, end: str = None, db: Session = Depends(get_db), user: dbmodels.User = Depends(get_current_user)):
102
+ s, e = None, None
103
+ if start:
104
+ s = datetime.datetime.fromisoformat(start)
105
+ if end:
106
+ e = datetime.datetime.fromisoformat(end)
107
+ rows = crud.get_user_attempts(db, user.id, subject=subject, start=s, end=e)
108
+ out = []
109
+ for r in rows:
110
+ out.append({
111
+ "id": r.id,
112
+ "subject": r.subject,
113
+ "topic": r.topic,
114
+ "total_questions": r.total_questions,
115
+ "correct_answers": r.correct_answers,
116
+ "percentage": r.percentage,
117
+ "timestamp": r.timestamp.isoformat()
118
+ })
119
+ return out
120
+
121
+ @app.get("/attempts/{attempt_id}")
122
+ def attempt_details(attempt_id: int, db: Session = Depends(get_db), user: dbmodels.User = Depends(get_current_user)):
123
+ att = db.query(dbmodels.QuizAttempt).filter(dbmodels.QuizAttempt.id == attempt_id, dbmodels.QuizAttempt.user_id == user.id).first()
124
+ if not att:
125
+ raise HTTPException(status_code=404, detail="Attempt not found")
126
+ answers = crud.get_attempt_details(db, attempt_id)
127
+ out = []
128
+ for a in answers:
129
+ out.append({
130
+ "question_number": a.question_number,
131
+ "question_text": a.question_text,
132
+ "user_answer": a.user_answer,
133
+ "correct_answer": a.correct_answer,
134
+ "explanation": a.explanation,
135
+ "detailed_solution": a.detailed_solution,
136
+ "is_correct": bool(a.is_correct)
137
+ })
138
+ return {"attempt": {
139
+ "id": att.id,
140
+ "subject": att.subject,
141
+ "topic": att.topic,
142
+ "total_questions": att.total_questions,
143
+ "correct_answers": att.correct_answers,
144
+ "percentage": att.percentage,
145
+ "timestamp": att.timestamp.isoformat()
146
+ }, "answers": out}
147
+
148
+ # Analytics
149
+ @app.get("/analytics/score-trend")
150
+ def score_trend(subject: str = None, limit: int = 100, db: Session = Depends(get_db), user: dbmodels.User = Depends(get_current_user)):
151
+ rows = crud.get_score_trend(db, user.id, subject=subject, limit=limit)
152
+ return [{"timestamp": r.timestamp.isoformat(), "percentage": r.percentage, "topic": r.topic} for r in rows]
153
+
154
+ # Chatbot
155
+ @app.post("/chat")
156
+ async def chat(req: ChatRequest):
157
+ ans = await ask_chatbot(req.question)
158
+ return {"answer": ans}
159
+
160
+ # Start scheduler on startup (best-effort)
161
+ @app.on_event("startup")
162
+ def startup_event():
163
+ try:
164
+ start_scheduler()
165
+ except Exception:
166
+ pass