def _eval_generic_conditions( rule, # type: Any course, # type: Course participation, # type: Optional[Participation] now_datetime, # type: datetime.datetime flow_id, # type: Text login_exam_ticket, # type: Optional[ExamTicket] ): # type: (...) -> bool if hasattr(rule, "if_before"): ds = parse_date_spec(course, rule.if_before) if not (now_datetime <= ds): return False if hasattr(rule, "if_after"): ds = parse_date_spec(course, rule.if_after) if not (now_datetime >= ds): return False if hasattr(rule, "if_has_role"): from course.enrollment import get_participation_role_identifiers roles = get_participation_role_identifiers(course, participation) if all(role not in rule.if_has_role for role in roles): return False if (hasattr(rule, "if_signed_in_with_matching_exam_ticket") and rule.if_signed_in_with_matching_exam_ticket): if login_exam_ticket is None: return False if login_exam_ticket.exam.flow_id != flow_id: return False return True
def _eval_generic_conditions(rule, course, role, now_datetime, flow_id, login_exam_ticket): if hasattr(rule, "if_before"): ds = parse_date_spec(course, rule.if_before) if not (now_datetime <= ds): return False if hasattr(rule, "if_after"): ds = parse_date_spec(course, rule.if_after) if not (now_datetime >= ds): return False if hasattr(rule, "if_has_role"): if role not in rule.if_has_role: return False if (hasattr(rule, "if_signed_in_with_matching_exam_ticket") and rule.if_signed_in_with_matching_exam_ticket): if login_exam_ticket is None: return False if login_exam_ticket is None: return False if login_exam_ticket.exam.flow_id != flow_id: return False return True
def get_session_grading_rule(session, role, flow_desc, now_datetime): flow_desc_rules = getattr(flow_desc, "rules", None) from relate.utils import dict_to_struct rules = get_flow_rules( flow_desc, flow_rule_kind.grading, session.participation, session.flow_id, now_datetime, default_rules_desc=[dict_to_struct(dict(generates_grade=False, ))]) for rule in rules: if hasattr(rule, "if_has_role"): if role not in rule.if_has_role: continue if hasattr(rule, "if_has_tag"): if session.access_rules_tag != rule.if_has_tag: continue if hasattr(rule, "if_completed_before"): ds = parse_date_spec(session.course, rule.if_completed_before) if session.in_progress and now_datetime > ds: continue if not session.in_progress and session.completion_time > ds: continue due = parse_date_spec(session.course, getattr(rule, "due", None)) if due is not None: assert due.tzinfo is not None generates_grade = getattr(rule, "generates_grade", True) grade_identifier = None grade_aggregation_strategy = None if flow_desc_rules is not None: grade_identifier = flow_desc_rules.grade_identifier grade_aggregation_strategy = getattr(flow_desc_rules, "grade_aggregation_strategy", None) return FlowSessionGradingRule( grade_identifier=grade_identifier, grade_aggregation_strategy=grade_aggregation_strategy, due=due, generates_grade=generates_grade, description=getattr(rule, "description", None), credit_percent=getattr(rule, "credit_percent", 100), use_last_activity_as_completion_time=getattr( rule, "use_last_activity_as_completion_time", False), ) raise RuntimeError( _("grading rule determination was unable to find " "a grading rule"))
def get_session_grading_rule(session, role, flow_desc, now_datetime): flow_desc_rules = getattr(flow_desc, "rules", None) from relate.utils import dict_to_struct rules = get_flow_rules(flow_desc, flow_rule_kind.grading, session.participation, session.flow_id, now_datetime, default_rules_desc=[ dict_to_struct(dict( generates_grade=False, ))]) for rule in rules: if hasattr(rule, "if_has_role"): if role not in rule.if_has_role: continue if hasattr(rule, "if_has_tag"): if session.access_rules_tag != rule.if_has_tag: continue if hasattr(rule, "if_completed_before"): ds = parse_date_spec(session.course, rule.if_completed_before) if session.in_progress and now_datetime > ds: continue if not session.in_progress and session.completion_time > ds: continue due = parse_date_spec(session.course, getattr(rule, "due", None)) if due is not None: assert due.tzinfo is not None generates_grade = getattr(rule, "generates_grade", True) grade_identifier = None grade_aggregation_strategy = None if flow_desc_rules is not None: grade_identifier = flow_desc_rules.grade_identifier grade_aggregation_strategy = getattr( flow_desc_rules, "grade_aggregation_strategy", None) return FlowSessionGradingRule( grade_identifier=grade_identifier, grade_aggregation_strategy=grade_aggregation_strategy, due=due, generates_grade=generates_grade, description=getattr(rule, "description", None), credit_percent=getattr(rule, "credit_percent", 100), use_last_activity_as_completion_time=getattr( rule, "use_last_activity_as_completion_time", False), ) raise RuntimeError(_("grading rule determination was unable to find " "a grading rule"))
def _eval_generic_conditions(rule, course, role, now_datetime): if hasattr(rule, "if_before"): ds = parse_date_spec(course, rule.if_before) if not (now_datetime <= ds): return False if hasattr(rule, "if_after"): ds = parse_date_spec(course, rule.if_after) if not (now_datetime >= ds): return False if hasattr(rule, "if_has_role"): if role not in rule.if_has_role: return False return True
def _eval_generic_session_conditions(rule, session, role, now_datetime): if hasattr(rule, "if_has_tag"): if session.access_rules_tag != rule.if_has_tag: return False if hasattr(rule, "if_started_before"): ds = parse_date_spec(session.course, rule.if_started_before) if not session.start_time < ds: return False return True
def get_session_grading_rule(session, role, flow_desc, now_datetime): rules = get_flow_rules(flow_desc, flow_rule_kind.grading, session.participation, session.flow_id, now_datetime) if not rules: return FlowSessionGradingRule() for rule in rules: if hasattr(rule, "if_has_role"): if role not in rule.if_has_role: continue if hasattr(rule, "if_has_tag"): if session.access_rules_tag != rule.if_has_tag: continue if hasattr(rule, "if_completed_before"): ds = parse_date_spec(session.course, rule.if_completed_before) if now_datetime > ds: continue if not session.in_progress and session.completion_time > ds: continue due = parse_date_spec(session.course, getattr(rule, "due", None)) if due is not None: assert due.tzinfo is not None return FlowSessionGradingRule( grade_identifier=getattr(rule, "grade_identifier", None), grade_aggregation_strategy=getattr(rule, "grade_aggregation_strategy", None), due=due, description=getattr(rule, "description", None), credit_percent=getattr(rule, "credit_percent", 100)) raise RuntimeError("grading rule determination was unable to find " "a grading rule")
def _eval_generic_session_conditions( rule, # type: Any session, # type: FlowSession now_datetime, # type: datetime.datetime ): # type: (...) -> bool if hasattr(rule, "if_has_tag"): if session.access_rules_tag != rule.if_has_tag: return False if hasattr(rule, "if_started_before"): ds = parse_date_spec(session.course, rule.if_started_before) if not session.start_time < ds: return False return True
def encounter_datespec(self, location, datespec): from course.content import parse_date_spec parse_date_spec(self.course, datespec, vctx=self, location=location)
def get_flow_access_rules(course, participation, flow_id, flow_desc, use_exceptions=True): rules = [] attr_names = [ "id", "roles", "start", "end", "allowed_session_count", "credit_percent", "permissions", "is_exception", "sticky", ] # {{{ scan for exceptions in database if use_exceptions: for exc in ( FlowAccessException.objects .filter( participation=participation, flow_id=flow_id) .order_by("-creation_time")): attrs = { "is_exception": True, "id": "exception", "permissions": [entry.permission for entry in exc.entries.all()], } if exc.expiration is not None: attrs["end"] = exc.expiration attrs["sticky"] = exc.is_sticky if exc.stipulations is not None and isinstance(exc.stipulations, dict): attrs.update(exc.stipulations) rules.append(FlowAccessRule(**attrs)) # }}} if not hasattr(flow_desc, "access_rules"): rules.append( FlowAccessRule(**{ "permissions": [ flow_permission.view, flow_permission.start_no_credit], "is_exception": False, })) else: for rule in flow_desc.access_rules: attrs = dict( (attr_name, getattr(rule, attr_name)) for attr_name in attr_names if hasattr(rule, attr_name)) if "start" in attrs: attrs["start"] = parse_date_spec(course, attrs["start"]) if "end" in attrs: attrs["end"] = parse_date_spec(course, attrs["end"]) rules.append(FlowAccessRule(**attrs)) # {{{ set unavailable attrs to None def add_attrs_with_nones(rule): for attr_name in attr_names: if not hasattr(rule, attr_name): setattr(rule, attr_name, None) for rule in rules: add_attrs_with_nones(rule, ) # }}} return rules
def get_session_grading_rule( session, # type: FlowSession flow_desc, # type: FlowDesc now_datetime # type: datetime.datetime ): # type: (...) -> FlowSessionGradingRule flow_desc_rules = getattr(flow_desc, "rules", None) from relate.utils import dict_to_struct rules = get_flow_rules(flow_desc, flow_rule_kind.grading, session.participation, session.flow_id, now_datetime, default_rules_desc=[ dict_to_struct(dict( generates_grade=False, ))]) from course.enrollment import get_participation_role_identifiers roles = get_participation_role_identifiers(session.course, session.participation) for rule in rules: if hasattr(rule, "if_has_role"): if all(role not in rule.if_has_role for role in roles): continue if not _eval_generic_session_conditions(rule, session, now_datetime): continue if not _eval_participation_tags_conditions(rule, session.participation): continue if hasattr(rule, "if_completed_before"): ds = parse_date_spec(session.course, rule.if_completed_before) use_last_activity_as_completion_time = False if hasattr(rule, "use_last_activity_as_completion_time"): use_last_activity_as_completion_time = \ rule.use_last_activity_as_completion_time if use_last_activity_as_completion_time: last_activity = session.last_activity() if last_activity is not None: completion_time = last_activity else: completion_time = now_datetime else: if session.in_progress: completion_time = now_datetime else: completion_time = session.completion_time if completion_time > ds: continue due = parse_date_spec(session.course, getattr(rule, "due", None)) if due is not None: assert due.tzinfo is not None generates_grade = getattr(rule, "generates_grade", True) grade_identifier = None grade_aggregation_strategy = None if flow_desc_rules is not None: grade_identifier = flow_desc_rules.grade_identifier grade_aggregation_strategy = getattr( flow_desc_rules, "grade_aggregation_strategy", None) bonus_points = getattr_with_fallback((rule, flow_desc), "bonus_points", 0) max_points = getattr_with_fallback((rule, flow_desc), "max_points", None) max_points_enforced_cap = getattr_with_fallback( (rule, flow_desc), "max_points_enforced_cap", None) grade_aggregation_strategy = cast(Text, grade_aggregation_strategy) return FlowSessionGradingRule( grade_identifier=grade_identifier, grade_aggregation_strategy=grade_aggregation_strategy, due=due, generates_grade=generates_grade, description=getattr(rule, "description", None), credit_percent=getattr(rule, "credit_percent", 100), use_last_activity_as_completion_time=getattr( rule, "use_last_activity_as_completion_time", False), bonus_points=bonus_points, max_points=max_points, max_points_enforced_cap=max_points_enforced_cap, ) raise RuntimeError(_("grading rule determination was unable to find " "a grading rule"))
def get_flow_permissions(course, participation, role, flow_id, flow_desc, now_datetime): # {{{ interpret flow rules flow_rule = None if not hasattr(flow_desc, "access_rules"): flow_rule = dict_to_struct( {"permissions": [flow_permission.view, flow_permission.start_no_credit]}) else: for rule in flow_desc.access_rules: if hasattr(rule, "roles"): if role not in rule.roles: continue if hasattr(rule, "start"): start_date = parse_date_spec(course, rule.start) if now_datetime < start_date: continue if hasattr(rule, "end"): end_date = parse_date_spec(course, rule.end) if end_date < now_datetime: continue flow_rule = rule break # }}} # {{{ scan for exceptions in database for exc in ( FlowAccessException.objects .filter(participation=participation, flow_id=flow_id) .order_by("expiration")): if exc.expiration is not None and exc.expiration < now_datetime: continue exc_stipulations = exc.stipulations if not isinstance(exc_stipulations, dict): exc_stipulations = {} stipulations = {} if flow_rule is not None: stipulations.update( (key, val) for key, val in flow_rule.__dict__.iteritems() if not key.startswith("_")) stipulations.update(exc_stipulations) stipulations = dict_to_struct(stipulations) return ( [entry.permission for entry in exc.entries.all()], stipulations ) # }}} if flow_rule is not None: return flow_rule.permissions, flow_rule raise ValueError("Flow access rules of flow '%s' did not resolve " "to access answer for '%s'" % (flow_id, participation))
def view_calendar(pctx): from course.content import markup_to_html, parse_date_spec from course.views import get_now_or_fake_time now = get_now_or_fake_time(pctx.request) if not pctx.has_permission(pperm.view_calendar): raise PermissionDenied(_("may not view calendar")) events_json = [] from course.content import get_raw_yaml_from_repo try: event_descr = get_raw_yaml_from_repo(pctx.repo, pctx.course.events_file, pctx.course_commit_sha) except ObjectDoesNotExist: event_descr = {} event_kinds_desc = event_descr.get("event_kinds", {}) event_info_desc = event_descr.get("events", {}) event_info_list = [] events = sorted( Event.objects .filter( course=pctx.course, shown_in_calendar=True), key=lambda evt: ( -evt.time.year, -evt.time.month, -evt.time.day, evt.time.hour, evt.time.minute, evt.time.second)) for event in events: kind_desc = event_kinds_desc.get(event.kind) human_title = six.text_type(event) event_json = { "id": event.id, "start": event.time.isoformat(), "allDay": event.all_day, } if event.end_time is not None: event_json["end"] = event.end_time.isoformat() if kind_desc is not None: if "color" in kind_desc: event_json["color"] = kind_desc["color"] if "title" in kind_desc: if event.ordinal is not None: human_title = kind_desc["title"].format(nr=event.ordinal) else: human_title = kind_desc["title"] description = None show_description = True event_desc = event_info_desc.get(six.text_type(event)) if event_desc is not None: if "description" in event_desc: description = markup_to_html( pctx.course, pctx.repo, pctx.course_commit_sha, event_desc["description"]) if "title" in event_desc: human_title = event_desc["title"] if "color" in event_desc: event_json["color"] = event_desc["color"] if "show_description_from" in event_desc: ds = parse_date_spec( pctx.course, event_desc["show_description_from"]) if now < ds: show_description = False if "show_description_until" in event_desc: ds = parse_date_spec( pctx.course, event_desc["show_description_until"]) if now > ds: show_description = False event_json["title"] = human_title if show_description and description: event_json["url"] = "#event-%d" % event.id start_time = event.time end_time = event.end_time if event.all_day: start_time = start_time.date() if end_time is not None: local_end_time = as_local_time(end_time) end_midnight = datetime.time(tzinfo=local_end_time.tzinfo) if local_end_time.time() == end_midnight: end_time = (end_time - datetime.timedelta(days=1)).date() else: end_time = end_time.date() event_info_list.append( EventInfo( id=event.id, human_title=human_title, start_time=start_time, end_time=end_time, description=description )) events_json.append(event_json) default_date = now.date() if pctx.course.end_date is not None and default_date > pctx.course.end_date: default_date = pctx.course.end_date from json import dumps return render_course_page(pctx, "course/calendar.html", { "events_json": dumps(events_json), "event_info_list": event_info_list, "default_date": default_date.isoformat(), })
def datespec_callback(location, datespec): try: parse_date_spec(pctx.course, datespec, return_now_on_error=False) except InvalidDatespec as e: invalid_datespecs.setdefault(e.datespec, []).append(location)
def encounter_datespec(self, location, datespec): # type: (Text, Text) -> None from course.content import parse_date_spec parse_date_spec(self.course, datespec, vctx=self, location=location)
def get_session_grading_rule( session, # type: FlowSession flow_desc, # type: FlowDesc now_datetime # type: datetime.datetime ): # type: (...) -> FlowSessionGradingRule flow_desc_rules = getattr(flow_desc, "rules", None) from relate.utils import dict_to_struct rules = get_flow_rules(flow_desc, flow_rule_kind.grading, session.participation, session.flow_id, now_datetime, default_rules_desc=[ dict_to_struct(dict( generates_grade=False, ))]) from course.enrollment import get_participation_role_identifiers roles = get_participation_role_identifiers(session.course, session.participation) for rule in rules: if hasattr(rule, "if_has_role"): if all(role not in rule.if_has_role for role in roles): continue if not _eval_generic_session_conditions(rule, session, now_datetime): continue if not _eval_participation_tags_conditions(rule, session.participation): continue if hasattr(rule, "if_completed_before"): ds = parse_date_spec(session.course, rule.if_completed_before) if session.in_progress and now_datetime > ds: continue if not session.in_progress and session.completion_time > ds: continue due = parse_date_spec(session.course, getattr(rule, "due", None)) if due is not None: assert due.tzinfo is not None generates_grade = getattr(rule, "generates_grade", True) grade_identifier = None grade_aggregation_strategy = None if flow_desc_rules is not None: grade_identifier = flow_desc_rules.grade_identifier grade_aggregation_strategy = getattr( flow_desc_rules, "grade_aggregation_strategy", None) bonus_points = getattr_with_fallback((rule, flow_desc), "bonus_points", 0) max_points = getattr_with_fallback((rule, flow_desc), "max_points", None) max_points_enforced_cap = getattr_with_fallback( (rule, flow_desc), "max_points_enforced_cap", None) return FlowSessionGradingRule( grade_identifier=grade_identifier, grade_aggregation_strategy=grade_aggregation_strategy, due=due, generates_grade=generates_grade, description=getattr(rule, "description", None), credit_percent=getattr(rule, "credit_percent", 100), use_last_activity_as_completion_time=getattr( rule, "use_last_activity_as_completion_time", False), bonus_points=bonus_points, max_points=max_points, max_points_enforced_cap=max_points_enforced_cap, ) raise RuntimeError(_("grading rule determination was unable to find " "a grading rule"))
def view_calendar(pctx): from course.content import markup_to_html, parse_date_spec from course.views import get_now_or_fake_time now = get_now_or_fake_time(pctx.request) if not pctx.has_permission(pperm.view_calendar): raise PermissionDenied(_("may not view calendar")) events_json = [] from course.content import get_raw_yaml_from_repo try: event_descr = get_raw_yaml_from_repo(pctx.repo, pctx.course.events_file, pctx.course_commit_sha) except ObjectDoesNotExist: event_descr = {} event_kinds_desc = event_descr.get("event_kinds", {}) event_info_desc = event_descr.get("events", {}) event_info_list = [] for event in Event.objects.filter(course=pctx.course, shown_in_calendar=True).order_by("-time"): kind_desc = event_kinds_desc.get(event.kind) human_title = six.text_type(event) event_json = {"id": event.id, "start": event.time.isoformat(), "allDay": event.all_day} if event.end_time is not None: event_json["end"] = event.end_time.isoformat() if kind_desc is not None: if "color" in kind_desc: event_json["color"] = kind_desc["color"] if "title" in kind_desc: if event.ordinal is not None: human_title = kind_desc["title"].format(nr=event.ordinal) else: human_title = kind_desc["title"] description = None show_description = True event_desc = event_info_desc.get(six.text_type(event)) if event_desc is not None: if "description" in event_desc: description = markup_to_html(pctx.course, pctx.repo, pctx.course_commit_sha, event_desc["description"]) if "title" in event_desc: human_title = event_desc["title"] if "color" in event_desc: event_json["color"] = event_desc["color"] if "show_description_from" in event_desc: ds = parse_date_spec(pctx.course, event_desc["show_description_from"]) if now < ds: show_description = False if "show_description_until" in event_desc: ds = parse_date_spec(pctx.course, event_desc["show_description_until"]) if now > ds: show_description = False event_json["title"] = human_title if show_description and description: event_json["url"] = "#event-%d" % event.id start_time = event.time end_time = event.end_time if event.all_day: start_time = start_time.date() local_end_time = as_local_time(end_time) end_midnight = datetime.time(tzinfo=local_end_time.tzinfo) if local_end_time.time() == end_midnight: end_time = (end_time - datetime.timedelta(days=1)).date() else: end_time = end_time.date() event_info_list.append( EventInfo( id=event.id, human_title=human_title, start_time=start_time, end_time=end_time, description=description, ) ) events_json.append(event_json) default_date = now.date() if pctx.course.end_date is not None and default_date > pctx.course.end_date: default_date = pctx.course.end_date from json import dumps return render_course_page( pctx, "course/calendar.html", { "events_json": dumps(events_json), "event_info_list": event_info_list, "default_date": default_date.isoformat(), }, )