Spaces:
Running
Running
Commit ·
f9ff81a
1
Parent(s): 672a9eb
performance optimize: N+1 ....
Browse files- backend/app/api/routes/admin.py +3 -1
- backend/app/api/routes/tests.py +24 -16
- backend/pytest.ini +5 -0
backend/app/api/routes/admin.py
CHANGED
|
@@ -744,9 +744,10 @@ async def upload_questions_file(
|
|
| 744 |
db.query(func.count(Question.id)).filter(Question.test_id == test_id).scalar()
|
| 745 |
or 0
|
| 746 |
)
|
|
|
|
| 747 |
total_added = 0.0
|
| 748 |
for question in validated_questions:
|
| 749 |
-
|
| 750 |
Question(
|
| 751 |
test_id=test_id,
|
| 752 |
question_type=question.question_type,
|
|
@@ -762,6 +763,7 @@ async def upload_questions_file(
|
|
| 762 |
)
|
| 763 |
total_added += question.marks
|
| 764 |
|
|
|
|
| 765 |
test.total_marks = (test.total_marks or 0.0) + total_added
|
| 766 |
db.commit()
|
| 767 |
|
|
|
|
| 744 |
db.query(func.count(Question.id)).filter(Question.test_id == test_id).scalar()
|
| 745 |
or 0
|
| 746 |
)
|
| 747 |
+
new_questions = []
|
| 748 |
total_added = 0.0
|
| 749 |
for question in validated_questions:
|
| 750 |
+
new_questions.append(
|
| 751 |
Question(
|
| 752 |
test_id=test_id,
|
| 753 |
question_type=question.question_type,
|
|
|
|
| 763 |
)
|
| 764 |
total_added += question.marks
|
| 765 |
|
| 766 |
+
db.add_all(new_questions)
|
| 767 |
test.total_marks = (test.total_marks or 0.0) + total_added
|
| 768 |
db.commit()
|
| 769 |
|
backend/app/api/routes/tests.py
CHANGED
|
@@ -206,7 +206,7 @@ def start_test(
|
|
| 206 |
TestAttempt.test_id == test_id,
|
| 207 |
TestAttempt.status == TestStatus.in_progress,
|
| 208 |
)
|
| 209 |
-
.
|
| 210 |
)
|
| 211 |
db.query(UserAnswer).filter(
|
| 212 |
UserAnswer.attempt_id.in_(leftover_attempt_ids)
|
|
@@ -352,12 +352,13 @@ def save_answers(
|
|
| 352 |
return {"message": "Practice answers are kept in browser until submit"}
|
| 353 |
|
| 354 |
existing = {a.question_id: a for a in attempt.answers}
|
|
|
|
| 355 |
for ans in payload.answers:
|
| 356 |
if ans.question_id in existing:
|
| 357 |
existing[ans.question_id].selected_answer = ans.selected_answer
|
| 358 |
existing[ans.question_id].time_spent_seconds = ans.time_spent_seconds
|
| 359 |
else:
|
| 360 |
-
|
| 361 |
UserAnswer(
|
| 362 |
attempt_id=attempt_id,
|
| 363 |
question_id=ans.question_id,
|
|
@@ -365,6 +366,8 @@ def save_answers(
|
|
| 365 |
time_spent_seconds=ans.time_spent_seconds,
|
| 366 |
)
|
| 367 |
)
|
|
|
|
|
|
|
| 368 |
db.commit()
|
| 369 |
return {"message": "Saved"}
|
| 370 |
|
|
@@ -474,14 +477,19 @@ def _result_payload(
|
|
| 474 |
"percentage": _percentage(topper.score, topper.total_marks),
|
| 475 |
}
|
| 476 |
|
|
|
|
|
|
|
|
|
|
| 477 |
for detail in answer_details:
|
| 478 |
topper_ua = topper_answers_map.get(detail["question_id"])
|
| 479 |
detail["topper_answer"] = topper_ua.selected_answer if topper_ua else None
|
| 480 |
detail["topper_time_seconds"] = topper_ua.time_spent_seconds if topper_ua else 0
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
|
|
|
|
|
|
| 485 |
rank = next(
|
| 486 |
(i + 1 for i, a in enumerate(first_attempts) if a.user_id == current_user.id),
|
| 487 |
None,
|
|
@@ -548,17 +556,17 @@ def submit_test(
|
|
| 548 |
submitted_at = datetime.now(timezone.utc)
|
| 549 |
|
| 550 |
if is_first:
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
time_spent_seconds=detail["time_spent_seconds"],
|
| 560 |
-
)
|
| 561 |
)
|
|
|
|
|
|
|
| 562 |
|
| 563 |
attempt.status = TestStatus.submitted
|
| 564 |
attempt.submitted_at = submitted_at
|
|
|
|
| 206 |
TestAttempt.test_id == test_id,
|
| 207 |
TestAttempt.status == TestStatus.in_progress,
|
| 208 |
)
|
| 209 |
+
.scalar_subquery()
|
| 210 |
)
|
| 211 |
db.query(UserAnswer).filter(
|
| 212 |
UserAnswer.attempt_id.in_(leftover_attempt_ids)
|
|
|
|
| 352 |
return {"message": "Practice answers are kept in browser until submit"}
|
| 353 |
|
| 354 |
existing = {a.question_id: a for a in attempt.answers}
|
| 355 |
+
new_answers = []
|
| 356 |
for ans in payload.answers:
|
| 357 |
if ans.question_id in existing:
|
| 358 |
existing[ans.question_id].selected_answer = ans.selected_answer
|
| 359 |
existing[ans.question_id].time_spent_seconds = ans.time_spent_seconds
|
| 360 |
else:
|
| 361 |
+
new_answers.append(
|
| 362 |
UserAnswer(
|
| 363 |
attempt_id=attempt_id,
|
| 364 |
question_id=ans.question_id,
|
|
|
|
| 366 |
time_spent_seconds=ans.time_spent_seconds,
|
| 367 |
)
|
| 368 |
)
|
| 369 |
+
if new_answers:
|
| 370 |
+
db.add_all(new_answers)
|
| 371 |
db.commit()
|
| 372 |
return {"message": "Saved"}
|
| 373 |
|
|
|
|
| 477 |
"percentage": _percentage(topper.score, topper.total_marks),
|
| 478 |
}
|
| 479 |
|
| 480 |
+
correct = 0
|
| 481 |
+
incorrect = 0
|
| 482 |
+
skipped = 0
|
| 483 |
for detail in answer_details:
|
| 484 |
topper_ua = topper_answers_map.get(detail["question_id"])
|
| 485 |
detail["topper_answer"] = topper_ua.selected_answer if topper_ua else None
|
| 486 |
detail["topper_time_seconds"] = topper_ua.time_spent_seconds if topper_ua else 0
|
| 487 |
+
if detail["is_correct"] is True:
|
| 488 |
+
correct += 1
|
| 489 |
+
elif detail["is_correct"] is False:
|
| 490 |
+
incorrect += 1
|
| 491 |
+
else:
|
| 492 |
+
skipped += 1
|
| 493 |
rank = next(
|
| 494 |
(i + 1 for i, a in enumerate(first_attempts) if a.user_id == current_user.id),
|
| 495 |
None,
|
|
|
|
| 556 |
submitted_at = datetime.now(timezone.utc)
|
| 557 |
|
| 558 |
if is_first:
|
| 559 |
+
db.add_all(
|
| 560 |
+
UserAnswer(
|
| 561 |
+
attempt_id=attempt_id,
|
| 562 |
+
question_id=detail["question_id"],
|
| 563 |
+
selected_answer=detail["selected_answer"],
|
| 564 |
+
is_correct=detail["is_correct"],
|
| 565 |
+
marks_awarded=detail["marks_awarded"],
|
| 566 |
+
time_spent_seconds=detail["time_spent_seconds"],
|
|
|
|
|
|
|
| 567 |
)
|
| 568 |
+
for detail in answer_details
|
| 569 |
+
)
|
| 570 |
|
| 571 |
attempt.status = TestStatus.submitted
|
| 572 |
attempt.submitted_at = submitted_at
|
backend/pytest.ini
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[pytest]
|
| 2 |
+
filterwarnings =
|
| 3 |
+
ignore:Using `httpx` with `starlette.testclient` is deprecated
|
| 4 |
+
ignore:'crypt' is deprecated
|
| 5 |
+
ignore:Setting per-request cookies
|