raj-tomar001 commited on
Commit
cb2d92d
·
verified ·
1 Parent(s): 149536f

Update models.py

Browse files
Files changed (1) hide show
  1. models.py +1215 -1213
models.py CHANGED
@@ -1,1213 +1,1215 @@
1
- import mysql.connector
2
- from flask_bcrypt import Bcrypt
3
- from flask import request, jsonify
4
- import logging
5
- import json
6
- import traceback
7
- from datetime import datetime
8
- from question_verify import verify_question
9
- from flask_jwt_extended import get_jwt_identity
10
- import uuid
11
- import random
12
- import re
13
- import json
14
- from generate_question import generate_ques
15
- logging.basicConfig(
16
- filename='app.log',
17
- level=logging.DEBUG,
18
- format='%(asctime)s - %(levelname)s - %(message)s'
19
- )
20
-
21
- bcrypt = Bcrypt()
22
-
23
- def get_db_connection():
24
- try:
25
- connection = mysql.connector.connect(
26
- host = "quamble-quamble.c.aivencloud.com",
27
- user = "avnadmin",
28
- password = "AVNS_NgB0Jv5F-yk567pP7fo",
29
- database = "quamble",
30
- port = "17939"
31
- )
32
- logging.info("Database connection established.")
33
- return connection
34
- except mysql.connector.Error as err :
35
- print(f"Error: {err}")
36
- logging.error("Error connecting to the database: %s", err)
37
- return None
38
-
39
-
40
- def add_user(username, email, password, role, preference_1=None, preference_2=None, preference_3=None, preference_4=None):
41
-
42
-
43
- try:
44
-
45
- user_id = str(uuid.uuid4())
46
- connection = get_db_connection()
47
- if connection:
48
- cursor = connection.cursor()
49
- hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
50
- cursor.execute("""
51
- INSERT INTO users (id, username, email, password, role, preference_1, preference_2, preference_3, preference_4)
52
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
53
- """, (user_id, username, email, hashed_password, role, preference_1, preference_2, preference_3, preference_4))
54
- connection.commit()
55
- logging.info("User added: %s", username)
56
-
57
- except mysql.connector.Error as err:
58
- logging.error("Error adding user %s: %s", username, err)
59
- finally:
60
- #cursor.close()
61
- #connection.close()
62
- logging.info("Database connection closed after adding user.")
63
-
64
-
65
-
66
-
67
- def get_user_by_email(email):
68
- connection = get_db_connection()
69
- if connection:
70
- try:
71
- cursor = connection.cursor(dictionary=True)
72
- cursor.execute("SELECT * FROM users WHERE email=%s", (email,))
73
- user = cursor.fetchone()
74
- logging.info("User retrieved: %s", user)
75
- return user
76
- except mysql.connector.Error as err:
77
- logging.error("Error retrieving user by email %s: %s", email, err)
78
- return None
79
- finally:
80
- cursor.close()
81
- connection.close()
82
- logging.info("Database connection closed after retrieving user.")
83
-
84
- def add_quiz(user_id, theme, questions, correct_options):
85
- connection = get_db_connection()
86
- if not connection:
87
- logging.error("Database connection failed.")
88
- return None
89
-
90
- try:
91
- cursor = connection.cursor()
92
-
93
-
94
- cursor.execute("SELECT theme_quiz_table FROM Themes WHERE theme = %s", (theme,))
95
- theme_data = cursor.fetchone()
96
-
97
- if not theme_data:
98
- logging.error("Invalid theme provided: %s", theme)
99
- return None
100
-
101
- theme_table = theme_data[0]
102
-
103
-
104
- cursor.execute("SELECT COUNT(*) FROM users WHERE id = %s", (user_id,))
105
- user_exists = cursor.fetchone()[0]
106
- if user_exists == 0:
107
- logging.error("User ID does not exist: %s", user_id)
108
- return None
109
-
110
-
111
- cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id, theme))
112
- quiz_id = cursor.lastrowid
113
-
114
-
115
- questions_json = json.dumps(questions)
116
- correct_options_json = json.dumps(correct_options)
117
-
118
-
119
- query = f"""
120
- INSERT INTO {theme_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm)
121
- VALUES (%s, %s, %s, %s)
122
- """
123
- cursor.execute(query, (quiz_id, user_id, questions_json, correct_options_json))
124
-
125
- connection.commit()
126
- logging.info("Quiz added for user_id %s with theme %s in table %s", user_id, theme, theme_table)
127
- return quiz_id
128
-
129
- except mysql.connector.Error as err:
130
- logging.error("Error adding quiz: %s", err)
131
- return None
132
-
133
- finally:
134
- if cursor:
135
- cursor.close()
136
- if connection:
137
- connection.close()
138
- logging.info("Database connection closed after adding quiz.")
139
-
140
-
141
-
142
-
143
- def add_question_from_master():
144
- if request.method == 'POST':
145
- logging.info("Received a POST request to add questions to quiz.")
146
-
147
- theme = request.form.get('theme')
148
- if not theme:
149
- logging.error("Theme not provided in the request.")
150
- return jsonify({"error": "Please provide the theme."}), 400
151
-
152
- question = request.form.get('question')
153
- correct_option = request.form.get('correct_option')
154
- if not question or not correct_option:
155
- logging.error("Missing question or correct option in the request.")
156
- return jsonify({"error": "Please provide both question and correct option."}), 400
157
-
158
- question = question.replace("\t", "")
159
- validation, feedback, difficulty = verify_question(question, correct_option)
160
- if validation != 'valid':
161
- logging.warning(f"Verification failed: {feedback}")
162
- return jsonify({"error": f"Verification failed: {feedback}"}), 400
163
-
164
- logging.info(f"Verified question: {question}")
165
- logging.info(f"Verified correct option: {correct_option}")
166
- logging.info(f"Difficulty level: {difficulty}")
167
-
168
- user_id_creator = get_jwt_identity()
169
- logging.info(f"Authenticated user ID: {user_id_creator}")
170
-
171
- connection = get_db_connection()
172
- if connection:
173
- try:
174
- cursor = connection.cursor()
175
- cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
176
- result = cursor.fetchone()
177
- if not result:
178
- logging.error("Invalid theme provided: %s", theme)
179
- return jsonify({"error": "Invalid theme provided."}), 400
180
-
181
- theme_bank = result[0]
182
-
183
- question_json = json.dumps({"question": question})
184
- check_query = f"""
185
- SELECT COUNT(*)
186
- FROM {theme_bank}
187
- WHERE question_by_master = %s AND user_id_creator = %s
188
- """
189
- cursor.execute(check_query, (question_json, user_id_creator))
190
- is_duplicate = cursor.fetchone()[0] > 0
191
-
192
- if is_duplicate:
193
- logging.warning("Duplicate question detected for user %s: %s", user_id_creator, question)
194
- return jsonify({"error": "Duplicate question detected."}), 400
195
-
196
- insert_query = f"""
197
- INSERT INTO {theme_bank} (user_id_creator, question_by_master, correct_option_master, Difficulty)
198
- VALUES (%s, %s, %s, %s)
199
- """
200
- cursor.execute(insert_query, (user_id_creator, question_json, correct_option, difficulty))
201
- connection.commit()
202
- logging.info("Question added for theme %s", theme)
203
- return 'True'
204
-
205
- except mysql.connector.Error as err:
206
- logging.error("Error adding question: %s", err)
207
- return jsonify({"error": "Failed to add question to the database."}), 500
208
- finally:
209
- #cursor.close()
210
- #connection.close()
211
- logging.info("Database connection closed.")
212
- else:
213
- logging.error("Database connection failed.")
214
- return jsonify({"error": "Database connection failed."}), 500
215
-
216
- def add_ques_llm(theme, question, options, correct_option, difficulty):
217
- connection = get_db_connection()
218
-
219
- if connection:
220
- try:
221
- cursor = connection.cursor()
222
-
223
- cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
224
- theme_bank_result = cursor.fetchone()
225
-
226
- if not theme_bank_result:
227
- logging.error("Invalid theme provided: %s", theme)
228
- return None
229
-
230
- theme_bank = theme_bank_result[0]
231
- user_id_creator = get_jwt_identity()
232
-
233
- question_json = json.dumps({
234
- "question": question,
235
- "options": options
236
- })
237
-
238
- check_query = f"SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm = %s"
239
- cursor.execute(check_query, (question_json,))
240
- is_duplicate = cursor.fetchone()[0] > 0
241
-
242
- if is_duplicate:
243
- logging.warning("Duplicate question detected for theme %s: %s", theme, question)
244
- return "Duplicate"
245
-
246
- insert_query = f"""
247
- INSERT INTO {theme_bank} (user_id_creator, question_by_llm, correct_option_llm, Difficulty)
248
- VALUES (%s, %s, %s, %s)
249
- """
250
- cursor.execute(insert_query, (user_id_creator, question_json, correct_option, difficulty))
251
-
252
- connection.commit()
253
- logging.info("Question added for theme %s", theme)
254
- return 'True'
255
-
256
- except mysql.connector.Error as err:
257
- logging.error("Error adding question: %s", err)
258
- return None
259
- finally:
260
- #cursor.close()
261
- #connection.close()
262
- logging.info("Database connection closed after adding question.")
263
- else:
264
- logging.error("Database connection failed.")
265
- return None
266
-
267
-
268
-
269
-
270
- def add_question_to_db(theme, question, correct_option, difficulty):
271
- connection = get_db_connection()
272
- try:
273
- cursor = connection.cursor()
274
-
275
-
276
- theme_check_query = "SELECT COUNT(*) FROM themes WHERE theme = %s"
277
- cursor.execute(theme_check_query, (theme,))
278
- theme_exists = cursor.fetchone()[0]
279
-
280
- if theme_exists == 0:
281
- insert_query = "INSERT INTO themes (theme, theme_bank, theme_quiz_table) VALUES (%s, %s, %s)"
282
- cursor.execute(insert_query, (theme, f"{theme}_bank", f"theme_{theme}"))
283
-
284
-
285
- safe_theme = re.sub(r'\W+', '_', theme)
286
-
287
- table_creation_query = f"""
288
- CREATE TABLE IF NOT EXISTS {safe_theme}_bank (
289
- ques_id INT AUTO_INCREMENT PRIMARY KEY,
290
- user_id_creator VARCHAR(64),
291
- question_by_llm MEDIUMTEXT,
292
- question_hash VARCHAR(640),
293
- correct_option_llm MEDIUMTEXT,
294
- question_by_master MEDIUMTEXT,
295
- correct_option_master MEDIUMTEXT,
296
- Difficulty VARCHAR(64),
297
- is_used VARCHAR(64),
298
- Issue VARCHAR(6400)
299
- )
300
- """
301
- cursor.execute(table_creation_query)
302
-
303
- theme_table_creation_query = f"""
304
- CREATE TABLE IF NOT EXISTS theme_{safe_theme} (
305
- quiz_id INT(8) PRIMARY KEY AUTO_INCREMENT,
306
- user_id_creator VARCHAR(64),
307
- questions_by_llm MEDIUMTEXT,
308
- correct_options_llm MEDIUMTEXT,
309
- questions_by_master MEDIUMTEXT,
310
- correct_options_master MEDIUMTEXT,
311
- num_questions INT(11),
312
- user_id_attempt MEDIUMTEXT,
313
- user_responses MEDIUMTEXT
314
- )
315
- """
316
- cursor.execute(theme_table_creation_query)
317
-
318
- check_query = f"SELECT COUNT(*) FROM '{safe_theme}_bank' WHERE question_by_llm = %s"
319
- cursor.execute(check_query, (question,))
320
- is_duplicate = cursor.fetchone()[0] > 0
321
-
322
- if is_duplicate:
323
- logging.warning("Duplicate question detected for theme %s: %s", safe_theme, question)
324
- return "Duplicate"
325
-
326
-
327
- query = f"INSERT INTO {safe_theme}_bank (question_by_llm, correct_option_llm, Difficulty, is_used) VALUES (%s, %s, %s, 'unused')"
328
- cursor.execute(query, (question, correct_option, difficulty))
329
- connection.commit()
330
- return 'True'
331
-
332
- except mysql.connector.Error as err:
333
- logging.error("Error adding question: %s", err)
334
- return None
335
- finally:
336
- #cursor.close()
337
- #connection.close()
338
- logging.info("Database connection closed")
339
-
340
-
341
- def create_quiz(theme, num_questions):
342
- connection = get_db_connection()
343
- if connection:
344
- try:
345
- cursor = connection.cursor()
346
-
347
-
348
- cursor.execute("SELECT theme_bank, theme_quiz_table FROM themes WHERE theme = %s", (theme,))
349
- theme_data = cursor.fetchone()
350
-
351
- if not theme_data:
352
- logging.error("Invalid theme provided: %s", theme)
353
- return jsonify({"error": "Invalid theme provided."}), 400
354
-
355
- theme_bank, theme_quiz_table = theme_data
356
-
357
- query_available_questions = f"""
358
- SELECT COUNT(*)
359
- FROM {theme_bank}
360
- WHERE question_by_llm IS NOT NULL
361
- AND question_by_llm != ''
362
- AND (is_used IS NULL OR is_used = '')
363
- """
364
- cursor.execute(query_available_questions)
365
- available_count = cursor.fetchone()[0]
366
-
367
- if available_count < int(num_questions):
368
- logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
369
- return jsonify({"error": "Not enough questions available."}), 400
370
-
371
-
372
- query_random_questions = f"""
373
- SELECT ques_id, question_by_llm, correct_option_llm
374
- FROM {theme_bank}
375
- WHERE question_by_llm IS NOT NULL
376
- AND question_by_llm != ''
377
- AND (is_used IS NULL OR is_used = '')
378
- ORDER BY RAND()
379
- LIMIT {int(num_questions)}
380
- """
381
- cursor.execute(query_random_questions)
382
- selected_questions = cursor.fetchall()
383
-
384
- questions = []
385
- correct_options = []
386
- selected_question_ids = []
387
-
388
- for question_id, question, correct_option in selected_questions:
389
- questions.append(question)
390
- correct_options.append(correct_option)
391
- selected_question_ids.append(question_id)
392
-
393
-
394
- mark_as_used_query = f"""
395
- UPDATE {theme_bank}
396
- SET is_used = 'used'
397
- WHERE ques_id IN ({', '.join(map(str, selected_question_ids))})
398
- """
399
- cursor.execute(mark_as_used_query)
400
-
401
- user_id_creator = get_jwt_identity()
402
-
403
-
404
- cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
405
- quiz_id = cursor.lastrowid
406
- questions_json = json.dumps(questions)
407
- correct_options_json = json.dumps(correct_options)
408
-
409
-
410
- insert_quiz_query = f"""
411
- INSERT INTO {theme_quiz_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm, num_questions)
412
- VALUES (%s, %s, %s, %s, %s)
413
- """
414
- cursor.execute(insert_quiz_query, (quiz_id, user_id_creator, questions_json, correct_options_json, num_questions))
415
- connection.commit()
416
- logging.info("Quiz created successfully for theme %s with %d questions.", theme, num_questions)
417
-
418
- questions_reflect = json.loads(questions_json)
419
- correct_options_reflect = json.loads(correct_options_json)
420
-
421
- return jsonify({
422
- "quiz_id": quiz_id,
423
- "questions": questions_reflect,
424
- "correct_options": correct_options_reflect
425
- }), 201
426
-
427
- except mysql.connector.Error as err:
428
- logging.error("Error creating quiz: %s", err)
429
- return jsonify({"error": "Failed to create quiz."}), 500
430
-
431
- except Exception as e:
432
- logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
433
- return jsonify({"error": "An unexpected error occurred."}), 500
434
-
435
- finally:
436
- if cursor:
437
- cursor.close()
438
- if connection:
439
- connection.close()
440
- logging.info("Database connection closed.")
441
- else:
442
- logging.error("Database connection failed.")
443
- return jsonify({"error": "Database connection failed."}), 500
444
-
445
-
446
-
447
- def create_quiz_master(user_id_creator, theme, num_questions):
448
- connection = get_db_connection()
449
- if connection:
450
- try:
451
- cursor = connection.cursor()
452
- cursor.execute("SELECT theme_bank, theme_quiz_table FROM themes WHERE theme = %s", (theme,))
453
- theme_data = cursor.fetchone()
454
- if not theme_data:
455
- logging.error("Invalid theme provided: %s", theme)
456
- return jsonify({"error": "Invalid theme provided."}), 400
457
- theme_bank, theme_quiz_table = theme_data
458
-
459
- query_available_questions = f"""
460
- SELECT COUNT(*) FROM {theme_bank} WHERE question_by_master IS NOT NULL AND is_used IS NULL OR is_used = "unused"
461
- """
462
- cursor.execute(query_available_questions)
463
- available_count = cursor.fetchone()[0]
464
-
465
- if available_count < int(num_questions):
466
- logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
467
- return jsonify({"error": "Not enough questions available."}), 400
468
-
469
- query_random_questions = f"""
470
- SELECT ques_id, question_by_master, correct_option_master
471
- FROM {theme_bank}
472
- WHERE question_by_master IS NOT NULL AND question_by_master != ''
473
- ORDER BY RAND()
474
- LIMIT {int(num_questions)}
475
- """
476
- cursor.execute(query_random_questions)
477
- selected_questions = cursor.fetchall()
478
-
479
- questions, correct_options, selected_question_ids = [], [], []
480
- for question_id, question, correct_option in selected_questions:
481
- questions.append(question)
482
- correct_options.append(correct_option)
483
- selected_question_ids.append(question_id)
484
-
485
- mark_as_used_query = f"""
486
- UPDATE {theme_bank}
487
- SET is_used = 'used'
488
- WHERE ques_id IN ({', '.join(map(str, selected_question_ids))})
489
- """
490
- cursor.execute(mark_as_used_query)
491
-
492
- cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
493
- quiz_id = cursor.lastrowid
494
-
495
- insert_quiz_query = f"""
496
- INSERT INTO {theme_quiz_table} (quiz_id, user_id_creator, questions_by_master, correct_options_master, num_questions)
497
- VALUES (%s, %s, %s, %s, %s)
498
- """
499
- cursor.execute(insert_quiz_query, (quiz_id, user_id_creator, json.dumps(questions), json.dumps(correct_options), num_questions))
500
- connection.commit()
501
-
502
- return jsonify({
503
- "quiz_id": quiz_id,
504
- "ques_id": selected_question_ids,
505
- "correct_options": correct_options
506
- }), 201
507
-
508
- except mysql.connector.Error as err:
509
- logging.error("Error creating quiz: %s", err)
510
- return jsonify({"error": "Failed to create quiz."}), 500
511
- except Exception as e:
512
- logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
513
- return jsonify({"error": "An unexpected error occurred."}), 500
514
- finally:
515
- if cursor:
516
- cursor.close()
517
- if connection:
518
- connection.close()
519
- logging.info("Database connection closed.")
520
- else:
521
- logging.error("Database connection failed.")
522
- return jsonify({"error": "Database connection failed."}), 500
523
-
524
-
525
-
526
-
527
- def recording_issue(theme, ques_id, issue_description):
528
- connection = get_db_connection()
529
- if connection:
530
- try:
531
- cursor = connection.cursor()
532
- cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
533
- theme_data = cursor.fetchone()
534
-
535
- if not theme_data:
536
- logging.error("Invalid theme provided: %s", theme)
537
- return jsonify({"error": "Invalid theme provided."}), 400
538
-
539
- theme_bank = theme_data[0]
540
-
541
- select_query = f"SELECT COUNT(*) FROM {theme_bank} WHERE ques_id = %s"
542
- cursor.execute(select_query, (ques_id,))
543
- count = cursor.fetchone()[0]
544
-
545
- if count == 0:
546
- return jsonify({"error": f"Question ID {ques_id} does not exist in the database"}), 400
547
-
548
- update_query = f"""
549
- UPDATE {theme_bank}
550
- SET Issue = %s
551
- WHERE ques_id = %s
552
- """
553
- cursor.execute(update_query, (issue_description, ques_id))
554
- connection.commit()
555
-
556
- logging.info("Issue recorded for theme %s and question_id %s.", theme, ques_id)
557
- return jsonify({"message": "Issue reported successfully"}), 201
558
-
559
- except mysql.connector.Error as err:
560
- return jsonify({"error": "An error occurred while reporting the issue", "details": str(err)}), 500
561
-
562
- finally:
563
- cursor.close()
564
- connection.close()
565
- logging.info("Database connection closed.")
566
- else:
567
- logging.error("Database connection failed.")
568
- return jsonify({"error": "Database connection failed."}), 500
569
-
570
-
571
-
572
- def record_feedback(user_id, rating, comments):
573
- connection = get_db_connection()
574
- if connection:
575
- try:
576
- cursor = connection.cursor()
577
-
578
- insert_query= """
579
- INSERT INTO feedbacks (user_id, rating, comments)
580
- VALUES (%s, %s, %s)
581
- """
582
- cursor.execute(insert_query, (user_id, rating, comments))
583
-
584
- # Commit the transaction and close the connection
585
- connection.commit()
586
- cursor.close()
587
- connection.close()
588
- return jsonify({"message": "Feedback submitted successfully"}), 201
589
-
590
- except mysql.connector.Error as err:
591
- print(f"Error: {err}")
592
- return jsonify({"error": "Failed to submit feedback"}), 500
593
-
594
-
595
- def create_quiz_by_id(theme, num_questions):
596
- connection = get_db_connection()
597
- if not connection:
598
- logging.error("Database connection failed.")
599
- return jsonify({"error": "Database connection failed."}), 500
600
-
601
- try:
602
- cursor = connection.cursor()
603
- cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
604
- result = cursor.fetchone()
605
-
606
- if not result:
607
- logging.error("Invalid theme provided: %s", theme)
608
- return jsonify({"error": "Invalid theme provided."}), 400
609
-
610
- theme_bank = result[0]
611
-
612
- cursor.execute(f"""
613
- SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm IS NOT NULL AND is_used IS NULL OR is_used = "unused"
614
- """)
615
- available_count = cursor.fetchone()[0]
616
-
617
- shortfall = int(num_questions) - available_count
618
- logging.info("Available questions: %d; Required: %d; Shortfall: %d", available_count, int(num_questions), shortfall)
619
-
620
- added_count = 0
621
- attempts = 0
622
- max_attempts = shortfall * 10
623
-
624
- while added_count < shortfall and attempts < max_attempts:
625
- attempts += 1
626
- ques = generate_ques(theme)
627
-
628
- question_match = re.search(r'Question:\s*(.*)', ques)
629
- options_matches = re.findall(r'([A-D])\)\s*(.*)', ques)
630
- correct_answer_match = re.search(r'Correct answer:\s*([A-D])', ques)
631
- difficulty_match = re.search(r'Difficulty level:\s*(.*)', ques)
632
-
633
- if question_match and options_matches and correct_answer_match:
634
- question = question_match.group(1).strip()
635
- options = [f"{opt[0]}) {opt[1].strip()}" for opt in options_matches]
636
- correct_option = correct_answer_match.group(1).strip().upper()
637
- difficulty = difficulty_match.group(1).strip() if difficulty_match else "medium"
638
-
639
- if correct_option not in ['A', 'B', 'C', 'D']:
640
- continue
641
-
642
- status = add_ques_llm(theme, question, options, correct_option, difficulty)
643
- if status == "True":
644
- added_count += 1
645
- elif status == "Duplicate":
646
- continue # retry
647
- else:
648
- logging.warning("Failed to insert generated question.")
649
- else:
650
- logging.warning("Malformed question generated; skipping.")
651
-
652
- if added_count < shortfall:
653
- return jsonify({"error": f"Could not generate enough unique questions. Needed {shortfall}, added {added_count}."}), 400
654
-
655
-
656
- cursor.execute(f"""
657
- SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm IS NOT NULL AND is_used IS NULL OR is_used = "unused"
658
- """)
659
- available_count = cursor.fetchone()[0]
660
- if available_count < int(num_questions):
661
- logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
662
- return jsonify({"error": "Not enough questions available."}), 400
663
-
664
- cursor.execute(f"""
665
- SELECT ques_id, correct_option_llm FROM {theme_bank}
666
- WHERE question_by_llm IS NOT NULL AND question_by_llm != ''
667
- AND (is_used IS NULL OR is_used = '')
668
- ORDER BY RAND() LIMIT {int(num_questions)}
669
- """)
670
- selected_questions = cursor.fetchall()
671
-
672
- questions = [q[0] for q in selected_questions]
673
- correct_options = [q[1] for q in selected_questions]
674
-
675
- cursor.execute(f"""
676
- UPDATE {theme_bank} SET is_used = 'used'
677
- WHERE ques_id IN ({', '.join(map(str, questions))})
678
- """)
679
-
680
- user_id_creator = get_jwt_identity()
681
- cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
682
- quiz_id = cursor.lastrowid
683
-
684
- cursor.execute("SELECT theme_quiz_table FROM themes WHERE theme = %s", (theme,))
685
- theme_table = cursor.fetchone()[0]
686
-
687
- cursor.execute(f"""
688
- INSERT INTO {theme_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm, num_questions)
689
- VALUES (%s, %s, %s, %s, %s)
690
- """, (quiz_id, user_id_creator, json.dumps(questions), json.dumps(correct_options), num_questions))
691
- connection.commit()
692
-
693
- logging.info("Quiz created successfully for theme %s with %d questions.", theme, num_questions)
694
- return jsonify({"quiz_id": quiz_id, "questions": questions, "correct_options": correct_options}), 201
695
-
696
- except mysql.connector.Error as err:
697
- logging.error("Error creating quiz: %s", err)
698
- return jsonify({"error": "Failed to create quiz."}), 500
699
-
700
- except Exception as e:
701
- logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
702
- return jsonify({"error": "An unexpected error occurred."}), 500
703
-
704
- finally:
705
- if cursor:
706
- cursor.close()
707
- if connection:
708
- connection.close()
709
- logging.info("Database connection closed.")
710
-
711
-
712
-
713
- def add_theme_if_not_exists(theme):
714
- connection = get_db_connection()
715
- try:
716
- cursor = connection.cursor()
717
-
718
- bank_table = f"{theme}_bank"
719
- quiz_table = f"theme_{theme}"
720
-
721
- theme_check_query = "SELECT COUNT(*) FROM themes WHERE theme = %s"
722
- cursor.execute(theme_check_query, (theme,))
723
- theme_exists = cursor.fetchone()[0]
724
-
725
-
726
- if theme_exists == 0:
727
- insert_query = "INSERT INTO themes (theme, theme_bank, theme_quiz_table) VALUES (%s, %s, %s)"
728
- cursor.execute(insert_query, (theme, f"{theme}_bank", f"theme_{theme}"))
729
- connection.commit()
730
-
731
- create_bank_table = f"""
732
- CREATE TABLE IF NOT EXISTS `{bank_table}` (
733
- ques_id INT PRIMARY KEY AUTO_INCREMENT,
734
- user_id_creator VARCHAR(64),
735
- question_by_llm MEDIUMTEXT,
736
- question_hash VARCHAR(640),
737
- correct_option_llm MEDIUMTEXT,
738
- question_by_master MEDIUMTEXT,
739
- correct_option_master MEDIUMTEXT,
740
- Difficulty VARCHAR(64),
741
- is_used VARCHAR(64),
742
- Issue VARCHAR(6400)
743
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
744
- """
745
- cursor.execute(create_bank_table)
746
- logging.info("Created table: %s", bank_table)
747
-
748
- # Create theme_ table
749
- create_quiz_table = f"""
750
- CREATE TABLE IF NOT EXISTS `{quiz_table}` (
751
- quiz_id INT PRIMARY KEY AUTO_INCREMENT,
752
- user_id_creator VARCHAR(64),
753
- questions_by_llm MEDIUMTEXT,
754
- correct_options_llm MEDIUMTEXT,
755
- questions_by_master MEDIUMTEXT,
756
- correct_options_master MEDIUMTEXT,
757
- num_questions INT,
758
- user_id_attempt MEDIUMTEXT,
759
- user_responses MEDIUMTEXT
760
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
761
- """
762
- cursor.execute(create_quiz_table)
763
- logging.info("Created table: %s", quiz_table)
764
-
765
- except mysql.connector.Error as err:
766
- logging.error("Error adding theme: %s", err)
767
- return jsonify({"error": "Failed to add theme."}), 500
768
- finally:
769
- cursor.close()
770
- connection.close()
771
- logging.info("Database connection is closed")
772
-
773
- return not theme_exists
774
-
775
- def profile_added_db(first_name, last_name, organisation, industry, bio):
776
- connection = get_db_connection()
777
- if connection:
778
- try:
779
- cursor = connection.cursor(dictionary=True)
780
- user_id_creator = get_jwt_identity()
781
- logging.info(f"Authenticated user ID: {user_id_creator}")
782
-
783
- query_check_user = "SELECT COUNT(*) AS user_count FROM users WHERE id = %s"
784
- cursor.execute(query_check_user, (user_id_creator,))
785
- user_exists = cursor.fetchone()['user_count']
786
-
787
- if user_exists:
788
-
789
- query_update_user = """
790
- UPDATE users
791
- SET first_name = %s, last_name = %s, organisation = %s, industry = %s, bio = %s
792
- WHERE id = %s
793
- """
794
- cursor.execute(query_update_user, (first_name, last_name, organisation, industry, bio, user_id_creator))
795
- connection.commit()
796
-
797
- logging.info(f"Profile for user {user_id_creator} updated successfully.")
798
- return jsonify({"message": "Profile updated successfully!"}), 200
799
- else:
800
-
801
- logging.warning(f"User with ID {user_id_creator} does not exist.")
802
- return jsonify({"error": "User does not exist."}), 404
803
- except Exception as e:
804
- logging.error(f"Unexpected error: {e}")
805
- return jsonify({"error":"Failed to update profile"}), 500
806
- finally:
807
- if connection.is_connected():
808
- cursor.close()
809
- connection.close()
810
- logging.info("Database connection closed.")
811
-
812
-
813
- def view_profile_db():
814
- try:
815
-
816
- user_id_creator = get_jwt_identity()
817
- logging.info(f"Authenticated user ID: {user_id_creator}")
818
- connection = get_db_connection()
819
- if connection:
820
- cursor = connection.cursor(dictionary=True)
821
-
822
- query_check_user = "SELECT COUNT(*) AS user_count FROM users WHERE id = %s"
823
- cursor.execute(query_check_user, (user_id_creator,))
824
- user_exists = cursor.fetchone()['user_count']
825
-
826
- if user_exists:
827
-
828
- query_get_profile = """
829
- SELECT first_name, last_name, role, organisation, industry, bio
830
- FROM users
831
- WHERE id = %s
832
- """
833
- cursor.execute(query_get_profile, (user_id_creator,))
834
- profile = cursor.fetchone()
835
-
836
- if profile:
837
- logging.info(f"User {user_id_creator}'s profile fetched successfully.")
838
- return jsonify({
839
- "first_name": profile["first_name"],
840
- "last_name": profile["last_name"],
841
- "role": profile["role"],
842
- "organisation": profile["organisation"],
843
- "industry": profile["industry"],
844
- "bio": profile["bio"]
845
- }), 200
846
- else:
847
- logging.warning(f"Profile for user {user_id_creator} not found.")
848
- return jsonify({"error": "Profile not found"}), 404
849
- else:
850
- logging.warning(f"User with ID {user_id_creator} does not exist.")
851
- return jsonify({"error": "User does not exist."}), 404
852
-
853
-
854
- except Exception as e:
855
- logging.error(f"Unexpected error: {e}")
856
- return jsonify({"error": f"Unexpected error: {str(e)}"}), 500
857
- finally:
858
- if connection.is_connected():
859
- cursor.close()
860
- connection.close()
861
- logging.info("Database connection closed.")
862
-
863
-
864
-
865
- def view_quiz_score_db(user_id, theme_table, theme, quiz_id):
866
- try:
867
- connection = get_db_connection()
868
- if connection:
869
- cursor = connection.cursor(dictionary=True)
870
-
871
-
872
- query = f"""
873
- SELECT num_questions
874
- FROM {theme_table}
875
- WHERE quiz_id = %s
876
- """
877
- cursor.execute(query, (quiz_id,))
878
- theme_data = cursor.fetchone()
879
-
880
- num_questions = theme_data['num_questions'] if theme_data else None
881
- if num_questions is None:
882
- logging.warning(f"Theme '{theme}' not found in theme_table.")
883
- return jsonify({"error": "Theme not found"}), 404
884
-
885
-
886
- query = """
887
- SELECT score, time_taken, user_response
888
- FROM quiz_response
889
- WHERE quiz_id = %s AND user_id_attempt = %s
890
- """
891
- cursor.execute(query, (quiz_id, user_id))
892
- quiz_response = cursor.fetchone()
893
-
894
- if quiz_response:
895
- user_response = quiz_response['user_response']
896
- score = quiz_response['score']
897
- time = quiz_response['time_taken']
898
-
899
-
900
- accuracy = (score / num_questions) * 100 if num_questions > 0 else 0
901
-
902
- response_data = {
903
- "quiz_id": quiz_id,
904
- "theme": theme,
905
- "user_response": user_response,
906
- "score": score,
907
- "accuracy": round(accuracy, 2),
908
- "time_taken": time
909
- }
910
-
911
- logging.info(f"Score and accuracy for user {user_id}, quiz {quiz_id} fetched successfully.")
912
- return jsonify(response_data), 200
913
-
914
-
915
- logging.warning(f"No quiz response found for user {user_id} and quiz {quiz_id}.")
916
- return jsonify({"error": "Quiz response not found"}), 404
917
-
918
- except Exception as e:
919
- logging.error(f"Error fetching quiz score: {e}")
920
- return jsonify({"error": "Unable to fetch quiz score"}), 500
921
-
922
- finally:
923
- if connection.is_connected():
924
- cursor.close()
925
- connection.close()
926
-
927
-
928
-
929
- def get_recent_quizzes_db(user_id, limit=5):
930
-
931
- try:
932
- connection = get_db_connection()
933
- if not connection:
934
- return {"error": "Database connection failed."}
935
-
936
- cursor = connection.cursor(dictionary=True)
937
-
938
-
939
- cursor.execute("""
940
- SELECT qr.quiz_id, qr.theme, qr.user_response, qr.score, qr.time_taken, qr.submitted_on,
941
- t.theme_quiz_table
942
- FROM quiz_response qr
943
- JOIN themes t ON qr.theme = t.theme
944
- WHERE qr.user_id_attempt = %s
945
- ORDER BY qr.submitted_on DESC
946
- LIMIT %s
947
- """, (user_id, limit))
948
-
949
- recent_quizzes = cursor.fetchall()
950
- if not recent_quizzes:
951
- return {"message": "No quizzes found for the user."}
952
-
953
- results = []
954
- for quiz in recent_quizzes:
955
- quiz_id = quiz['quiz_id']
956
- theme = quiz['theme']
957
- theme_quiz_table = quiz['theme_quiz_table']
958
-
959
-
960
- if not theme_quiz_table:
961
- logging.warning(f"No quiz table found for theme: {theme}")
962
- continue
963
-
964
-
965
- cursor.execute(f"""
966
- SELECT questions_by_llm, correct_options_llm,
967
- questions_by_master, correct_options_master
968
- FROM {theme_quiz_table}
969
- WHERE quiz_id = %s
970
- """, (quiz_id,))
971
-
972
- quiz_details = cursor.fetchone()
973
- if not quiz_details:
974
- logging.warning(f"Quiz '{quiz_id}' not found in theme table '{theme_quiz_table}'.")
975
- continue
976
-
977
- questions = quiz_details['questions_by_llm'] or quiz_details['questions_by_master']
978
- correct_answers = quiz_details['correct_options_llm'] or quiz_details['correct_options_master']
979
-
980
-
981
- if not questions or not correct_answers:
982
- logging.warning(f"Quiz '{quiz_id}' has no questions or answers.")
983
- continue
984
-
985
- results.append({
986
- "quiz_id": quiz_id,
987
- "theme": theme,
988
- "questions": questions,
989
- "correct_answers": correct_answers,
990
- "user_responses": quiz['user_response'],
991
- "score": quiz["score"],
992
- "time_taken": quiz['time_taken'],
993
- "date_attempted": quiz['submitted_on']
994
- })
995
-
996
- return results
997
-
998
- except mysql.connector.Error as err:
999
- logging.error(f"MySQL Error: {err}")
1000
- return {"error": "Database operation failed."}
1001
-
1002
- except Exception as e:
1003
- logging.error(f"Unexpected error: {str(e)}")
1004
- return {"error": "An unexpected error occurred."}
1005
-
1006
- finally:
1007
- if 'cursor' in locals():
1008
- cursor.close()
1009
- if 'connection' in locals():
1010
- connection.close()
1011
- logging.info("Database connection closed.")
1012
-
1013
-
1014
-
1015
- def get_quiz_details_db(user_id, quiz_id):
1016
-
1017
- try:
1018
- connection = get_db_connection()
1019
- if not connection:
1020
- return {"error": "Database connection failed."}
1021
-
1022
- cursor = connection.cursor(dictionary=True)
1023
- logging.info(f"Checking for user_id_attempt: {user_id}, quiz_id: {quiz_id}")
1024
- cursor.execute("""
1025
- SELECT qr.quiz_id, qr.theme, qr.user_response, qr.score, qr.time_taken, qr.submitted_on,
1026
- t.theme_quiz_table
1027
- FROM quiz_response qr
1028
- JOIN themes t ON qr.theme = t.theme
1029
- WHERE qr.user_id_attempt = %s AND qr.quiz_id = %s
1030
- """, (user_id, quiz_id))
1031
-
1032
- quiz = cursor.fetchone()
1033
- if not quiz:
1034
- return {"message": "Quiz not found."}
1035
-
1036
- theme_quiz_table = quiz['theme_quiz_table']
1037
-
1038
- if not theme_quiz_table:
1039
- logging.warning(f"No quiz table found for theme: {quiz['theme']}")
1040
- return {"error": "Invalid theme data."}
1041
-
1042
-
1043
- cursor.execute(f"""
1044
- SELECT questions_by_llm, correct_options_llm,
1045
- questions_by_master, correct_options_master
1046
- FROM {theme_quiz_table}
1047
- WHERE quiz_id = %s
1048
- """, (quiz_id,))
1049
-
1050
- quiz_details = cursor.fetchone()
1051
- if not quiz_details:
1052
- logging.warning(f"Quiz '{quiz_id}' not found in theme table '{theme_quiz_table}'.")
1053
- return {"error": "Quiz questions not found."}
1054
-
1055
-
1056
- questions = quiz_details['questions_by_llm'] or quiz_details['questions_by_master']
1057
- correct_answers = quiz_details['correct_options_llm'] or quiz_details['correct_options_master']
1058
-
1059
- if not questions or not correct_answers:
1060
- logging.warning(f"Quiz '{quiz_id}' has incomplete data.")
1061
- return {"error": "Incomplete quiz data."}
1062
-
1063
-
1064
- return {
1065
- "quiz_id": quiz["quiz_id"],
1066
- "theme": quiz["theme"],
1067
- "questions": questions,
1068
- "correct_answers": correct_answers,
1069
- "user_responses": quiz["user_response"],
1070
- "score": quiz["score"],
1071
- "time_taken": quiz["time_taken"],
1072
- "date_attempted": quiz["submitted_on"]
1073
- }
1074
-
1075
- except mysql.connector.Error as err:
1076
- logging.error(f"MySQL Error: {err}")
1077
- return {"error": "Database operation failed."}
1078
-
1079
- except Exception as e:
1080
- logging.error(f"Unexpected error: {str(e)}")
1081
- return {"error": "An unexpected error occurred."}
1082
-
1083
- finally:
1084
- if 'cursor' in locals():
1085
- cursor.close()
1086
- if 'connection' in locals():
1087
- connection.close()
1088
- logging.info("Database connection closed.")
1089
-
1090
-
1091
- def fetch_quiz_for_theme_db(user_id, theme):
1092
- try:
1093
- connection = get_db_connection()
1094
- if not connection:
1095
- return {"error": "Database connection failed."}
1096
-
1097
- cursor = connection.cursor(dictionary=True)
1098
-
1099
-
1100
- cursor.execute("SELECT theme_quiz_table, theme_bank FROM themes WHERE theme = %s", (theme,))
1101
- theme_data = cursor.fetchone()
1102
-
1103
- if not theme_data or not theme_data['theme_quiz_table'] or not theme_data['theme_bank']:
1104
- return {"error": "No quiz table found for this theme."}
1105
-
1106
- theme_quiz_table = theme_data['theme_quiz_table']
1107
- theme_bank_table = theme_data['theme_bank']
1108
-
1109
-
1110
- cursor.execute("SELECT quiz_id FROM quiz_response WHERE user_id_attempt = %s", (user_id,))
1111
- attempted_quizzes = {row['quiz_id'] for row in cursor.fetchall()}
1112
-
1113
-
1114
- cursor.execute(f"""
1115
- SELECT quiz_id, num_questions, questions_by_llm, correct_options_llm,
1116
- questions_by_master, correct_options_master
1117
- FROM {theme_quiz_table}
1118
- """)
1119
- quizzes = cursor.fetchall()
1120
-
1121
- if not quizzes:
1122
- return {"message": "No new quizzes available for this theme."}
1123
-
1124
- results = []
1125
- for quiz in quizzes:
1126
- quiz_id = quiz['quiz_id']
1127
- if quiz_id in attempted_quizzes:
1128
- continue
1129
-
1130
- ques_id_list = None
1131
- if quiz['questions_by_llm']:
1132
- try:
1133
- ques_id_list = json.loads(quiz['questions_by_llm'])
1134
- except json.JSONDecodeError:
1135
- logging.error(f"Invalid JSON in questions_by_llm for quiz_id {quiz_id}: {quiz['questions_by_llm']}")
1136
- continue
1137
-
1138
- elif quiz['questions_by_master']:
1139
- try:
1140
- ques_id_list = json.loads(quiz['questions_by_master'])
1141
- except json.JSONDecodeError:
1142
- logging.error(f"Invalid JSON in questions_by_master for quiz_id {quiz_id}: {quiz['questions_by_master']}")
1143
- continue
1144
-
1145
- logging.info(f"Quiz ID {quiz_id} - Extracted ques_id_list: {ques_id_list} (Type: {type(ques_id_list)})")
1146
-
1147
-
1148
- if isinstance(ques_id_list, dict):
1149
- ques_id_list = list(ques_id_list.values())
1150
- elif not isinstance(ques_id_list, list):
1151
- logging.error(f"Invalid ques_id_list type for quiz_id {quiz_id}: {type(ques_id_list)}")
1152
- continue
1153
-
1154
-
1155
- if not ques_id_list:
1156
- logging.warning(f"No questions found for quiz_id {quiz_id}")
1157
- continue
1158
-
1159
- number_of_questions = quiz['num_questions']
1160
-
1161
-
1162
- format_strings = ",".join(["%s"] * len(ques_id_list))
1163
- logging.info(f"Executing SQL query with ques_id_list: {ques_id_list}")
1164
-
1165
- cursor.execute(f"""
1166
- SELECT ques_id, question_by_llm, correct_option_llm,
1167
- question_by_master, correct_option_master
1168
- FROM {theme_bank_table}
1169
- WHERE ques_id IN ({format_strings})
1170
- """, tuple(ques_id_list))
1171
-
1172
- question_details = cursor.fetchall()
1173
-
1174
-
1175
- final_questions = []
1176
- final_correct_options = []
1177
- final_ques_ids = []
1178
-
1179
- for q in question_details:
1180
- question_text = q['question_by_llm'] or q['question_by_master']
1181
- correct_option = q['correct_option_llm'] or q['correct_option_master']
1182
-
1183
- if question_text and correct_option:
1184
- final_questions.append(question_text)
1185
- final_correct_options.append(correct_option)
1186
- final_ques_ids.append(q['ques_id'])
1187
-
1188
- results.append({
1189
- "quiz_id": quiz_id,
1190
- "num_questions": number_of_questions,
1191
- "ques_id": final_ques_ids,
1192
- "questions": final_questions,
1193
- "correct_options": final_correct_options
1194
- })
1195
-
1196
- return {"quizzes": results}
1197
-
1198
- except mysql.connector.Error as err:
1199
- logging.error(f"MySQL Error: {err}")
1200
- return {"error": "Database operation failed."}
1201
-
1202
- except Exception as e:
1203
- logging.error(f"Unexpected error: {str(e)}")
1204
- return {"error": "An unexpected error occurred."}
1205
-
1206
- finally:
1207
- if 'cursor' in locals():
1208
- cursor.close()
1209
- if 'connection' in locals():
1210
- connection.close()
1211
- logging.info("Database connection closed.")
1212
-
1213
-
 
 
 
1
+ import mysql.connector
2
+ from flask_bcrypt import Bcrypt
3
+ from flask import request, jsonify
4
+ import logging
5
+ import json
6
+ import traceback
7
+ from datetime import datetime
8
+ from question_verify import verify_question
9
+ from flask_jwt_extended import get_jwt_identity
10
+ import uuid
11
+ import random
12
+ import re
13
+ import json
14
+ from generate_question import generate_ques
15
+ import os
16
+
17
+ logging.basicConfig(
18
+ filename=os.path.join('/tmp', 'app.log'),
19
+ level=logging.DEBUG,
20
+ format='%(asctime)s - %(levelname)s - %(message)s'
21
+ )
22
+
23
+ bcrypt = Bcrypt()
24
+
25
+ def get_db_connection():
26
+ try:
27
+ connection = mysql.connector.connect(
28
+ host = "quamble-quamble.c.aivencloud.com",
29
+ user = "avnadmin",
30
+ password = "AVNS_NgB0Jv5F-yk567pP7fo",
31
+ database = "quamble",
32
+ port = "17939"
33
+ )
34
+ logging.info("Database connection established.")
35
+ return connection
36
+ except mysql.connector.Error as err :
37
+ print(f"Error: {err}")
38
+ logging.error("Error connecting to the database: %s", err)
39
+ return None
40
+
41
+
42
+ def add_user(username, email, password, role, preference_1=None, preference_2=None, preference_3=None, preference_4=None):
43
+
44
+
45
+ try:
46
+
47
+ user_id = str(uuid.uuid4())
48
+ connection = get_db_connection()
49
+ if connection:
50
+ cursor = connection.cursor()
51
+ hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
52
+ cursor.execute("""
53
+ INSERT INTO users (id, username, email, password, role, preference_1, preference_2, preference_3, preference_4)
54
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
55
+ """, (user_id, username, email, hashed_password, role, preference_1, preference_2, preference_3, preference_4))
56
+ connection.commit()
57
+ logging.info("User added: %s", username)
58
+
59
+ except mysql.connector.Error as err:
60
+ logging.error("Error adding user %s: %s", username, err)
61
+ finally:
62
+ #cursor.close()
63
+ #connection.close()
64
+ logging.info("Database connection closed after adding user.")
65
+
66
+
67
+
68
+
69
+ def get_user_by_email(email):
70
+ connection = get_db_connection()
71
+ if connection:
72
+ try:
73
+ cursor = connection.cursor(dictionary=True)
74
+ cursor.execute("SELECT * FROM users WHERE email=%s", (email,))
75
+ user = cursor.fetchone()
76
+ logging.info("User retrieved: %s", user)
77
+ return user
78
+ except mysql.connector.Error as err:
79
+ logging.error("Error retrieving user by email %s: %s", email, err)
80
+ return None
81
+ finally:
82
+ cursor.close()
83
+ connection.close()
84
+ logging.info("Database connection closed after retrieving user.")
85
+
86
+ def add_quiz(user_id, theme, questions, correct_options):
87
+ connection = get_db_connection()
88
+ if not connection:
89
+ logging.error("Database connection failed.")
90
+ return None
91
+
92
+ try:
93
+ cursor = connection.cursor()
94
+
95
+
96
+ cursor.execute("SELECT theme_quiz_table FROM Themes WHERE theme = %s", (theme,))
97
+ theme_data = cursor.fetchone()
98
+
99
+ if not theme_data:
100
+ logging.error("Invalid theme provided: %s", theme)
101
+ return None
102
+
103
+ theme_table = theme_data[0]
104
+
105
+
106
+ cursor.execute("SELECT COUNT(*) FROM users WHERE id = %s", (user_id,))
107
+ user_exists = cursor.fetchone()[0]
108
+ if user_exists == 0:
109
+ logging.error("User ID does not exist: %s", user_id)
110
+ return None
111
+
112
+
113
+ cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id, theme))
114
+ quiz_id = cursor.lastrowid
115
+
116
+
117
+ questions_json = json.dumps(questions)
118
+ correct_options_json = json.dumps(correct_options)
119
+
120
+
121
+ query = f"""
122
+ INSERT INTO {theme_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm)
123
+ VALUES (%s, %s, %s, %s)
124
+ """
125
+ cursor.execute(query, (quiz_id, user_id, questions_json, correct_options_json))
126
+
127
+ connection.commit()
128
+ logging.info("Quiz added for user_id %s with theme %s in table %s", user_id, theme, theme_table)
129
+ return quiz_id
130
+
131
+ except mysql.connector.Error as err:
132
+ logging.error("Error adding quiz: %s", err)
133
+ return None
134
+
135
+ finally:
136
+ if cursor:
137
+ cursor.close()
138
+ if connection:
139
+ connection.close()
140
+ logging.info("Database connection closed after adding quiz.")
141
+
142
+
143
+
144
+
145
+ def add_question_from_master():
146
+ if request.method == 'POST':
147
+ logging.info("Received a POST request to add questions to quiz.")
148
+
149
+ theme = request.form.get('theme')
150
+ if not theme:
151
+ logging.error("Theme not provided in the request.")
152
+ return jsonify({"error": "Please provide the theme."}), 400
153
+
154
+ question = request.form.get('question')
155
+ correct_option = request.form.get('correct_option')
156
+ if not question or not correct_option:
157
+ logging.error("Missing question or correct option in the request.")
158
+ return jsonify({"error": "Please provide both question and correct option."}), 400
159
+
160
+ question = question.replace("\t", "")
161
+ validation, feedback, difficulty = verify_question(question, correct_option)
162
+ if validation != 'valid':
163
+ logging.warning(f"Verification failed: {feedback}")
164
+ return jsonify({"error": f"Verification failed: {feedback}"}), 400
165
+
166
+ logging.info(f"Verified question: {question}")
167
+ logging.info(f"Verified correct option: {correct_option}")
168
+ logging.info(f"Difficulty level: {difficulty}")
169
+
170
+ user_id_creator = get_jwt_identity()
171
+ logging.info(f"Authenticated user ID: {user_id_creator}")
172
+
173
+ connection = get_db_connection()
174
+ if connection:
175
+ try:
176
+ cursor = connection.cursor()
177
+ cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
178
+ result = cursor.fetchone()
179
+ if not result:
180
+ logging.error("Invalid theme provided: %s", theme)
181
+ return jsonify({"error": "Invalid theme provided."}), 400
182
+
183
+ theme_bank = result[0]
184
+
185
+ question_json = json.dumps({"question": question})
186
+ check_query = f"""
187
+ SELECT COUNT(*)
188
+ FROM {theme_bank}
189
+ WHERE question_by_master = %s AND user_id_creator = %s
190
+ """
191
+ cursor.execute(check_query, (question_json, user_id_creator))
192
+ is_duplicate = cursor.fetchone()[0] > 0
193
+
194
+ if is_duplicate:
195
+ logging.warning("Duplicate question detected for user %s: %s", user_id_creator, question)
196
+ return jsonify({"error": "Duplicate question detected."}), 400
197
+
198
+ insert_query = f"""
199
+ INSERT INTO {theme_bank} (user_id_creator, question_by_master, correct_option_master, Difficulty)
200
+ VALUES (%s, %s, %s, %s)
201
+ """
202
+ cursor.execute(insert_query, (user_id_creator, question_json, correct_option, difficulty))
203
+ connection.commit()
204
+ logging.info("Question added for theme %s", theme)
205
+ return 'True'
206
+
207
+ except mysql.connector.Error as err:
208
+ logging.error("Error adding question: %s", err)
209
+ return jsonify({"error": "Failed to add question to the database."}), 500
210
+ finally:
211
+ #cursor.close()
212
+ #connection.close()
213
+ logging.info("Database connection closed.")
214
+ else:
215
+ logging.error("Database connection failed.")
216
+ return jsonify({"error": "Database connection failed."}), 500
217
+
218
+ def add_ques_llm(theme, question, options, correct_option, difficulty):
219
+ connection = get_db_connection()
220
+
221
+ if connection:
222
+ try:
223
+ cursor = connection.cursor()
224
+
225
+ cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
226
+ theme_bank_result = cursor.fetchone()
227
+
228
+ if not theme_bank_result:
229
+ logging.error("Invalid theme provided: %s", theme)
230
+ return None
231
+
232
+ theme_bank = theme_bank_result[0]
233
+ user_id_creator = get_jwt_identity()
234
+
235
+ question_json = json.dumps({
236
+ "question": question,
237
+ "options": options
238
+ })
239
+
240
+ check_query = f"SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm = %s"
241
+ cursor.execute(check_query, (question_json,))
242
+ is_duplicate = cursor.fetchone()[0] > 0
243
+
244
+ if is_duplicate:
245
+ logging.warning("Duplicate question detected for theme %s: %s", theme, question)
246
+ return "Duplicate"
247
+
248
+ insert_query = f"""
249
+ INSERT INTO {theme_bank} (user_id_creator, question_by_llm, correct_option_llm, Difficulty)
250
+ VALUES (%s, %s, %s, %s)
251
+ """
252
+ cursor.execute(insert_query, (user_id_creator, question_json, correct_option, difficulty))
253
+
254
+ connection.commit()
255
+ logging.info("Question added for theme %s", theme)
256
+ return 'True'
257
+
258
+ except mysql.connector.Error as err:
259
+ logging.error("Error adding question: %s", err)
260
+ return None
261
+ finally:
262
+ #cursor.close()
263
+ #connection.close()
264
+ logging.info("Database connection closed after adding question.")
265
+ else:
266
+ logging.error("Database connection failed.")
267
+ return None
268
+
269
+
270
+
271
+
272
+ def add_question_to_db(theme, question, correct_option, difficulty):
273
+ connection = get_db_connection()
274
+ try:
275
+ cursor = connection.cursor()
276
+
277
+
278
+ theme_check_query = "SELECT COUNT(*) FROM themes WHERE theme = %s"
279
+ cursor.execute(theme_check_query, (theme,))
280
+ theme_exists = cursor.fetchone()[0]
281
+
282
+ if theme_exists == 0:
283
+ insert_query = "INSERT INTO themes (theme, theme_bank, theme_quiz_table) VALUES (%s, %s, %s)"
284
+ cursor.execute(insert_query, (theme, f"{theme}_bank", f"theme_{theme}"))
285
+
286
+
287
+ safe_theme = re.sub(r'\W+', '_', theme)
288
+
289
+ table_creation_query = f"""
290
+ CREATE TABLE IF NOT EXISTS {safe_theme}_bank (
291
+ ques_id INT AUTO_INCREMENT PRIMARY KEY,
292
+ user_id_creator VARCHAR(64),
293
+ question_by_llm MEDIUMTEXT,
294
+ question_hash VARCHAR(640),
295
+ correct_option_llm MEDIUMTEXT,
296
+ question_by_master MEDIUMTEXT,
297
+ correct_option_master MEDIUMTEXT,
298
+ Difficulty VARCHAR(64),
299
+ is_used VARCHAR(64),
300
+ Issue VARCHAR(6400)
301
+ )
302
+ """
303
+ cursor.execute(table_creation_query)
304
+
305
+ theme_table_creation_query = f"""
306
+ CREATE TABLE IF NOT EXISTS theme_{safe_theme} (
307
+ quiz_id INT(8) PRIMARY KEY AUTO_INCREMENT,
308
+ user_id_creator VARCHAR(64),
309
+ questions_by_llm MEDIUMTEXT,
310
+ correct_options_llm MEDIUMTEXT,
311
+ questions_by_master MEDIUMTEXT,
312
+ correct_options_master MEDIUMTEXT,
313
+ num_questions INT(11),
314
+ user_id_attempt MEDIUMTEXT,
315
+ user_responses MEDIUMTEXT
316
+ )
317
+ """
318
+ cursor.execute(theme_table_creation_query)
319
+
320
+ check_query = f"SELECT COUNT(*) FROM '{safe_theme}_bank' WHERE question_by_llm = %s"
321
+ cursor.execute(check_query, (question,))
322
+ is_duplicate = cursor.fetchone()[0] > 0
323
+
324
+ if is_duplicate:
325
+ logging.warning("Duplicate question detected for theme %s: %s", safe_theme, question)
326
+ return "Duplicate"
327
+
328
+
329
+ query = f"INSERT INTO {safe_theme}_bank (question_by_llm, correct_option_llm, Difficulty, is_used) VALUES (%s, %s, %s, 'unused')"
330
+ cursor.execute(query, (question, correct_option, difficulty))
331
+ connection.commit()
332
+ return 'True'
333
+
334
+ except mysql.connector.Error as err:
335
+ logging.error("Error adding question: %s", err)
336
+ return None
337
+ finally:
338
+ #cursor.close()
339
+ #connection.close()
340
+ logging.info("Database connection closed")
341
+
342
+
343
+ def create_quiz(theme, num_questions):
344
+ connection = get_db_connection()
345
+ if connection:
346
+ try:
347
+ cursor = connection.cursor()
348
+
349
+
350
+ cursor.execute("SELECT theme_bank, theme_quiz_table FROM themes WHERE theme = %s", (theme,))
351
+ theme_data = cursor.fetchone()
352
+
353
+ if not theme_data:
354
+ logging.error("Invalid theme provided: %s", theme)
355
+ return jsonify({"error": "Invalid theme provided."}), 400
356
+
357
+ theme_bank, theme_quiz_table = theme_data
358
+
359
+ query_available_questions = f"""
360
+ SELECT COUNT(*)
361
+ FROM {theme_bank}
362
+ WHERE question_by_llm IS NOT NULL
363
+ AND question_by_llm != ''
364
+ AND (is_used IS NULL OR is_used = '')
365
+ """
366
+ cursor.execute(query_available_questions)
367
+ available_count = cursor.fetchone()[0]
368
+
369
+ if available_count < int(num_questions):
370
+ logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
371
+ return jsonify({"error": "Not enough questions available."}), 400
372
+
373
+
374
+ query_random_questions = f"""
375
+ SELECT ques_id, question_by_llm, correct_option_llm
376
+ FROM {theme_bank}
377
+ WHERE question_by_llm IS NOT NULL
378
+ AND question_by_llm != ''
379
+ AND (is_used IS NULL OR is_used = '')
380
+ ORDER BY RAND()
381
+ LIMIT {int(num_questions)}
382
+ """
383
+ cursor.execute(query_random_questions)
384
+ selected_questions = cursor.fetchall()
385
+
386
+ questions = []
387
+ correct_options = []
388
+ selected_question_ids = []
389
+
390
+ for question_id, question, correct_option in selected_questions:
391
+ questions.append(question)
392
+ correct_options.append(correct_option)
393
+ selected_question_ids.append(question_id)
394
+
395
+
396
+ mark_as_used_query = f"""
397
+ UPDATE {theme_bank}
398
+ SET is_used = 'used'
399
+ WHERE ques_id IN ({', '.join(map(str, selected_question_ids))})
400
+ """
401
+ cursor.execute(mark_as_used_query)
402
+
403
+ user_id_creator = get_jwt_identity()
404
+
405
+
406
+ cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
407
+ quiz_id = cursor.lastrowid
408
+ questions_json = json.dumps(questions)
409
+ correct_options_json = json.dumps(correct_options)
410
+
411
+
412
+ insert_quiz_query = f"""
413
+ INSERT INTO {theme_quiz_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm, num_questions)
414
+ VALUES (%s, %s, %s, %s, %s)
415
+ """
416
+ cursor.execute(insert_quiz_query, (quiz_id, user_id_creator, questions_json, correct_options_json, num_questions))
417
+ connection.commit()
418
+ logging.info("Quiz created successfully for theme %s with %d questions.", theme, num_questions)
419
+
420
+ questions_reflect = json.loads(questions_json)
421
+ correct_options_reflect = json.loads(correct_options_json)
422
+
423
+ return jsonify({
424
+ "quiz_id": quiz_id,
425
+ "questions": questions_reflect,
426
+ "correct_options": correct_options_reflect
427
+ }), 201
428
+
429
+ except mysql.connector.Error as err:
430
+ logging.error("Error creating quiz: %s", err)
431
+ return jsonify({"error": "Failed to create quiz."}), 500
432
+
433
+ except Exception as e:
434
+ logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
435
+ return jsonify({"error": "An unexpected error occurred."}), 500
436
+
437
+ finally:
438
+ if cursor:
439
+ cursor.close()
440
+ if connection:
441
+ connection.close()
442
+ logging.info("Database connection closed.")
443
+ else:
444
+ logging.error("Database connection failed.")
445
+ return jsonify({"error": "Database connection failed."}), 500
446
+
447
+
448
+
449
+ def create_quiz_master(user_id_creator, theme, num_questions):
450
+ connection = get_db_connection()
451
+ if connection:
452
+ try:
453
+ cursor = connection.cursor()
454
+ cursor.execute("SELECT theme_bank, theme_quiz_table FROM themes WHERE theme = %s", (theme,))
455
+ theme_data = cursor.fetchone()
456
+ if not theme_data:
457
+ logging.error("Invalid theme provided: %s", theme)
458
+ return jsonify({"error": "Invalid theme provided."}), 400
459
+ theme_bank, theme_quiz_table = theme_data
460
+
461
+ query_available_questions = f"""
462
+ SELECT COUNT(*) FROM {theme_bank} WHERE question_by_master IS NOT NULL AND is_used IS NULL OR is_used = "unused"
463
+ """
464
+ cursor.execute(query_available_questions)
465
+ available_count = cursor.fetchone()[0]
466
+
467
+ if available_count < int(num_questions):
468
+ logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
469
+ return jsonify({"error": "Not enough questions available."}), 400
470
+
471
+ query_random_questions = f"""
472
+ SELECT ques_id, question_by_master, correct_option_master
473
+ FROM {theme_bank}
474
+ WHERE question_by_master IS NOT NULL AND question_by_master != ''
475
+ ORDER BY RAND()
476
+ LIMIT {int(num_questions)}
477
+ """
478
+ cursor.execute(query_random_questions)
479
+ selected_questions = cursor.fetchall()
480
+
481
+ questions, correct_options, selected_question_ids = [], [], []
482
+ for question_id, question, correct_option in selected_questions:
483
+ questions.append(question)
484
+ correct_options.append(correct_option)
485
+ selected_question_ids.append(question_id)
486
+
487
+ mark_as_used_query = f"""
488
+ UPDATE {theme_bank}
489
+ SET is_used = 'used'
490
+ WHERE ques_id IN ({', '.join(map(str, selected_question_ids))})
491
+ """
492
+ cursor.execute(mark_as_used_query)
493
+
494
+ cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
495
+ quiz_id = cursor.lastrowid
496
+
497
+ insert_quiz_query = f"""
498
+ INSERT INTO {theme_quiz_table} (quiz_id, user_id_creator, questions_by_master, correct_options_master, num_questions)
499
+ VALUES (%s, %s, %s, %s, %s)
500
+ """
501
+ cursor.execute(insert_quiz_query, (quiz_id, user_id_creator, json.dumps(questions), json.dumps(correct_options), num_questions))
502
+ connection.commit()
503
+
504
+ return jsonify({
505
+ "quiz_id": quiz_id,
506
+ "ques_id": selected_question_ids,
507
+ "correct_options": correct_options
508
+ }), 201
509
+
510
+ except mysql.connector.Error as err:
511
+ logging.error("Error creating quiz: %s", err)
512
+ return jsonify({"error": "Failed to create quiz."}), 500
513
+ except Exception as e:
514
+ logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
515
+ return jsonify({"error": "An unexpected error occurred."}), 500
516
+ finally:
517
+ if cursor:
518
+ cursor.close()
519
+ if connection:
520
+ connection.close()
521
+ logging.info("Database connection closed.")
522
+ else:
523
+ logging.error("Database connection failed.")
524
+ return jsonify({"error": "Database connection failed."}), 500
525
+
526
+
527
+
528
+
529
+ def recording_issue(theme, ques_id, issue_description):
530
+ connection = get_db_connection()
531
+ if connection:
532
+ try:
533
+ cursor = connection.cursor()
534
+ cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
535
+ theme_data = cursor.fetchone()
536
+
537
+ if not theme_data:
538
+ logging.error("Invalid theme provided: %s", theme)
539
+ return jsonify({"error": "Invalid theme provided."}), 400
540
+
541
+ theme_bank = theme_data[0]
542
+
543
+ select_query = f"SELECT COUNT(*) FROM {theme_bank} WHERE ques_id = %s"
544
+ cursor.execute(select_query, (ques_id,))
545
+ count = cursor.fetchone()[0]
546
+
547
+ if count == 0:
548
+ return jsonify({"error": f"Question ID {ques_id} does not exist in the database"}), 400
549
+
550
+ update_query = f"""
551
+ UPDATE {theme_bank}
552
+ SET Issue = %s
553
+ WHERE ques_id = %s
554
+ """
555
+ cursor.execute(update_query, (issue_description, ques_id))
556
+ connection.commit()
557
+
558
+ logging.info("Issue recorded for theme %s and question_id %s.", theme, ques_id)
559
+ return jsonify({"message": "Issue reported successfully"}), 201
560
+
561
+ except mysql.connector.Error as err:
562
+ return jsonify({"error": "An error occurred while reporting the issue", "details": str(err)}), 500
563
+
564
+ finally:
565
+ cursor.close()
566
+ connection.close()
567
+ logging.info("Database connection closed.")
568
+ else:
569
+ logging.error("Database connection failed.")
570
+ return jsonify({"error": "Database connection failed."}), 500
571
+
572
+
573
+
574
+ def record_feedback(user_id, rating, comments):
575
+ connection = get_db_connection()
576
+ if connection:
577
+ try:
578
+ cursor = connection.cursor()
579
+
580
+ insert_query= """
581
+ INSERT INTO feedbacks (user_id, rating, comments)
582
+ VALUES (%s, %s, %s)
583
+ """
584
+ cursor.execute(insert_query, (user_id, rating, comments))
585
+
586
+ # Commit the transaction and close the connection
587
+ connection.commit()
588
+ cursor.close()
589
+ connection.close()
590
+ return jsonify({"message": "Feedback submitted successfully"}), 201
591
+
592
+ except mysql.connector.Error as err:
593
+ print(f"Error: {err}")
594
+ return jsonify({"error": "Failed to submit feedback"}), 500
595
+
596
+
597
+ def create_quiz_by_id(theme, num_questions):
598
+ connection = get_db_connection()
599
+ if not connection:
600
+ logging.error("Database connection failed.")
601
+ return jsonify({"error": "Database connection failed."}), 500
602
+
603
+ try:
604
+ cursor = connection.cursor()
605
+ cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
606
+ result = cursor.fetchone()
607
+
608
+ if not result:
609
+ logging.error("Invalid theme provided: %s", theme)
610
+ return jsonify({"error": "Invalid theme provided."}), 400
611
+
612
+ theme_bank = result[0]
613
+
614
+ cursor.execute(f"""
615
+ SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm IS NOT NULL AND is_used IS NULL OR is_used = "unused"
616
+ """)
617
+ available_count = cursor.fetchone()[0]
618
+
619
+ shortfall = int(num_questions) - available_count
620
+ logging.info("Available questions: %d; Required: %d; Shortfall: %d", available_count, int(num_questions), shortfall)
621
+
622
+ added_count = 0
623
+ attempts = 0
624
+ max_attempts = shortfall * 10
625
+
626
+ while added_count < shortfall and attempts < max_attempts:
627
+ attempts += 1
628
+ ques = generate_ques(theme)
629
+
630
+ question_match = re.search(r'Question:\s*(.*)', ques)
631
+ options_matches = re.findall(r'([A-D])\)\s*(.*)', ques)
632
+ correct_answer_match = re.search(r'Correct answer:\s*([A-D])', ques)
633
+ difficulty_match = re.search(r'Difficulty level:\s*(.*)', ques)
634
+
635
+ if question_match and options_matches and correct_answer_match:
636
+ question = question_match.group(1).strip()
637
+ options = [f"{opt[0]}) {opt[1].strip()}" for opt in options_matches]
638
+ correct_option = correct_answer_match.group(1).strip().upper()
639
+ difficulty = difficulty_match.group(1).strip() if difficulty_match else "medium"
640
+
641
+ if correct_option not in ['A', 'B', 'C', 'D']:
642
+ continue
643
+
644
+ status = add_ques_llm(theme, question, options, correct_option, difficulty)
645
+ if status == "True":
646
+ added_count += 1
647
+ elif status == "Duplicate":
648
+ continue # retry
649
+ else:
650
+ logging.warning("Failed to insert generated question.")
651
+ else:
652
+ logging.warning("Malformed question generated; skipping.")
653
+
654
+ if added_count < shortfall:
655
+ return jsonify({"error": f"Could not generate enough unique questions. Needed {shortfall}, added {added_count}."}), 400
656
+
657
+
658
+ cursor.execute(f"""
659
+ SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm IS NOT NULL AND is_used IS NULL OR is_used = "unused"
660
+ """)
661
+ available_count = cursor.fetchone()[0]
662
+ if available_count < int(num_questions):
663
+ logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
664
+ return jsonify({"error": "Not enough questions available."}), 400
665
+
666
+ cursor.execute(f"""
667
+ SELECT ques_id, correct_option_llm FROM {theme_bank}
668
+ WHERE question_by_llm IS NOT NULL AND question_by_llm != ''
669
+ AND (is_used IS NULL OR is_used = '')
670
+ ORDER BY RAND() LIMIT {int(num_questions)}
671
+ """)
672
+ selected_questions = cursor.fetchall()
673
+
674
+ questions = [q[0] for q in selected_questions]
675
+ correct_options = [q[1] for q in selected_questions]
676
+
677
+ cursor.execute(f"""
678
+ UPDATE {theme_bank} SET is_used = 'used'
679
+ WHERE ques_id IN ({', '.join(map(str, questions))})
680
+ """)
681
+
682
+ user_id_creator = get_jwt_identity()
683
+ cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
684
+ quiz_id = cursor.lastrowid
685
+
686
+ cursor.execute("SELECT theme_quiz_table FROM themes WHERE theme = %s", (theme,))
687
+ theme_table = cursor.fetchone()[0]
688
+
689
+ cursor.execute(f"""
690
+ INSERT INTO {theme_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm, num_questions)
691
+ VALUES (%s, %s, %s, %s, %s)
692
+ """, (quiz_id, user_id_creator, json.dumps(questions), json.dumps(correct_options), num_questions))
693
+ connection.commit()
694
+
695
+ logging.info("Quiz created successfully for theme %s with %d questions.", theme, num_questions)
696
+ return jsonify({"quiz_id": quiz_id, "questions": questions, "correct_options": correct_options}), 201
697
+
698
+ except mysql.connector.Error as err:
699
+ logging.error("Error creating quiz: %s", err)
700
+ return jsonify({"error": "Failed to create quiz."}), 500
701
+
702
+ except Exception as e:
703
+ logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
704
+ return jsonify({"error": "An unexpected error occurred."}), 500
705
+
706
+ finally:
707
+ if cursor:
708
+ cursor.close()
709
+ if connection:
710
+ connection.close()
711
+ logging.info("Database connection closed.")
712
+
713
+
714
+
715
+ def add_theme_if_not_exists(theme):
716
+ connection = get_db_connection()
717
+ try:
718
+ cursor = connection.cursor()
719
+
720
+ bank_table = f"{theme}_bank"
721
+ quiz_table = f"theme_{theme}"
722
+
723
+ theme_check_query = "SELECT COUNT(*) FROM themes WHERE theme = %s"
724
+ cursor.execute(theme_check_query, (theme,))
725
+ theme_exists = cursor.fetchone()[0]
726
+
727
+
728
+ if theme_exists == 0:
729
+ insert_query = "INSERT INTO themes (theme, theme_bank, theme_quiz_table) VALUES (%s, %s, %s)"
730
+ cursor.execute(insert_query, (theme, f"{theme}_bank", f"theme_{theme}"))
731
+ connection.commit()
732
+
733
+ create_bank_table = f"""
734
+ CREATE TABLE IF NOT EXISTS `{bank_table}` (
735
+ ques_id INT PRIMARY KEY AUTO_INCREMENT,
736
+ user_id_creator VARCHAR(64),
737
+ question_by_llm MEDIUMTEXT,
738
+ question_hash VARCHAR(640),
739
+ correct_option_llm MEDIUMTEXT,
740
+ question_by_master MEDIUMTEXT,
741
+ correct_option_master MEDIUMTEXT,
742
+ Difficulty VARCHAR(64),
743
+ is_used VARCHAR(64),
744
+ Issue VARCHAR(6400)
745
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
746
+ """
747
+ cursor.execute(create_bank_table)
748
+ logging.info("Created table: %s", bank_table)
749
+
750
+ # Create theme_ table
751
+ create_quiz_table = f"""
752
+ CREATE TABLE IF NOT EXISTS `{quiz_table}` (
753
+ quiz_id INT PRIMARY KEY AUTO_INCREMENT,
754
+ user_id_creator VARCHAR(64),
755
+ questions_by_llm MEDIUMTEXT,
756
+ correct_options_llm MEDIUMTEXT,
757
+ questions_by_master MEDIUMTEXT,
758
+ correct_options_master MEDIUMTEXT,
759
+ num_questions INT,
760
+ user_id_attempt MEDIUMTEXT,
761
+ user_responses MEDIUMTEXT
762
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
763
+ """
764
+ cursor.execute(create_quiz_table)
765
+ logging.info("Created table: %s", quiz_table)
766
+
767
+ except mysql.connector.Error as err:
768
+ logging.error("Error adding theme: %s", err)
769
+ return jsonify({"error": "Failed to add theme."}), 500
770
+ finally:
771
+ cursor.close()
772
+ connection.close()
773
+ logging.info("Database connection is closed")
774
+
775
+ return not theme_exists
776
+
777
+ def profile_added_db(first_name, last_name, organisation, industry, bio):
778
+ connection = get_db_connection()
779
+ if connection:
780
+ try:
781
+ cursor = connection.cursor(dictionary=True)
782
+ user_id_creator = get_jwt_identity()
783
+ logging.info(f"Authenticated user ID: {user_id_creator}")
784
+
785
+ query_check_user = "SELECT COUNT(*) AS user_count FROM users WHERE id = %s"
786
+ cursor.execute(query_check_user, (user_id_creator,))
787
+ user_exists = cursor.fetchone()['user_count']
788
+
789
+ if user_exists:
790
+
791
+ query_update_user = """
792
+ UPDATE users
793
+ SET first_name = %s, last_name = %s, organisation = %s, industry = %s, bio = %s
794
+ WHERE id = %s
795
+ """
796
+ cursor.execute(query_update_user, (first_name, last_name, organisation, industry, bio, user_id_creator))
797
+ connection.commit()
798
+
799
+ logging.info(f"Profile for user {user_id_creator} updated successfully.")
800
+ return jsonify({"message": "Profile updated successfully!"}), 200
801
+ else:
802
+
803
+ logging.warning(f"User with ID {user_id_creator} does not exist.")
804
+ return jsonify({"error": "User does not exist."}), 404
805
+ except Exception as e:
806
+ logging.error(f"Unexpected error: {e}")
807
+ return jsonify({"error":"Failed to update profile"}), 500
808
+ finally:
809
+ if connection.is_connected():
810
+ cursor.close()
811
+ connection.close()
812
+ logging.info("Database connection closed.")
813
+
814
+
815
+ def view_profile_db():
816
+ try:
817
+
818
+ user_id_creator = get_jwt_identity()
819
+ logging.info(f"Authenticated user ID: {user_id_creator}")
820
+ connection = get_db_connection()
821
+ if connection:
822
+ cursor = connection.cursor(dictionary=True)
823
+
824
+ query_check_user = "SELECT COUNT(*) AS user_count FROM users WHERE id = %s"
825
+ cursor.execute(query_check_user, (user_id_creator,))
826
+ user_exists = cursor.fetchone()['user_count']
827
+
828
+ if user_exists:
829
+
830
+ query_get_profile = """
831
+ SELECT first_name, last_name, role, organisation, industry, bio
832
+ FROM users
833
+ WHERE id = %s
834
+ """
835
+ cursor.execute(query_get_profile, (user_id_creator,))
836
+ profile = cursor.fetchone()
837
+
838
+ if profile:
839
+ logging.info(f"User {user_id_creator}'s profile fetched successfully.")
840
+ return jsonify({
841
+ "first_name": profile["first_name"],
842
+ "last_name": profile["last_name"],
843
+ "role": profile["role"],
844
+ "organisation": profile["organisation"],
845
+ "industry": profile["industry"],
846
+ "bio": profile["bio"]
847
+ }), 200
848
+ else:
849
+ logging.warning(f"Profile for user {user_id_creator} not found.")
850
+ return jsonify({"error": "Profile not found"}), 404
851
+ else:
852
+ logging.warning(f"User with ID {user_id_creator} does not exist.")
853
+ return jsonify({"error": "User does not exist."}), 404
854
+
855
+
856
+ except Exception as e:
857
+ logging.error(f"Unexpected error: {e}")
858
+ return jsonify({"error": f"Unexpected error: {str(e)}"}), 500
859
+ finally:
860
+ if connection.is_connected():
861
+ cursor.close()
862
+ connection.close()
863
+ logging.info("Database connection closed.")
864
+
865
+
866
+
867
+ def view_quiz_score_db(user_id, theme_table, theme, quiz_id):
868
+ try:
869
+ connection = get_db_connection()
870
+ if connection:
871
+ cursor = connection.cursor(dictionary=True)
872
+
873
+
874
+ query = f"""
875
+ SELECT num_questions
876
+ FROM {theme_table}
877
+ WHERE quiz_id = %s
878
+ """
879
+ cursor.execute(query, (quiz_id,))
880
+ theme_data = cursor.fetchone()
881
+
882
+ num_questions = theme_data['num_questions'] if theme_data else None
883
+ if num_questions is None:
884
+ logging.warning(f"Theme '{theme}' not found in theme_table.")
885
+ return jsonify({"error": "Theme not found"}), 404
886
+
887
+
888
+ query = """
889
+ SELECT score, time_taken, user_response
890
+ FROM quiz_response
891
+ WHERE quiz_id = %s AND user_id_attempt = %s
892
+ """
893
+ cursor.execute(query, (quiz_id, user_id))
894
+ quiz_response = cursor.fetchone()
895
+
896
+ if quiz_response:
897
+ user_response = quiz_response['user_response']
898
+ score = quiz_response['score']
899
+ time = quiz_response['time_taken']
900
+
901
+
902
+ accuracy = (score / num_questions) * 100 if num_questions > 0 else 0
903
+
904
+ response_data = {
905
+ "quiz_id": quiz_id,
906
+ "theme": theme,
907
+ "user_response": user_response,
908
+ "score": score,
909
+ "accuracy": round(accuracy, 2),
910
+ "time_taken": time
911
+ }
912
+
913
+ logging.info(f"Score and accuracy for user {user_id}, quiz {quiz_id} fetched successfully.")
914
+ return jsonify(response_data), 200
915
+
916
+
917
+ logging.warning(f"No quiz response found for user {user_id} and quiz {quiz_id}.")
918
+ return jsonify({"error": "Quiz response not found"}), 404
919
+
920
+ except Exception as e:
921
+ logging.error(f"Error fetching quiz score: {e}")
922
+ return jsonify({"error": "Unable to fetch quiz score"}), 500
923
+
924
+ finally:
925
+ if connection.is_connected():
926
+ cursor.close()
927
+ connection.close()
928
+
929
+
930
+
931
+ def get_recent_quizzes_db(user_id, limit=5):
932
+
933
+ try:
934
+ connection = get_db_connection()
935
+ if not connection:
936
+ return {"error": "Database connection failed."}
937
+
938
+ cursor = connection.cursor(dictionary=True)
939
+
940
+
941
+ cursor.execute("""
942
+ SELECT qr.quiz_id, qr.theme, qr.user_response, qr.score, qr.time_taken, qr.submitted_on,
943
+ t.theme_quiz_table
944
+ FROM quiz_response qr
945
+ JOIN themes t ON qr.theme = t.theme
946
+ WHERE qr.user_id_attempt = %s
947
+ ORDER BY qr.submitted_on DESC
948
+ LIMIT %s
949
+ """, (user_id, limit))
950
+
951
+ recent_quizzes = cursor.fetchall()
952
+ if not recent_quizzes:
953
+ return {"message": "No quizzes found for the user."}
954
+
955
+ results = []
956
+ for quiz in recent_quizzes:
957
+ quiz_id = quiz['quiz_id']
958
+ theme = quiz['theme']
959
+ theme_quiz_table = quiz['theme_quiz_table']
960
+
961
+
962
+ if not theme_quiz_table:
963
+ logging.warning(f"No quiz table found for theme: {theme}")
964
+ continue
965
+
966
+
967
+ cursor.execute(f"""
968
+ SELECT questions_by_llm, correct_options_llm,
969
+ questions_by_master, correct_options_master
970
+ FROM {theme_quiz_table}
971
+ WHERE quiz_id = %s
972
+ """, (quiz_id,))
973
+
974
+ quiz_details = cursor.fetchone()
975
+ if not quiz_details:
976
+ logging.warning(f"Quiz '{quiz_id}' not found in theme table '{theme_quiz_table}'.")
977
+ continue
978
+
979
+ questions = quiz_details['questions_by_llm'] or quiz_details['questions_by_master']
980
+ correct_answers = quiz_details['correct_options_llm'] or quiz_details['correct_options_master']
981
+
982
+
983
+ if not questions or not correct_answers:
984
+ logging.warning(f"Quiz '{quiz_id}' has no questions or answers.")
985
+ continue
986
+
987
+ results.append({
988
+ "quiz_id": quiz_id,
989
+ "theme": theme,
990
+ "questions": questions,
991
+ "correct_answers": correct_answers,
992
+ "user_responses": quiz['user_response'],
993
+ "score": quiz["score"],
994
+ "time_taken": quiz['time_taken'],
995
+ "date_attempted": quiz['submitted_on']
996
+ })
997
+
998
+ return results
999
+
1000
+ except mysql.connector.Error as err:
1001
+ logging.error(f"MySQL Error: {err}")
1002
+ return {"error": "Database operation failed."}
1003
+
1004
+ except Exception as e:
1005
+ logging.error(f"Unexpected error: {str(e)}")
1006
+ return {"error": "An unexpected error occurred."}
1007
+
1008
+ finally:
1009
+ if 'cursor' in locals():
1010
+ cursor.close()
1011
+ if 'connection' in locals():
1012
+ connection.close()
1013
+ logging.info("Database connection closed.")
1014
+
1015
+
1016
+
1017
+ def get_quiz_details_db(user_id, quiz_id):
1018
+
1019
+ try:
1020
+ connection = get_db_connection()
1021
+ if not connection:
1022
+ return {"error": "Database connection failed."}
1023
+
1024
+ cursor = connection.cursor(dictionary=True)
1025
+ logging.info(f"Checking for user_id_attempt: {user_id}, quiz_id: {quiz_id}")
1026
+ cursor.execute("""
1027
+ SELECT qr.quiz_id, qr.theme, qr.user_response, qr.score, qr.time_taken, qr.submitted_on,
1028
+ t.theme_quiz_table
1029
+ FROM quiz_response qr
1030
+ JOIN themes t ON qr.theme = t.theme
1031
+ WHERE qr.user_id_attempt = %s AND qr.quiz_id = %s
1032
+ """, (user_id, quiz_id))
1033
+
1034
+ quiz = cursor.fetchone()
1035
+ if not quiz:
1036
+ return {"message": "Quiz not found."}
1037
+
1038
+ theme_quiz_table = quiz['theme_quiz_table']
1039
+
1040
+ if not theme_quiz_table:
1041
+ logging.warning(f"No quiz table found for theme: {quiz['theme']}")
1042
+ return {"error": "Invalid theme data."}
1043
+
1044
+
1045
+ cursor.execute(f"""
1046
+ SELECT questions_by_llm, correct_options_llm,
1047
+ questions_by_master, correct_options_master
1048
+ FROM {theme_quiz_table}
1049
+ WHERE quiz_id = %s
1050
+ """, (quiz_id,))
1051
+
1052
+ quiz_details = cursor.fetchone()
1053
+ if not quiz_details:
1054
+ logging.warning(f"Quiz '{quiz_id}' not found in theme table '{theme_quiz_table}'.")
1055
+ return {"error": "Quiz questions not found."}
1056
+
1057
+
1058
+ questions = quiz_details['questions_by_llm'] or quiz_details['questions_by_master']
1059
+ correct_answers = quiz_details['correct_options_llm'] or quiz_details['correct_options_master']
1060
+
1061
+ if not questions or not correct_answers:
1062
+ logging.warning(f"Quiz '{quiz_id}' has incomplete data.")
1063
+ return {"error": "Incomplete quiz data."}
1064
+
1065
+
1066
+ return {
1067
+ "quiz_id": quiz["quiz_id"],
1068
+ "theme": quiz["theme"],
1069
+ "questions": questions,
1070
+ "correct_answers": correct_answers,
1071
+ "user_responses": quiz["user_response"],
1072
+ "score": quiz["score"],
1073
+ "time_taken": quiz["time_taken"],
1074
+ "date_attempted": quiz["submitted_on"]
1075
+ }
1076
+
1077
+ except mysql.connector.Error as err:
1078
+ logging.error(f"MySQL Error: {err}")
1079
+ return {"error": "Database operation failed."}
1080
+
1081
+ except Exception as e:
1082
+ logging.error(f"Unexpected error: {str(e)}")
1083
+ return {"error": "An unexpected error occurred."}
1084
+
1085
+ finally:
1086
+ if 'cursor' in locals():
1087
+ cursor.close()
1088
+ if 'connection' in locals():
1089
+ connection.close()
1090
+ logging.info("Database connection closed.")
1091
+
1092
+
1093
+ def fetch_quiz_for_theme_db(user_id, theme):
1094
+ try:
1095
+ connection = get_db_connection()
1096
+ if not connection:
1097
+ return {"error": "Database connection failed."}
1098
+
1099
+ cursor = connection.cursor(dictionary=True)
1100
+
1101
+
1102
+ cursor.execute("SELECT theme_quiz_table, theme_bank FROM themes WHERE theme = %s", (theme,))
1103
+ theme_data = cursor.fetchone()
1104
+
1105
+ if not theme_data or not theme_data['theme_quiz_table'] or not theme_data['theme_bank']:
1106
+ return {"error": "No quiz table found for this theme."}
1107
+
1108
+ theme_quiz_table = theme_data['theme_quiz_table']
1109
+ theme_bank_table = theme_data['theme_bank']
1110
+
1111
+
1112
+ cursor.execute("SELECT quiz_id FROM quiz_response WHERE user_id_attempt = %s", (user_id,))
1113
+ attempted_quizzes = {row['quiz_id'] for row in cursor.fetchall()}
1114
+
1115
+
1116
+ cursor.execute(f"""
1117
+ SELECT quiz_id, num_questions, questions_by_llm, correct_options_llm,
1118
+ questions_by_master, correct_options_master
1119
+ FROM {theme_quiz_table}
1120
+ """)
1121
+ quizzes = cursor.fetchall()
1122
+
1123
+ if not quizzes:
1124
+ return {"message": "No new quizzes available for this theme."}
1125
+
1126
+ results = []
1127
+ for quiz in quizzes:
1128
+ quiz_id = quiz['quiz_id']
1129
+ if quiz_id in attempted_quizzes:
1130
+ continue
1131
+
1132
+ ques_id_list = None
1133
+ if quiz['questions_by_llm']:
1134
+ try:
1135
+ ques_id_list = json.loads(quiz['questions_by_llm'])
1136
+ except json.JSONDecodeError:
1137
+ logging.error(f"Invalid JSON in questions_by_llm for quiz_id {quiz_id}: {quiz['questions_by_llm']}")
1138
+ continue
1139
+
1140
+ elif quiz['questions_by_master']:
1141
+ try:
1142
+ ques_id_list = json.loads(quiz['questions_by_master'])
1143
+ except json.JSONDecodeError:
1144
+ logging.error(f"Invalid JSON in questions_by_master for quiz_id {quiz_id}: {quiz['questions_by_master']}")
1145
+ continue
1146
+
1147
+ logging.info(f"Quiz ID {quiz_id} - Extracted ques_id_list: {ques_id_list} (Type: {type(ques_id_list)})")
1148
+
1149
+
1150
+ if isinstance(ques_id_list, dict):
1151
+ ques_id_list = list(ques_id_list.values())
1152
+ elif not isinstance(ques_id_list, list):
1153
+ logging.error(f"Invalid ques_id_list type for quiz_id {quiz_id}: {type(ques_id_list)}")
1154
+ continue
1155
+
1156
+
1157
+ if not ques_id_list:
1158
+ logging.warning(f"No questions found for quiz_id {quiz_id}")
1159
+ continue
1160
+
1161
+ number_of_questions = quiz['num_questions']
1162
+
1163
+
1164
+ format_strings = ",".join(["%s"] * len(ques_id_list))
1165
+ logging.info(f"Executing SQL query with ques_id_list: {ques_id_list}")
1166
+
1167
+ cursor.execute(f"""
1168
+ SELECT ques_id, question_by_llm, correct_option_llm,
1169
+ question_by_master, correct_option_master
1170
+ FROM {theme_bank_table}
1171
+ WHERE ques_id IN ({format_strings})
1172
+ """, tuple(ques_id_list))
1173
+
1174
+ question_details = cursor.fetchall()
1175
+
1176
+
1177
+ final_questions = []
1178
+ final_correct_options = []
1179
+ final_ques_ids = []
1180
+
1181
+ for q in question_details:
1182
+ question_text = q['question_by_llm'] or q['question_by_master']
1183
+ correct_option = q['correct_option_llm'] or q['correct_option_master']
1184
+
1185
+ if question_text and correct_option:
1186
+ final_questions.append(question_text)
1187
+ final_correct_options.append(correct_option)
1188
+ final_ques_ids.append(q['ques_id'])
1189
+
1190
+ results.append({
1191
+ "quiz_id": quiz_id,
1192
+ "num_questions": number_of_questions,
1193
+ "ques_id": final_ques_ids,
1194
+ "questions": final_questions,
1195
+ "correct_options": final_correct_options
1196
+ })
1197
+
1198
+ return {"quizzes": results}
1199
+
1200
+ except mysql.connector.Error as err:
1201
+ logging.error(f"MySQL Error: {err}")
1202
+ return {"error": "Database operation failed."}
1203
+
1204
+ except Exception as e:
1205
+ logging.error(f"Unexpected error: {str(e)}")
1206
+ return {"error": "An unexpected error occurred."}
1207
+
1208
+ finally:
1209
+ if 'cursor' in locals():
1210
+ cursor.close()
1211
+ if 'connection' in locals():
1212
+ connection.close()
1213
+ logging.info("Database connection closed.")
1214
+
1215
+