def validate_entry_from_allowed(self, setting_name, allowed_list, errorstore, missing_ok=True): """ Validate that the entry under the setting setting_name for this object is in the allowed list. If not, put the error in the errorstore. :param setting_name: The name of the setting :param allowed_list: List of allowed entries :param errorstore: ErrorStore object to store errors :param missing_ok: Whether the entry can be missing :return: None """ # Check the entry entry = self.attributes.get(setting_name) if entry is None: if not missing_ok: # pragma: no cover msg = f"The tag {self} does not have the required setting '{setting_name}'." errorstore.add_error( InvalidSetting(self.filenames[-1], msg=msg)) return elif entry not in allowed_list: msg = f"The tag {self} has an invalid setting '{setting_name}={entry}'." errorstore.add_error(InvalidSetting(self.filenames[-1], msg=msg))
def require_positive_attempts(self, errorstore): """ Require that the object's "attempts" attribute is positive. :param errorstore: ErrorStore to report any errors :return: None """ attempts = self.attributes.get("attempts") try: if attempts and int(attempts) < 1: msg = f"The tag {self} should have a positive number of attempts." errorstore.add_error( InvalidSetting(self.filenames[-1], msg=msg)) except ValueError: msg = f"The tag {self} should have a positive number of attempts." errorstore.add_error(InvalidSetting(self.filenames[-1], msg=msg))
def validate(self, course, errorstore): """ Perform validation on this object. :param course: The course object, which may contain settings relevant to the validation of this object :param errorstore: An ErrorStore object to which errors should be reported :return: None """ self.validate_entry_from_allowed("rerandomize", randomize_list, errorstore) self.validate_entry_from_allowed("show_correctness", show_correctness_list, errorstore) self.validate_entry_from_allowed("showanswer", show_answer_list, errorstore) # Clean the start and due dates self.clean_date("start", errorstore) self.clean_date("due", errorstore) # Ensure dates fall in the correct order self.ensure_date_order( course.attributes.get("start"), self.attributes.get("start"), errorstore, same_ok=True, error_msg="start date cannot be before course start date") self.ensure_date_order( self.attributes.get("start"), course.attributes.get("end"), errorstore, error_msg="start date must be before course end date") self.ensure_date_order( course.attributes.get("start"), self.attributes.get("due"), errorstore, error_msg="due date must be after course start date") self.ensure_date_order(self.attributes.get("start"), self.attributes.get("due"), errorstore, error_msg="start date must be before due date") self.ensure_date_order( self.attributes.get("due"), course.attributes.get("end"), errorstore, same_ok=True, error_msg="due date must be before course end date") # If this is a timed exam, make sure it's enabled in the policy if self.is_exam: if not course.attributes.get('enable_timed_exams'): msg = f"The tag {self} is a timed exam, but the course policy does not have 'enable_timed_exams=true'." errorstore.add_error( InvalidSetting(self.filenames[-1], msg=msg))
def validate(self, course, errorstore): """ Perform validation on this object. :param course: The course object, which may contain settings relevant to the validation of this object :param errorstore: An ErrorStore object to which errors should be reported :return: None """ # Require some settings that need no further validation self.require_setting("org", errorstore) self.require_setting("course", errorstore) # Validate settings from the allowed list self.validate_entry_from_allowed("rerandomize", randomize_list, errorstore) self.validate_entry_from_allowed("show_correctness", show_correctness_list, errorstore) self.validate_entry_from_allowed("showanswer", show_answer_list, errorstore) # Handle start/end dates self.clean_date("start", errorstore, required=True) self.clean_date("end", errorstore, required=True) self.ensure_date_order(self.attributes.get("start"), self.attributes.get("end"), errorstore, error_msg="start date must be before end date") # Handle enrollment_start/enrollment_end dates self.clean_date("enrollment_start", errorstore) self.clean_date("enrollment_end", errorstore) # Make sure there's a course image self.require_setting("course_image", errorstore) if self.attributes.get("course_image"): if not check_static_file_exists( self, self.attributes.get("course_image")): errorstore.add_error( MissingFile( self.filenames[-1], edxobj=self, missing_file=self.attributes.get("course_image"))) # Check that the grace period is valid if not validate_graceperiod(self.attributes.get("graceperiod")): errorstore.add_error( InvalidSetting( self.filenames[-1], msg="Unable to recognize graceperiod format in policy.")) # Ensure that the default number of attempts is None or positive self.require_positive_attempts(errorstore)
def require_setting(self, setting_name, errorstore): """ Validate that the setting setting_name has an entry for this object. If not, put the error in the errorstore. :param setting_name: The name of the setting :param errorstore: ErrorStore object to store errors :return: None """ # Check the entry entry = self.attributes.get(setting_name) if entry is None: msg = f"The tag {self} does not have the required setting '{setting_name}'." errorstore.add_error(InvalidSetting(self.filenames[-1], msg=msg))
def validate(self, course, errorstore): """ Perform validation on this object. :param course: The course object, which may contain settings relevant to the validation of this object :param errorstore: An ErrorStore object to which errors should be reported :return: None """ # The data for this Xblock is exported as json in the data field datafields = self.attributes.get('data') if datafields is None: return # Make sure that the field is valid, and save it for future validation try: parsed_data = json.loads(datafields) self.parsed_data = parsed_data if not isinstance(self.parsed_data, dict): msg = f"The tag {self} data JSON is not a dictionary." errorstore.add_error(InvalidSetting(self.filenames[-1], msg=msg)) except json.decoder.JSONDecodeError as err: msg = f"The tag {self} has an error in the data JSON: {err}." errorstore.add_error(InvalidSetting(self.filenames[-1], msg=msg)) return
def convert2date(self, date, errorstore, setting_name): """ Returns a date interpretation of the given date. If there's an issue, store it in the errorstore, using setting_name in the error message. """ if date is None: return None # Make sure it parses properly try: parsed_date = dateutil.parser.parse(date) if parsed_date.tzinfo is None or parsed_date.tzinfo.utcoffset( parsed_date) is None: # Apply a timezone to the date return pytz.utc.localize(parsed_date) else: # Shift to UTC return parsed_date.astimezone(pytz.utc) except (TypeError, ValueError): msg = f"The tag {self} has an invalid date setting for {setting_name}: '{date}'." errorstore.add_error(InvalidSetting(self.filenames[-1], msg=msg)) return None
def validate(self, course, errorstore): """ Perform validation on this object. :param course: The course object, which may contain settings relevant to the validation of this object :param errorstore: An ErrorStore object to which errors should be reported :return: None """ self.validate_entry_from_allowed("rerandomize", randomize_list, errorstore) self.validate_entry_from_allowed("show_correctness", show_correctness_list, errorstore) self.validate_entry_from_allowed("showanswer", show_answer_list, errorstore) # Clean the start and due dates self.clean_date("start", errorstore) self.clean_date("due", errorstore) # Ensure dates fall in the correct order self.ensure_date_order( course.attributes.get("start"), self.attributes.get("start"), errorstore, same_ok=True, error_msg="start date cannot be before course start date") self.ensure_date_order( self.attributes.get("start"), course.attributes.get("end"), errorstore, error_msg="start date must be before course end date") self.ensure_date_order( course.attributes.get("start"), self.attributes.get("due"), errorstore, error_msg="due date must be after course start date") self.ensure_date_order(self.attributes.get("start"), self.attributes.get("due"), errorstore, error_msg="start date must be before due date") self.ensure_date_order( self.attributes.get("due"), course.attributes.get("end"), errorstore, same_ok=True, error_msg="due date must be before course end date") # Ensure that problem weight isn't negative weight = self.attributes.get("weight") if weight and float(weight) < 0: msg = f"The tag {self} has a negative problem weight." errorstore.add_error(InvalidSetting(self.filenames[-1], msg=msg)) # Ensure that number of attempts is None or positive self.require_positive_attempts(errorstore) # Detect scripts self.scripts = self.detect_scripts() # Detect response types self.response_types = self.detect_response_types() # Detect input types self.input_types = self.detect_input_types() # Detect solution self.has_solution = self.detect_solution()