2121from model_utils .models import TimeStampedModel
2222from opaque_keys .edx .django .models import CourseKeyField , UsageKeyField
2323from opaque_keys .edx .keys import CourseKey , UsageKey
24- from openedx_events .learning .data import CourseData , PersistentCourseGradeData
25- from openedx_events .learning .signals import PERSISTENT_GRADE_SUMMARY_CHANGED
24+ from openedx_events .learning .data import (
25+ CourseData ,
26+ PersistentCourseGradeData ,
27+ PersistentSubsectionGradeData ,
28+ XBlockWithScoringData ,
29+ )
30+ from openedx_events .learning .signals import (
31+ PERSISTENT_GRADE_SUMMARY_CHANGED ,
32+ PERSISTENT_SUBSECTION_GRADE_CHANGED ,
33+ )
2634from simple_history .models import HistoricalRecords
2735
2836from lms .djangoapps .courseware .fields import UnsignedBigIntAutoField
2937from lms .djangoapps .grades import events # pylint: disable=unused-import
38+ from lms .djangoapps .grades .course_data import CourseData as GradesCourseData
3039from lms .djangoapps .grades .signals .signals import (
3140 COURSE_GRADE_PASSED_FIRST_TIME ,
3241 COURSE_GRADE_PASSED_UPDATE_IN_LEARNER_PATHWAY ,
@@ -478,6 +487,7 @@ def update_or_create_grade(cls, **params):
478487 grade .save ()
479488
480489 cls ._emit_grade_calculated_event (grade )
490+ cls ._emit_openedx_persistent_subsection_grade_changed_event (grade )
481491 return grade
482492
483493 @classmethod
@@ -500,6 +510,7 @@ def bulk_create_grades(cls, grade_params_iter, user_id, course_key):
500510 grades = cls .objects .bulk_create (grades )
501511 for grade in grades :
502512 cls ._emit_grade_calculated_event (grade )
513+ cls ._emit_openedx_persistent_subsection_grade_changed_event (grade )
503514 return grades
504515
505516 @classmethod
@@ -529,6 +540,53 @@ def _prepare_params_visible_blocks_id(cls, params):
529540 def _emit_grade_calculated_event (grade ):
530541 events .subsection_grade_calculated (grade )
531542
543+ @staticmethod
544+ def _emit_openedx_persistent_subsection_grade_changed_event (grade ):
545+ """
546+ When called emits an event when a persistent subsection grade is created or updated.
547+ """
548+ # .. event_implemented_name: PERSISTENT_SUBSECTION_GRADE_CHANGED
549+ # .. event_type: org.openedx.learning.course.persistent_subsection_grade.changed.v1
550+ try :
551+ grading_policy_hash = GradesCourseData (user = None , course_key = grade .course_id ).grading_policy_hash
552+ except Exception : # pylint: disable=broad-except
553+ grading_policy_hash = ""
554+ log .debug (
555+ "Unable to compute grading_policy_hash for course %s" ,
556+ grade .course_id ,
557+ exc_info = True ,
558+ )
559+
560+ visible_blocks = [
561+ XBlockWithScoringData (
562+ usage_key = block .locator ,
563+ block_type = block .locator .block_type ,
564+ graded = block .graded ,
565+ raw_possible = block .raw_possible ,
566+ weight = block .weight ,
567+ )
568+ for block in grade .visible_blocks .blocks
569+ ]
570+
571+ PERSISTENT_SUBSECTION_GRADE_CHANGED .send_event (
572+ grade = PersistentSubsectionGradeData (
573+ user_id = grade .user_id ,
574+ course = CourseData (
575+ course_key = grade .course_id ,
576+ ),
577+ subsection_edited_timestamp = grade .subtree_edited_timestamp ,
578+ grading_policy_hash = grading_policy_hash ,
579+ usage_key = grade .usage_key ,
580+ weighted_graded_earned = grade .earned_graded ,
581+ weighted_graded_possible = grade .possible_graded ,
582+ weighted_total_earned = grade .earned_all ,
583+ weighted_total_possible = grade .possible_all ,
584+ first_attempted = grade .first_attempted ,
585+ visible_blocks = visible_blocks ,
586+ visible_blocks_hash = str (grade .visible_blocks_id ),
587+ )
588+ )
589+
532590 @classmethod
533591 def _cache_key (cls , course_id ):
534592 return f"subsection_grades_cache.{ course_id } "
0 commit comments