refactor: rename descriptor -> block within cms

Co-authored-by: Agrendalath <piotr@surowiec.it>
This commit is contained in:
Pooja Kulkarni
2023-01-03 16:43:19 -05:00
committed by Agrendalath
parent a2f1ad1f11
commit ce13cd540a
18 changed files with 195 additions and 194 deletions

View File

@@ -25,21 +25,21 @@ class CourseGradingModel:
"""
# Within this class, allow access to protected members of client classes.
# This comes up when accessing kvs data and caches during kvs saves and modulestore writes.
def __init__(self, course_descriptor):
def __init__(self, course):
self.graders = [
CourseGradingModel.jsonize_grader(i, grader) for i, grader in enumerate(course_descriptor.raw_grader)
CourseGradingModel.jsonize_grader(i, grader) for i, grader in enumerate(course.raw_grader)
] # weights transformed to ints [0..100]
self.grade_cutoffs = course_descriptor.grade_cutoffs
self.grace_period = CourseGradingModel.convert_set_grace_period(course_descriptor)
self.minimum_grade_credit = course_descriptor.minimum_grade_credit
self.grade_cutoffs = course.grade_cutoffs
self.grace_period = CourseGradingModel.convert_set_grace_period(course)
self.minimum_grade_credit = course.minimum_grade_credit
@classmethod
def fetch(cls, course_key):
"""
Fetch the course grading policy for the given course from persistence and return a CourseGradingModel.
"""
descriptor = modulestore().get_course(course_key)
model = cls(descriptor)
course = modulestore().get_course(course_key)
model = cls(course)
return model
@staticmethod
@@ -48,10 +48,10 @@ class CourseGradingModel:
Fetch the course's nth grader
Returns an empty dict if there's no such grader.
"""
descriptor = modulestore().get_course(course_key)
course = modulestore().get_course(course_key)
index = int(index)
if len(descriptor.raw_grader) > index:
return CourseGradingModel.jsonize_grader(index, descriptor.raw_grader[index])
if len(course.raw_grader) > index:
return CourseGradingModel.jsonize_grader(index, course.raw_grader[index])
# return empty model
else:
@@ -69,27 +69,27 @@ class CourseGradingModel:
Decode the json into CourseGradingModel and save any changes. Returns the modified model.
Probably not the usual path for updates as it's too coarse grained.
"""
descriptor = modulestore().get_course(course_key)
previous_grading_policy_hash = str(hash_grading_policy(descriptor.grading_policy))
course = modulestore().get_course(course_key)
previous_grading_policy_hash = str(hash_grading_policy(course.grading_policy))
graders_parsed = [CourseGradingModel.parse_grader(jsonele) for jsonele in jsondict['graders']]
fire_signal = CourseGradingModel.must_fire_grading_event_and_signal(
course_key,
graders_parsed,
descriptor,
course,
jsondict
)
descriptor.raw_grader = graders_parsed
descriptor.grade_cutoffs = jsondict['grade_cutoffs']
course.raw_grader = graders_parsed
course.grade_cutoffs = jsondict['grade_cutoffs']
modulestore().update_item(descriptor, user.id)
modulestore().update_item(course, user.id)
CourseGradingModel.update_grace_period_from_json(course_key, jsondict['grace_period'], user)
CourseGradingModel.update_minimum_grade_credit_from_json(course_key, jsondict['minimum_grade_credit'], user)
descriptor = modulestore().get_course(course_key)
new_grading_policy_hash = str(hash_grading_policy(descriptor.grading_policy))
course = modulestore().get_course(course_key)
new_grading_policy_hash = str(hash_grading_policy(course.grading_policy))
log.info(
"Updated course grading policy for course %s from %s to %s. fire_signal = %s",
str(course_key),
@@ -153,28 +153,28 @@ class CourseGradingModel:
Create or update the grader of the given type (string key) for the given course. Returns the modified
grader which is a full model on the client but not on the server (just a dict)
"""
descriptor = modulestore().get_course(course_key)
previous_grading_policy_hash = str(hash_grading_policy(descriptor.grading_policy))
course = modulestore().get_course(course_key)
previous_grading_policy_hash = str(hash_grading_policy(course.grading_policy))
# parse removes the id; so, grab it before parse
index = int(grader.get('id', len(descriptor.raw_grader)))
index = int(grader.get('id', len(course.raw_grader)))
grader = CourseGradingModel.parse_grader(grader)
fire_signal = True
if index < len(descriptor.raw_grader):
if index < len(course.raw_grader):
fire_signal = CourseGradingModel.must_fire_grading_event_and_signal_single_grader(
course_key,
grader,
descriptor.raw_grader[index]
course.raw_grader[index]
)
descriptor.raw_grader[index] = grader
course.raw_grader[index] = grader
else:
descriptor.raw_grader.append(grader)
course.raw_grader.append(grader)
modulestore().update_item(descriptor, user.id)
modulestore().update_item(course, user.id)
descriptor = modulestore().get_course(course_key)
new_grading_policy_hash = str(hash_grading_policy(descriptor.grading_policy))
course = modulestore().get_course(course_key)
new_grading_policy_hash = str(hash_grading_policy(course.grading_policy))
log.info(
"Updated grader for course %s. Grading policy has changed from %s to %s. fire_signal = %s",
str(course_key),
@@ -185,7 +185,7 @@ class CourseGradingModel:
if fire_signal:
_grading_event_and_signal(course_key, user.id)
return CourseGradingModel.jsonize_grader(index, descriptor.raw_grader[index])
return CourseGradingModel.jsonize_grader(index, course.raw_grader[index])
@staticmethod
def update_cutoffs_from_json(course_key, cutoffs, user):
@@ -193,10 +193,10 @@ class CourseGradingModel:
Create or update the grade cutoffs for the given course. Returns sent in cutoffs (ie., no extra
db fetch).
"""
descriptor = modulestore().get_course(course_key)
descriptor.grade_cutoffs = cutoffs
course = modulestore().get_course(course_key)
course.grade_cutoffs = cutoffs
modulestore().update_item(descriptor, user.id)
modulestore().update_item(course, user.id)
_grading_event_and_signal(course_key, user.id)
return cutoffs
@@ -207,7 +207,7 @@ class CourseGradingModel:
grace_period entry in an enclosing dict. It is also safe to call this method with a value of
None for graceperiodjson.
"""
descriptor = modulestore().get_course(course_key)
course = modulestore().get_course(course_key)
# Before a graceperiod has ever been created, it will be None (once it has been
# created, it cannot be set back to None).
@@ -216,9 +216,9 @@ class CourseGradingModel:
graceperiodjson = graceperiodjson['grace_period']
grace_timedelta = timedelta(**graceperiodjson)
descriptor.graceperiod = grace_timedelta
course.graceperiod = grace_timedelta
modulestore().update_item(descriptor, user.id)
modulestore().update_item(course, user.id)
@staticmethod
def update_minimum_grade_credit_from_json(course_key, minimum_grade_credit, user):
@@ -230,29 +230,29 @@ class CourseGradingModel:
user(User): The user object
"""
descriptor = modulestore().get_course(course_key)
course = modulestore().get_course(course_key)
# 'minimum_grade_credit' cannot be set to None
if minimum_grade_credit is not None:
minimum_grade_credit = minimum_grade_credit # lint-amnesty, pylint: disable=self-assigning-variable
descriptor.minimum_grade_credit = minimum_grade_credit
modulestore().update_item(descriptor, user.id)
course.minimum_grade_credit = minimum_grade_credit
modulestore().update_item(course, user.id)
@staticmethod
def delete_grader(course_key, index, user):
"""
Delete the grader of the given type from the given course.
"""
descriptor = modulestore().get_course(course_key)
course = modulestore().get_course(course_key)
index = int(index)
if index < len(descriptor.raw_grader):
del descriptor.raw_grader[index]
if index < len(course.raw_grader):
del course.raw_grader[index]
# force propagation to definition
descriptor.raw_grader = descriptor.raw_grader
course.raw_grader = course.raw_grader
modulestore().update_item(descriptor, user.id)
modulestore().update_item(course, user.id)
_grading_event_and_signal(course_key, user.id)
@staticmethod
@@ -260,37 +260,37 @@ class CourseGradingModel:
"""
Delete the course's grace period.
"""
descriptor = modulestore().get_course(course_key)
course = modulestore().get_course(course_key)
del descriptor.graceperiod
del course.graceperiod
modulestore().update_item(descriptor, user.id)
modulestore().update_item(course, user.id)
@staticmethod
def get_section_grader_type(location):
descriptor = modulestore().get_item(location)
block = modulestore().get_item(location)
return {
"graderType": descriptor.format if descriptor.format is not None else 'notgraded',
"graderType": block.format if block.format is not None else 'notgraded',
"location": str(location),
}
@staticmethod
def update_section_grader_type(descriptor, grader_type, user): # lint-amnesty, pylint: disable=missing-function-docstring
def update_section_grader_type(block, grader_type, user): # lint-amnesty, pylint: disable=missing-function-docstring
if grader_type is not None and grader_type != 'notgraded':
descriptor.format = grader_type
descriptor.graded = True
block.format = grader_type
block.graded = True
else:
del descriptor.format
del descriptor.graded
del block.format
del block.graded
modulestore().update_item(descriptor, user.id)
_grading_event_and_signal(descriptor.location.course_key, user.id)
modulestore().update_item(block, user.id)
_grading_event_and_signal(block.location.course_key, user.id)
return {'graderType': grader_type}
@staticmethod
def convert_set_grace_period(descriptor): # lint-amnesty, pylint: disable=missing-function-docstring
def convert_set_grace_period(course): # lint-amnesty, pylint: disable=missing-function-docstring
# 5 hours 59 minutes 59 seconds => converted to iso format
rawgrace = descriptor.graceperiod
rawgrace = course.graceperiod
if rawgrace:
hours_from_days = rawgrace.days * 24
seconds = rawgrace.seconds

View File

@@ -155,14 +155,14 @@ class CourseMetadata:
return exclude_list
@classmethod
def fetch(cls, descriptor, filter_fields=None):
def fetch(cls, block, filter_fields=None):
"""
Fetch the key:value editable course details for the given course from
persistence and return a CourseMetadata model.
"""
result = {}
metadata = cls.fetch_all(descriptor, filter_fields=filter_fields)
exclude_list_of_fields = cls.get_exclude_list_of_fields(descriptor.id)
metadata = cls.fetch_all(block, filter_fields=filter_fields)
exclude_list_of_fields = cls.get_exclude_list_of_fields(block.id)
for key, value in metadata.items():
if key in exclude_list_of_fields:
@@ -171,12 +171,12 @@ class CourseMetadata:
return result
@classmethod
def fetch_all(cls, descriptor, filter_fields=None):
def fetch_all(cls, block, filter_fields=None):
"""
Fetches all key:value pairs from persistence and returns a CourseMetadata model.
"""
result = {}
for field in descriptor.fields.values():
for field in block.fields.values():
if field.scope != Scope.settings:
continue
@@ -189,7 +189,7 @@ class CourseMetadata:
field_help = field_help.format(**help_args)
result[field.name] = {
'value': field.read_json(descriptor),
'value': field.read_json(block),
'display_name': _(field.display_name), # lint-amnesty, pylint: disable=translation-of-non-string
'help': field_help,
'deprecated': field.runtime_options.get('deprecated', False),
@@ -198,13 +198,13 @@ class CourseMetadata:
return result
@classmethod
def update_from_json(cls, descriptor, jsondict, user, filter_tabs=True):
def update_from_json(cls, block, jsondict, user, filter_tabs=True):
"""
Decode the json into CourseMetadata and save any changed attrs to the db.
Ensures none of the fields are in the exclude list.
"""
exclude_list_of_fields = cls.get_exclude_list_of_fields(descriptor.id)
exclude_list_of_fields = cls.get_exclude_list_of_fields(block.id)
# Don't filter on the tab attribute if filter_tabs is False.
if not filter_tabs:
exclude_list_of_fields.remove("tabs")
@@ -218,16 +218,16 @@ class CourseMetadata:
continue
try:
val = model['value']
if hasattr(descriptor, key) and getattr(descriptor, key) != val:
key_values[key] = descriptor.fields[key].from_json(val)
if hasattr(block, key) and getattr(block, key) != val:
key_values[key] = block.fields[key].from_json(val)
except (TypeError, ValueError) as err:
raise ValueError(_("Incorrect format for field '{name}'. {detailed_message}").format( # lint-amnesty, pylint: disable=raise-missing-from
name=model['display_name'], detailed_message=str(err)))
return cls.update_from_dict(key_values, descriptor, user)
return cls.update_from_dict(key_values, block, user)
@classmethod
def validate_and_update_from_json(cls, descriptor, jsondict, user, filter_tabs=True):
def validate_and_update_from_json(cls, block, jsondict, user, filter_tabs=True):
"""
Validate the values in the json dict (validated by xblock fields from_json method)
@@ -240,7 +240,7 @@ class CourseMetadata:
errors: list of error objects
result: the updated course metadata or None if error
"""
exclude_list_of_fields = cls.get_exclude_list_of_fields(descriptor.id)
exclude_list_of_fields = cls.get_exclude_list_of_fields(block.id)
if not filter_tabs:
exclude_list_of_fields.remove("tabs")
@@ -254,8 +254,8 @@ class CourseMetadata:
for key, model in filtered_dict.items():
try:
val = model['value']
if hasattr(descriptor, key) and getattr(descriptor, key) != val:
key_values[key] = descriptor.fields[key].from_json(val)
if hasattr(block, key) and getattr(block, key) != val:
key_values[key] = block.fields[key].from_json(val)
except (TypeError, ValueError, ValidationError) as err:
did_validate = False
errors.append({'key': key, 'message': str(err), 'model': model})
@@ -264,7 +264,7 @@ class CourseMetadata:
# Because we cannot pass course context to the exception, we need to check if the LTI provider
# should actually be available to the course
err_message = str(err)
if not exams_ida_enabled(descriptor.id):
if not exams_ida_enabled(block.id):
available_providers = get_available_providers()
available_providers.remove('lti_external')
err_message = str(InvalidProctoringProvider(val, available_providers))
@@ -277,29 +277,29 @@ class CourseMetadata:
errors = errors + team_setting_errors
did_validate = False
proctoring_errors = cls.validate_proctoring_settings(descriptor, filtered_dict, user)
proctoring_errors = cls.validate_proctoring_settings(block, filtered_dict, user)
if proctoring_errors:
errors = errors + proctoring_errors
did_validate = False
# If did validate, go ahead and update the metadata
if did_validate:
updated_data = cls.update_from_dict(key_values, descriptor, user, save=False)
updated_data = cls.update_from_dict(key_values, block, user, save=False)
return did_validate, errors, updated_data
@classmethod
def update_from_dict(cls, key_values, descriptor, user, save=True):
def update_from_dict(cls, key_values, block, user, save=True):
"""
Update metadata descriptor from key_values. Saves to modulestore if save is true.
Update metadata from key_values. Saves to modulestore if save is true.
"""
for key, value in key_values.items():
setattr(descriptor, key, value)
setattr(block, key, value)
if save and key_values:
modulestore().update_item(descriptor, user.id)
modulestore().update_item(block, user.id)
return cls.fetch(descriptor)
return cls.fetch(block)
@classmethod
def validate_team_settings(cls, settings_dict):
@@ -397,7 +397,7 @@ class CourseMetadata:
return None
@classmethod
def validate_proctoring_settings(cls, descriptor, settings_dict, user):
def validate_proctoring_settings(cls, block, settings_dict, user):
"""
Verify proctoring settings
@@ -412,9 +412,9 @@ class CourseMetadata:
if (
not user.is_staff and
cls._has_requested_proctoring_provider_changed(
descriptor.proctoring_provider, proctoring_provider_model.get('value')
block.proctoring_provider, proctoring_provider_model.get('value')
) and
datetime.now(pytz.UTC) > descriptor.start
datetime.now(pytz.UTC) > block.start
):
message = (
'The proctoring provider cannot be modified after a course has started.'
@@ -426,7 +426,7 @@ class CourseMetadata:
# should only be allowed if the exams IDA is enabled for a course
available_providers = get_available_providers()
updated_provider = settings_dict.get('proctoring_provider', {}).get('value')
if updated_provider == 'lti_external' and not exams_ida_enabled(descriptor.id):
if updated_provider == 'lti_external' and not exams_ida_enabled(block.id):
available_providers.remove('lti_external')
error = InvalidProctoringProvider('lti_external', available_providers)
errors.append({'key': 'proctoring_provider', 'message': str(error), 'model': proctoring_provider_model})
@@ -435,7 +435,7 @@ class CourseMetadata:
if enable_proctoring_model:
enable_proctoring = enable_proctoring_model.get('value')
else:
enable_proctoring = descriptor.enable_proctored_exams
enable_proctoring = block.enable_proctored_exams
if enable_proctoring:
# Require a valid escalation email if Proctortrack is chosen as the proctoring provider
@@ -443,12 +443,12 @@ class CourseMetadata:
if escalation_email_model:
escalation_email = escalation_email_model.get('value')
else:
escalation_email = descriptor.proctoring_escalation_email
escalation_email = block.proctoring_escalation_email
if proctoring_provider_model:
proctoring_provider = proctoring_provider_model.get('value')
else:
proctoring_provider = descriptor.proctoring_provider
proctoring_provider = block.proctoring_provider
missing_escalation_email_msg = 'Provider \'{provider}\' requires an exam escalation contact.'
if proctoring_provider_model and proctoring_provider == 'proctortrack':
@@ -477,7 +477,7 @@ class CourseMetadata:
if zendesk_ticket_model:
create_zendesk_tickets = zendesk_ticket_model.get('value')
else:
create_zendesk_tickets = descriptor.create_zendesk_tickets
create_zendesk_tickets = block.create_zendesk_tickets
if (
(proctoring_provider == 'proctortrack' and create_zendesk_tickets)
@@ -489,7 +489,7 @@ class CourseMetadata:
'should be updated for this course.'.format(
ticket_value=create_zendesk_tickets,
provider=proctoring_provider,
course_id=descriptor.id
course_id=block.id
)
)