diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e75958c..ff5f0b8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,9 @@ jobs: name: Pre-commit runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.3 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.0 unit-tests: name: Test cases diff --git a/.gitignore b/.gitignore index 1ce71e3b..3292e21d 100644 --- a/.gitignore +++ b/.gitignore @@ -160,3 +160,16 @@ static/admin/* zappa_settings.json templates/staging-test.yaml + +*.log.* +deployment/.terraform/* +deployment/.terraform +deployment/.terraform.lock.hcl +deployment/terraform.tfstate.backup +deployment/terraform.tfstate +deployment/terraform.tfstate.d +terraform.tfstate +deployment/.env.staging +deployment/.env.production + +app/scripts/dump diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a13244bc..c8853a26 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,29 +1,37 @@ repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-json - - id: check-yaml - exclude: "templates/*" - - id: check-merge-conflict - - id: check-added-large-files - - repo: https://github.com/aws-cloudformation/cfn-python-lint - rev: v0.58.4 - hooks: - - id: cfn-python-lint - files: templates/.*\.(json|yml|yaml)$ - - repo: https://github.com/psf/black - rev: 22.3.0 - hooks: - - id: black - - repo: https://github.com/pycqa/flake8 - rev: "3.9.0" - hooks: - - id: flake8 - args: - # these are errors that will be ignored by flake8 - # check out their meaning here - # https://flake8.pycqa.org/en/latest/user/error-codes.html - - "--ignore=E501,E203,W503" +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-json + - id: check-yaml + exclude: "templates/*" + - id: check-merge-conflict + - id: check-added-large-files +- repo: https://github.com/psf/black + rev: 23.10.1 + hooks: + - id: black + exclude: ^psycopg2/ +- repo: https://github.com/pycqa/flake8 + rev: '6.1.0' + hooks: + - id: flake8 + exclude: ^psycopg2/ + args: + # these are errors that will be ignored by flake8 + # check out their meaning here + # https://flake8.pycqa.org/en/latest/user/error-codes.html + - "--ignore=E501,E203,W503" + +- repo: https://github.com/terraform-linters/tflint + rev: v0.30.0 + hooks: + - id: tflint + files: \.tf$ +- repo: https://github.com/aws-cloudformation/cfn-python-lint + rev: v0.58.4 + hooks: + - id: cfn-python-lint + files: templates/.*\.(json|yml|yaml)$ diff --git a/app/models.py b/app/models.py index 2445f089..059a290d 100644 --- a/app/models.py +++ b/app/models.py @@ -73,6 +73,29 @@ class Event(BaseModel): updated_at: datetime = Field(default_factory=datetime.utcnow) +class QuestionSetMetric(BaseModel): + name: str + qset_id: str + marks_scored: int + num_answered: int + num_skipped: int + num_correct: int + num_wrong: int + num_partially_correct: int + attempt_rate: float + accuracy_rate: float + + +class SessionMetrics(BaseModel): + qset_metrics: List[QuestionSetMetric] + total_answered: int + total_skipped: int + total_correct: int + total_wrong: int + total_partially_correct: int + total_marks: int + + class QuestionMetadata(BaseModel): grade: Optional[str] subject: Optional[str] @@ -423,6 +446,7 @@ class Session(BaseModel): created_at: datetime = Field(default_factory=datetime.utcnow) events: List[Event] = [] has_quiz_ended: bool = False + metrics: Optional[SessionMetrics] = None # gets updated when quiz ends class Config: allow_population_by_field_name = True @@ -440,6 +464,7 @@ class UpdateSession(BaseModel): """Model for the body of the request that updates a session""" event: EventType + metrics: Optional[SessionMetrics] class Config: schema_extra = {"example": {"event": "start-quiz"}} diff --git a/app/routers/quizzes.py b/app/routers/quizzes.py index 330517f7..5c2161d1 100644 --- a/app/routers/quizzes.py +++ b/app/routers/quizzes.py @@ -197,7 +197,6 @@ async def get_quiz(quiz_id: str): ) for question_set_index, question_set in enumerate(quiz["question_sets"]): - updated_subset_without_details = [] options_count_per_set = options_count_across_sets[question_set_index][ "options_count_per_set" diff --git a/app/routers/sessions.py b/app/routers/sessions.py index 8aba2398..7d98b14a 100644 --- a/app/routers/sessions.py +++ b/app/routers/sessions.py @@ -117,6 +117,7 @@ async def create_session(session: Session): current_session["events"] = last_session.get("events", []) current_session["time_remaining"] = last_session.get("time_remaining", None) current_session["has_quiz_ended"] = last_session.get("has_quiz_ended", False) + current_session["metrics"] = last_session.get("metrics", None) # restore the answers from the last (previous) sessions session_answers_of_the_last_session = last_session["session_answers"] @@ -161,6 +162,8 @@ async def update_session(session_id: str, session_updates: UpdateSession): * resume button is clicked (resume-quiz event) * end button is clicked (end-quiz event) * dummy event logic added for JNV -- will be removed! + + when end-quiz event is sent, session_updates also contains netrics """ new_event = jsonable_encoder(session_updates)["event"] log_message = f"Updating session with id {session_id} and event {new_event}" @@ -192,7 +195,6 @@ async def update_session(session_id: str, session_updates: UpdateSession): else: session_update_query["$set"].update({"events": [new_event_obj]}) else: - if ( new_event == EventType.dummy_event and session["events"][-1]["event_type"] == EventType.dummy_event @@ -274,10 +276,17 @@ async def update_session(session_id: str, session_updates: UpdateSession): # update the document in the sessions collection if new_event == EventType.end_quiz: + session_metrics = jsonable_encoder(session_updates)["metrics"] + if "$set" not in session_update_query: - session_update_query["$set"] = {"has_quiz_ended": True} + session_update_query["$set"] = { + "has_quiz_ended": True, + "metrics": session_metrics, + } else: - session_update_query["$set"].update({"has_quiz_ended": True}) + session_update_query["$set"].update( + {"has_quiz_ended": True, "metrics": session_metrics} + ) update_result = client.quiz.sessions.update_one( {"_id": session_id}, session_update_query diff --git a/app/scripts/add_marking_scheme_to_questions_without_details.py b/app/scripts/add_marking_scheme_to_questions_without_details.py index 7d813370..5a5b3b5f 100644 --- a/app/scripts/add_marking_scheme_to_questions_without_details.py +++ b/app/scripts/add_marking_scheme_to_questions_without_details.py @@ -5,7 +5,6 @@ settings = Settings() if __name__ == "__main__": - if "MONGO_AUTH_CREDENTIALS" not in os.environ: from dotenv import load_dotenv diff --git a/app/scripts/find_and_replace_some_text_in_all_quizzes.py b/app/scripts/find_and_replace_some_text_in_all_quizzes.py index 36e124ae..072f373e 100644 --- a/app/scripts/find_and_replace_some_text_in_all_quizzes.py +++ b/app/scripts/find_and_replace_some_text_in_all_quizzes.py @@ -2,7 +2,6 @@ import os if __name__ == "__main__": - if "MONGO_AUTH_CREDENTIALS" not in os.environ: from dotenv import load_dotenv diff --git a/app/scripts/remove_extra_dummy_events_in_sessions.py b/app/scripts/remove_extra_dummy_events_in_sessions.py index cc5757b5..78e5ed3f 100644 --- a/app/scripts/remove_extra_dummy_events_in_sessions.py +++ b/app/scripts/remove_extra_dummy_events_in_sessions.py @@ -4,7 +4,6 @@ # test session["_id"] in prod: 633646b0639829dad5a896b4 if __name__ == "__main__": - if "MONGO_AUTH_CREDENTIALS" not in os.environ: from dotenv import load_dotenv @@ -16,7 +15,6 @@ for session in session_collection.find( {"events": {"$exists": True}, "$expr": {"$gt": [{"$size": "$events"}, 1000]}} ): - print(session["_id"]) if session["events"] is None: @@ -29,7 +27,6 @@ # Loop through all events in the session for event in session["events"]: - # Check if the event is a dummy event if event["event_type"] == "dummy-event": # Check if we've already seen a dummy event diff --git a/app/scripts/tag_all_questions_with_question_set_ids.py b/app/scripts/tag_all_questions_with_question_set_ids.py index ee96f02a..f24c65f8 100644 --- a/app/scripts/tag_all_questions_with_question_set_ids.py +++ b/app/scripts/tag_all_questions_with_question_set_ids.py @@ -2,7 +2,6 @@ import os if __name__ == "__main__": - if "MONGO_AUTH_CREDENTIALS" not in os.environ: from dotenv import load_dotenv diff --git a/app/scripts/update_specific_user_ids.py b/app/scripts/update_specific_user_ids.py index f4bbc181..d9b87b6f 100644 --- a/app/scripts/update_specific_user_ids.py +++ b/app/scripts/update_specific_user_ids.py @@ -3,7 +3,6 @@ import json if __name__ == "__main__": - if "MONGO_AUTH_CREDENTIALS" not in os.environ: from dotenv import load_dotenv diff --git a/app/tests/test_questions.py b/app/tests/test_questions.py index d5510167..643bb426 100644 --- a/app/tests/test_questions.py +++ b/app/tests/test_questions.py @@ -38,5 +38,5 @@ def test_get_questions_for_multiple_question_sets(self): assert response.status_code == 200 response = response.json() - assert type(response) == list + assert isinstance(response, list) assert len(response) == settings.subset_size