KingNish commited on
Commit
f62defa
·
verified ·
1 Parent(s): 9c684e6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +50 -102
app.py CHANGED
@@ -1,7 +1,7 @@
1
- from fastapi import FastAPI, HTTPException, Response, Request
2
  from fastapi.middleware.cors import CORSMiddleware
3
  import aiosqlite
4
- from typing import List, AsyncGenerator
5
  import random
6
  import orjson
7
  from fastapi.middleware.gzip import GZipMiddleware
@@ -11,8 +11,7 @@ from cryptography.hazmat.primitives import hashes, serialization
11
  from cryptography.hazmat.primitives.asymmetric import rsa
12
  from datetime import datetime, timedelta
13
  import os
14
- from fastapi.responses import StreamingResponse
15
-
16
 
17
  app = FastAPI(
18
  title="Quiz API",
@@ -39,6 +38,7 @@ async def add_cache_headers(request: Request, call_next):
39
  response.headers["Expires"] = expires.strftime("%a, %d %b %Y %H:%M:%S GMT")
40
  return response
41
 
 
42
  # Database Manager (Singleton)
43
  class DatabaseManager:
44
  _instance = None
@@ -52,13 +52,13 @@ class DatabaseManager:
52
 
53
  async def connect(self):
54
  if self.conn is None:
55
- try:
56
- self.conn = await aiosqlite.connect(self.db_path)
57
- self.conn.row_factory = aiosqlite.Row
58
- print("Database connection established.")
59
- except Exception as e:
60
- print(f"Error establishing database connection: {e}")
61
- raise # Re-raise exception to be handled by calling code
62
 
63
  return self.conn
64
 
@@ -71,7 +71,6 @@ class DatabaseManager:
71
  except Exception as e:
72
  print(f"Error closing database connection: {e}")
73
 
74
-
75
  db_manager = DatabaseManager()
76
 
77
  # Cache for precomputed data
@@ -131,16 +130,6 @@ def get_image():
131
  "clickUrl": click_url
132
  }
133
 
134
- async def stream_questions(cursor):
135
- yield b"[" # Start of JSON array
136
- first = True
137
- async for row in cursor:
138
- if not first:
139
- yield b","
140
- yield orjson.dumps(dict(row))
141
- first = False
142
- yield b"]" # End of JSON array
143
-
144
  @app.get("/questions",
145
  summary="Filter questions",
146
  tags=["Questions"],
@@ -184,20 +173,14 @@ async def filter_questions(
184
  db = await db_manager.connect()
185
  try:
186
  cursor = await db.execute(query, params)
187
-
188
- # Check if there's any data to stream before yielding
189
- if not await cursor.fetchone():
190
- raise HTTPException(
191
  status_code=404, detail="No questions found with the given filters."
192
  )
193
-
194
- await cursor.close()
195
- cursor = await db.execute(query, params) # re-execute since fetchone consume it
196
- return StreamingResponse(
197
- stream_questions(cursor), media_type="application/json"
198
- )
199
- except HTTPException as e:
200
- raise e
201
  except Exception as e:
202
  raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
203
  # finally:
@@ -262,22 +245,6 @@ def list_topics(subject: str, chapter: str):
262
 
263
  return cache["subject_chapters"][key]
264
 
265
- async def stream_topic_questions(cursor, subject, chapter, topic):
266
- yield b'{"subject": '
267
- yield orjson.dumps(subject)
268
- yield b', "chapter": '
269
- yield orjson.dumps(chapter)
270
- yield b', "topic": '
271
- yield orjson.dumps(topic)
272
- yield b', "questions": ['
273
- first = True
274
- async for row in cursor:
275
- if not first:
276
- yield b","
277
- yield orjson.dumps(dict(row))
278
- first = False
279
- yield b"]}"
280
-
281
 
282
  @app.get("/{subject}/{chapter}/{topic}",
283
  summary="List questions for a topic",
@@ -294,53 +261,24 @@ async def list_questions(subject: str, chapter: str, topic: str):
294
  db = await db_manager.connect()
295
  try:
296
  cursor = await db.execute(query, params)
297
-
298
- # Check if there's any data to stream before yielding
299
- if not await cursor.fetchone():
300
- raise HTTPException(
301
- status_code=404, detail="Topic not found or no questions available."
302
- )
303
- await cursor.close()
304
- cursor = await db.execute(query, params)
305
-
306
- return StreamingResponse(
307
- stream_topic_questions(cursor, subject, chapter, topic),
308
- media_type="application/json",
309
  )
310
- except HTTPException as e:
311
- raise e
 
 
 
 
 
312
  except Exception as e:
313
  raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
314
  # finally:
315
  # await db_manager.close()
316
 
317
- async def stream_test_questions(cursor, num_questions, total_time):
318
- questions_list = []
319
- async for row in cursor:
320
- questions_list.append(dict(row))
321
-
322
- if len(questions_list) < num_questions:
323
- raise HTTPException(
324
- status_code=400,
325
- detail=f"Not enough questions available. Requested {num_questions}, but found {len(questions_list)}."
326
- )
327
-
328
- selected_questions = random.sample(questions_list, num_questions)
329
-
330
- yield b'{"total_questions": '
331
- yield orjson.dumps(num_questions)
332
- yield b', "total_time": '
333
- yield orjson.dumps(total_time)
334
- yield b', "questions": ['
335
- first = True
336
- for question in selected_questions:
337
- if not first:
338
- yield b","
339
- yield orjson.dumps(question)
340
- first = False
341
- yield b"]}"
342
-
343
-
344
  @app.post("/generate-test",
345
  summary="Generate a test",
346
  tags=["Test Generation"],
@@ -376,26 +314,35 @@ async def generate_test(
376
 
377
  db = await db_manager.connect()
378
  try:
379
- cursor = await db.execute(query, params)
380
- # Check if there's any data to stream before yielding
381
- if not await cursor.fetchone():
 
382
  raise HTTPException(
383
  status_code=404, detail="No questions found matching the criteria."
384
  )
385
- await cursor.close()
386
- cursor = await db.execute(query, params)
387
-
388
- return StreamingResponse(
389
- stream_test_questions(cursor, num_questions, total_time), media_type="application/json"
390
- )
391
 
392
- except HTTPException as e:
393
- raise e
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  except Exception as e:
395
  raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
396
  # finally:
397
  # await db_manager.close()
398
 
 
399
  @app.on_event("shutdown")
400
  async def shutdown_event():
401
  await db_manager.close()
@@ -465,4 +412,5 @@ if __name__ == "__main__":
465
  ssl_certfile="cert.pem",
466
  timeout_keep_alive=60,
467
  log_level="warning"
 
468
  )
 
1
+ from fastapi import FastAPI, HTTPException, Response
2
  from fastapi.middleware.cors import CORSMiddleware
3
  import aiosqlite
4
+ from typing import List
5
  import random
6
  import orjson
7
  from fastapi.middleware.gzip import GZipMiddleware
 
11
  from cryptography.hazmat.primitives.asymmetric import rsa
12
  from datetime import datetime, timedelta
13
  import os
14
+ from fastapi import Request
 
15
 
16
  app = FastAPI(
17
  title="Quiz API",
 
38
  response.headers["Expires"] = expires.strftime("%a, %d %b %Y %H:%M:%S GMT")
39
  return response
40
 
41
+
42
  # Database Manager (Singleton)
43
  class DatabaseManager:
44
  _instance = None
 
52
 
53
  async def connect(self):
54
  if self.conn is None:
55
+ try:
56
+ self.conn = await aiosqlite.connect(self.db_path)
57
+ self.conn.row_factory = aiosqlite.Row
58
+ print("Database connection established.")
59
+ except Exception as e:
60
+ print(f"Error establishing database connection: {e}")
61
+ raise # Re-raise exception to be handled by calling code
62
 
63
  return self.conn
64
 
 
71
  except Exception as e:
72
  print(f"Error closing database connection: {e}")
73
 
 
74
  db_manager = DatabaseManager()
75
 
76
  # Cache for precomputed data
 
130
  "clickUrl": click_url
131
  }
132
 
 
 
 
 
 
 
 
 
 
 
133
  @app.get("/questions",
134
  summary="Filter questions",
135
  tags=["Questions"],
 
173
  db = await db_manager.connect()
174
  try:
175
  cursor = await db.execute(query, params)
176
+ questions = await cursor.fetchall()
177
+
178
+ if not questions:
179
+ raise HTTPException(
180
  status_code=404, detail="No questions found with the given filters."
181
  )
182
+
183
+ return [dict(question) for question in questions]
 
 
 
 
 
 
184
  except Exception as e:
185
  raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
186
  # finally:
 
245
 
246
  return cache["subject_chapters"][key]
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  @app.get("/{subject}/{chapter}/{topic}",
250
  summary="List questions for a topic",
 
261
  db = await db_manager.connect()
262
  try:
263
  cursor = await db.execute(query, params)
264
+ questions = await cursor.fetchall()
265
+
266
+ if not questions:
267
+ raise HTTPException(
268
+ status_code=404, detail="Topic not found or no questions available."
 
 
 
 
 
 
 
269
  )
270
+
271
+ return {
272
+ "subject": subject,
273
+ "chapter": chapter,
274
+ "topic": topic,
275
+ "questions": [dict(question) for question in questions],
276
+ }
277
  except Exception as e:
278
  raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
279
  # finally:
280
  # await db_manager.close()
281
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  @app.post("/generate-test",
283
  summary="Generate a test",
284
  tags=["Test Generation"],
 
314
 
315
  db = await db_manager.connect()
316
  try:
317
+ cursor = await db.execute(query, params)
318
+ questions = await cursor.fetchall()
319
+
320
+ if not questions:
321
  raise HTTPException(
322
  status_code=404, detail="No questions found matching the criteria."
323
  )
 
 
 
 
 
 
324
 
325
+ if len(questions) < num_questions:
326
+ raise HTTPException(
327
+ status_code=400,
328
+ detail=f"Not enough questions available. Requested {num_questions}, but found {len(questions)}."
329
+ )
330
+
331
+ selected_questions = random.sample(questions, num_questions)
332
+
333
+ test = {
334
+ "total_questions": num_questions,
335
+ "total_time": total_time,
336
+ "questions": [dict(q) for q in selected_questions],
337
+ }
338
+
339
+ return test
340
  except Exception as e:
341
  raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
342
  # finally:
343
  # await db_manager.close()
344
 
345
+
346
  @app.on_event("shutdown")
347
  async def shutdown_event():
348
  await db_manager.close()
 
412
  ssl_certfile="cert.pem",
413
  timeout_keep_alive=60,
414
  log_level="warning"
415
+
416
  )