From 1c06c7850f789c75c67906a8c450fa4995181f6c Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Thu, 21 Sep 2023 12:58:25 -0400 Subject: [PATCH] feat: add submission status interaction with submissions API --- mindmap/apps.py | 11 +++ mindmap/mindmap.py | 149 +++++++++++-------------------- mindmap/public/js/src/mindmap.js | 10 +-- mindmap/receivers.py | 23 +++++ 4 files changed, 87 insertions(+), 106 deletions(-) create mode 100644 mindmap/receivers.py diff --git a/mindmap/apps.py b/mindmap/apps.py index 07a6692..0c0ec00 100644 --- a/mindmap/apps.py +++ b/mindmap/apps.py @@ -23,5 +23,16 @@ class MindMapConfig(AppConfig): "test": {"relative_path": "settings.test"}, "production": {"relative_path": "settings.production"}, }, + }, + "signals_config": { + "lms.djangoapp": { + "relative_path": "receivers", + "receivers": [ + { + "receiver_func_name": "update_student_submission_status", + "signal_path": "submissions.models.score_reset", + }, + ] + }, } } diff --git a/mindmap/mindmap.py b/mindmap/mindmap.py index 6619692..63fea55 100644 --- a/mindmap/mindmap.py +++ b/mindmap/mindmap.py @@ -15,11 +15,6 @@ from xblock.fields import Boolean, DateTime, Dict, Float, Integer, Scope, String from xblockutils.resources import ResourceLoader -try: - from lms.djangoapps.courseware.models import StudentModule -except ImportError: - StudentModule = None - from mindmap.edxapp_wrapper.student import user_by_anonymous_id from mindmap.edxapp_wrapper.xmodule import get_extended_due_date from mindmap.utils import _, utcnow @@ -118,13 +113,6 @@ class MindMapXBlock(XBlock): scope=Scope.settings, ) - submission_status = String( - display_name=_("Submission status"), - help=_("The submission status of the assignment."), - default=SubmissionStatus.NOT_ATTEMPTED.value, - scope=Scope.user_state, - ) - @property def block_id(self): """ @@ -191,17 +179,6 @@ def render_template(self, template_path, context=None) -> str: template_path, context, i18n_service=self.runtime.service(self, 'i18n') ) - def set_submission_status(self) -> None: - """Update the submission status for the current user.""" - student_id = self.get_current_user().opt_attrs.get(ATTR_KEY_ANONYMOUS_USER_ID) - submission = self.get_submission(student_id) - - if submission: - submission_status = submission["answer"].get( - "submission_status", SubmissionStatus.NOT_ATTEMPTED.value - ) - self.submission_status = submission_status - def get_context(self, user): """ Return the context for the student view. @@ -218,6 +195,14 @@ def get_context(self, user): else: editable = in_student_view + submission = self.get_submission() + submission_status = SubmissionStatus.NOT_ATTEMPTED.value + if submission: + submission_status = submission.get("answer", {}).get( + "submission_status", + SubmissionStatus.NOT_ATTEMPTED.value, + ) + return { "display_name": self.display_name, "in_student_view": in_student_view, @@ -228,7 +213,7 @@ def get_context(self, user): "can_submit_assignment": self.submit_allowed(), "score": self.score, "max_score": self.max_score(), - "submission_status": self.submission_status, + "submission_status": submission_status, } def get_js_context(self, user, context): @@ -259,7 +244,6 @@ def student_view(self, _context=None) -> Fragment: Returns: Fragment: The fragment to render """ - self.set_submission_status() user = self.get_current_user() context = self.get_context(user) js_context = self.get_js_context(user, context) @@ -421,38 +405,10 @@ def submit_assignment(self, data, _suffix="") -> dict: student_item_dict = self.get_student_item_dict() create_submission(student_item_dict, answer) - self.submission_status = SubmissionStatus.SUBMITTED.value - return { "success": True, } - # def get_or_create_student_module(self, user): - # """ - # Gets or creates a StudentModule for the given user for this block - - # Returns: - # StudentModule: A StudentModule object - # """ - # # pylint: disable=no-member - # student_module, created = StudentModule.objects.get_or_create( - # course_id=self.course_id, - # module_state_key=self.location, - # student=user, - # defaults={ - # "state": "{}", - # "module_type": self.category, - # }, - # ) - # if created: - # log.info( - # "Created student module %s [course: %s] [student: %s]", - # student_module.module_state_key, - # student_module.course_id, - # student_module.student.username, - # ) - # return student_module - @XBlock.json_handler def get_instructor_grading_data(self, _, _suffix="") -> dict: """Return student assignment information for display on the grading screen. @@ -482,11 +438,14 @@ def get_student_data() -> dict: for student in students: submission = self.get_submission(student.student_id) + + if not submission: + continue answer = submission.get("answer", {}) if ( not submission or - answer.get("submission_status") == + answer.get("submission_status", SubmissionStatus.NOT_ATTEMPTED.value) == SubmissionStatus.NOT_ATTEMPTED.value ): continue @@ -514,18 +473,6 @@ def get_student_data() -> dict: "display_name": self.display_name, } - # def get_student_module(self, module_id): - # """ - # Returns a StudentModule that matches the given id - - # Args: - # module_id (int): The module id - - # Returns: - # StudentModule: A StudentModule object - # """ - # return StudentModule.objects.get(pk=module_id) - @XBlock.json_handler def enter_grade(self, data, _suffix="") -> dict: """ @@ -551,27 +498,34 @@ def enter_grade(self, data, _suffix="") -> dict: if score > self.max_score(): raise JsonHandlerError(400, "Score cannot be greater than max score") - # Create a new submission - self.mindmap_student_body = data.get("mind_map") - answer = { - "mindmap_student_body": json.dumps(self.mindmap_student_body), - "submission_status": SubmissionStatus.COMPLETED.value, - } - student_item_dict = self.get_student_item_dict() - submission = create_submission(student_item_dict, answer) - set_score(submission["uuid"], score, self.max_score()) - breakpoint() - - # module = self.get_student_module(data.get("module_id")) - # state = json.loads(module.state) - # state["submission_status"] = SubmissionStatus.COMPLETED.value - # module.state = json.dumps(state) - # module.save() + latest_submission = self.update_student_submission_status( + data.get("student_id"), SubmissionStatus.COMPLETED.value, + ) + set_score(latest_submission["uuid"], score, self.max_score()) return { "success": True, } + def update_student_submission_status(self, student_id, submission_status): + """ + Update the submission status for a student. + + Args: + submission_id (str): The submission id. + student_id (str): The student id. + submission_status (str): The submission status. + """ + from submissions.api import create_submission + student_submission = self.get_submission(student_id) + mindmap_student_body = student_submission.get("mindmap_student_body", {}) + answer = { + "mindmap_student_body": mindmap_student_body, + "submission_status": submission_status, + } + student_item_dict = self.get_student_item_dict(student_id) + return create_submission(student_item_dict, answer) + @XBlock.json_handler def remove_grade(self, data, _suffix="") -> dict: """ @@ -593,22 +547,11 @@ def remove_grade(self, data, _suffix="") -> dict: if not student_id: raise JsonHandlerError(400, "Missing required parameters") - # Create a new submission - self.mindmap_student_body = data.get("mind_map") - answer = { - "mindmap_student_body": json.dumps(self.mindmap_student_body), - "submission_status": SubmissionStatus.SUBMITTED.value, - } - student_item_dict = self.get_student_item_dict() - submission = create_submission(student_item_dict, answer) - - reset_score(submission["uuid"], self.block_course_id, self.block_id) + latest_submission = self.update_student_submission_status( + data.get("student_id"), SubmissionStatus.SUBMITTED.value, + ) - # module = self.get_student_module(data.get("module_id")) - # state = json.loads(module.state) - # state["submission_status"] = SubmissionStatus.SUBMITTED.value - # module.state = json.dumps(state) - # module.save() + reset_score(latest_submission["uuid"], self.block_course_id, self.block_id) return { "success": True, @@ -652,10 +595,20 @@ def submit_allowed(self) -> bool: Returns: bool: True if student is allowed to submit an assignment. """ + student_submission = self.get_submission() + if not student_submission: + return ( + not self.past_due() + and self.score is None + ) + submission_status = student_submission.get("answer", {}).get( + "submission_status", + SubmissionStatus.NOT_ATTEMPTED.value, + ) return ( not self.past_due() and self.score is None - and self.submission_status == SubmissionStatus.NOT_ATTEMPTED.value + and submission_status == SubmissionStatus.NOT_ATTEMPTED.value ) def get_score(self, student_id=None) -> int: diff --git a/mindmap/public/js/src/mindmap.js b/mindmap/public/js/src/mindmap.js index e563be1..108211d 100644 --- a/mindmap/public/js/src/mindmap.js +++ b/mindmap/public/js/src/mindmap.js @@ -227,7 +227,7 @@ function MindMapXBlock(runtime, element, context) { e.preventDefault(); const typeAction = $(this).attr("data-type"); const grade = $("#grade_value").val(); - const { submission_id, student_id, module_id } = submissionData; + const { student_id } = submissionData; const invalidGradeMessage = gettext("Invalid grade must be a number"); const maxGradeMessage = gettext("Please enter a lower grade, maximum grade allowed is:"); const gradeParsed = parseInt(grade, 10); @@ -253,8 +253,7 @@ function MindMapXBlock(runtime, element, context) { apiUrl = enterGradeURL; data = { grade: grade, - submission_id: submission_id, - module_id: module_id, + student_id: student_id, }; } @@ -262,7 +261,6 @@ function MindMapXBlock(runtime, element, context) { apiUrl = removeGradeURL; data = { student_id: student_id, - module_id: module_id, }; } @@ -298,11 +296,9 @@ function MindMapXBlock(runtime, element, context) { .click(function () { const grade = $(element).find(`#grade-input_${context.xblock_id}`).val(); const submission_id = $(element).find(`#submission-id-input_${context.xblock_id}`).val(); - const module_id = $(element).find(`#module-id-input_${context.xblock_id}`).val(); const data = { grade: grade, submission_id: submission_id, - module_id: module_id, }; console.log(data); $.post(enterGradeURL, JSON.stringify(data)) @@ -318,10 +314,8 @@ function MindMapXBlock(runtime, element, context) { .find(`#remove-grade-button_${context.xblock_id}`) .click(function () { const student_id = $(element).find(`#student-id-input_${context.xblock_id}`).val(); - const module_id = $(element).find(`#module-id-input_${context.xblock_id}`).val(); const data = { student_id: student_id, - module_id: module_id, }; console.log(data); $.post(removeGradeURL, JSON.stringify(data)) diff --git a/mindmap/receivers.py b/mindmap/receivers.py new file mode 100644 index 0000000..409b461 --- /dev/null +++ b/mindmap/receivers.py @@ -0,0 +1,23 @@ +from mindmap.mindmap import SubmissionStatus + + +def update_student_submission_status(*args, **kwargs): + """Update the submission status for a student after resetting their score.""" + from submissions.api import get_submissions # pylint: disable=import-outside-toplevel + from submissions.api import create_submission + + student_item_dict = { + "student_id": kwargs.get("anonymous_user_id"), + "item_id": kwargs.get("item_id"), + "item_type": "mindmap", + "course_id": kwargs.get("course_id"), + } + submissions = get_submissions(student_item_dict) + if not submissions: + return + + student_submission = submissions[0] + new_answer = student_submission.get("answer") + new_answer["submission_status"] = SubmissionStatus.NOT_ATTEMPTED.value + + create_submission(student_item_dict, new_answer)