From 59819de66d2323b68af2133828e161dab1a4ea60 Mon Sep 17 00:00:00 2001 From: dzhuang Date: Wed, 24 May 2017 09:37:52 +0800 Subject: [PATCH] if_has_participation_tags_{all,any} --- course/content.py | 6 ++++ course/utils.py | 20 +++++++++++++ course/validation.py | 67 ++++++++++++++++++++++++++++++++++++++++++++ doc/content.rst | 22 +++++++++++---- doc/flow.rst | 30 ++++++++++++++++++++ 5 files changed, 139 insertions(+), 6 deletions(-) diff --git a/course/content.py b/course/content.py index fb3fcb93..d5f7db03 100644 --- a/course/content.py +++ b/course/content.py @@ -78,6 +78,8 @@ class ChunkRulesDesc(Struct): if_before = None # type: Datespec if_after = None # type: Datespec if_in_facility = None # type: Text + if_has_participation_tags_any = None # type: List[Text] + if_has_participation_tags_all = None # type: List[Text] roles = None # type: List[Text] start = None # type: Datespec end = None # type: Datespec @@ -108,6 +110,8 @@ class FlowSessionStartRuleDesc(Struct): if_after = None # type: Date_ish if_before = None # type: Date_ish if_has_role = None # type: list + if_has_participation_tags_any = None # type: List[Text] + if_has_participation_tags_all = None # type: List[Text] if_in_facility = None # type: Text if_has_in_progress_session = None # type: bool if_has_session_tagged = None # type: Optional[Text] @@ -127,6 +131,8 @@ class FlowSessionAccessRuleDesc(Struct): if_before = None # type: Date_ish if_started_before = None # type: Date_ish if_has_role = None # type: List[Text] + if_has_participation_tags_any = None # type: List[Text] + if_has_participation_tags_all = None # type: List[Text] if_in_facility = None # type: Text if_has_tag = None # type: Optional[Text] if_in_progress = None # type: bool diff --git a/course/utils.py b/course/utils.py index 122af4fd..efb26c95 100644 --- a/course/utils.py +++ b/course/utils.py @@ -172,6 +172,26 @@ def _eval_generic_conditions( if all(role not in rule.if_has_role for role in roles): return False + participation_tags_any_set = ( + set(getattr(rule, "if_has_participation_tags_any", []))) + participation_tags_all_set = ( + set(getattr(rule, "if_has_participation_tags_all", []))) + + if participation_tags_any_set or participation_tags_all_set: + if not participation: + return False + ptag_set = set(participation.tags.all().values_list("name", flat=True)) + if not ptag_set: + return False + if (participation_tags_any_set + and + not participation_tags_any_set & ptag_set): + return False + if (participation_tags_all_set + and + not participation_tags_all_set <= ptag_set): + 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: diff --git a/course/validation.py b/course/validation.py index f3b51b1c..6010911d 100644 --- a/course/validation.py +++ b/course/validation.py @@ -115,6 +115,33 @@ def validate_facility(vctx, location, facility): }) +def validate_participationtag(vctx, location, participationtag): + # type: (ValidationContext, Text, Text) -> None + + if vctx.course is not None: + from pytools import memoize_in + + @memoize_in(vctx, "available_participation_tags") + def get_ptag_list(vctx): + # type: (ValidationContext) -> List[str] + from course.models import ParticipationTag + return list( + ParticipationTag.objects.filter(course=vctx.course) + .values_list('name', flat=True)) + + ptag_list = get_ptag_list(vctx) + if participationtag not in ptag_list: + vctx.add_warning( + location, + _( + "Name of participation tag not recognized: '%(ptag_name)s'. " + "Known participation tag names: '%(known_ptag_names)s'") + % { + "ptag_name": participationtag, + "known_ptag_names": ", ".join(ptag_list), + }) + + def validate_struct( vctx, # type: ValidationContext location, # type: Text @@ -285,6 +312,8 @@ def validate_chunk_rule(vctx, location, chunk_rule): ("if_before", datespec_types), ("if_in_facility", str), ("if_has_role", list), + ("if_has_participation_tags_any", list), + ("if_has_participation_tags_all", list), ("start", datespec_types), ("end", datespec_types), @@ -303,6 +332,14 @@ def validate_chunk_rule(vctx, location, chunk_rule): for role in chunk_rule.if_has_role: validate_role(vctx, location, role) + if hasattr(chunk_rule, "if_has_participation_tags_any"): + for ptag in chunk_rule.if_has_participation_tags_any: + validate_participationtag(vctx, location, ptag) + + if hasattr(chunk_rule, "if_has_participation_tags_all"): + for ptag in chunk_rule.if_has_participation_tags_all: + validate_participationtag(vctx, location, ptag) + if hasattr(chunk_rule, "if_in_facility"): validate_facility(vctx, location, chunk_rule.if_in_facility) @@ -523,6 +560,8 @@ def validate_session_start_rule(vctx, location, nrule, tags): ("if_after", datespec_types), ("if_before", datespec_types), ("if_has_role", list), + ("if_has_participation_tags_any", list), + ("if_has_participation_tags_all", list), ("if_in_facility", str), ("if_has_in_progress_session", bool), ("if_has_session_tagged", (six.string_types, type(None))), @@ -548,6 +587,14 @@ def validate_session_start_rule(vctx, location, nrule, tags): "%s, role %d" % (location, j+1), role) + if hasattr(nrule, "if_has_participation_tags_any"): + for ptag in nrule.if_has_participation_tags_any: + validate_participationtag(vctx, location, ptag) + + if hasattr(nrule, "if_has_participation_tags_all"): + for ptag in nrule.if_has_participation_tags_all: + validate_participationtag(vctx, location, ptag) + if hasattr(nrule, "if_in_facility"): validate_facility(vctx, location, nrule.if_in_facility) @@ -608,6 +655,8 @@ def validate_session_access_rule(vctx, location, arule, tags): ("if_before", datespec_types), ("if_started_before", datespec_types), ("if_has_role", list), + ("if_has_participation_tags_any", list), + ("if_has_participation_tags_all", list), ("if_in_facility", str), ("if_has_tag", (six.string_types, type(None))), ("if_in_progress", bool), @@ -633,6 +682,14 @@ def validate_session_access_rule(vctx, location, arule, tags): "%s, role %d" % (location, j+1), role) + if hasattr(arule, "if_has_participation_tags_any"): + for ptag in arule.if_has_participation_tags_any: + validate_participationtag(vctx, location, ptag) + + if hasattr(arule, "if_has_participation_tags_all"): + for ptag in arule.if_has_participation_tags_all: + validate_participationtag(vctx, location, ptag) + if hasattr(arule, "if_in_facility"): validate_facility(vctx, location, arule.if_in_facility) @@ -690,6 +747,8 @@ def validate_session_grading_rule( ], allowed_attrs=[ ("if_has_role", list), + ("if_has_participation_tags_any", list), + ("if_has_participation_tags_all", list), ("if_has_tag", (six.string_types, type(None))), ("if_started_before", datespec_types), ("if_completed_before", datespec_types), @@ -742,6 +801,14 @@ def validate_session_grading_rule( role) has_conditionals = True + if hasattr(grule, "if_has_participation_tags_any"): + for ptag in grule.if_has_participation_tags_any: + validate_participationtag(vctx, location, ptag) + + if hasattr(grule, "if_has_participation_tags_all"): + for ptag in grule.if_has_participation_tags_all: + validate_participationtag(vctx, location, ptag) + if hasattr(grule, "if_has_tag"): if not (grule.if_has_tag is None or grule.if_has_tag in tags): raise ValidationError( diff --git a/doc/content.rst b/doc/content.rst index 8c5ae9d2..610028a3 100644 --- a/doc/content.rst +++ b/doc/content.rst @@ -470,34 +470,44 @@ Here's an example: .. attribute:: weight - (required) An integer indicating how far up the page the block + (Required) An integer indicating how far up the page the block will be shown. Blocks with identical weight retain the order in which they are given in the course information file. .. attribute:: if_after - A :ref:`datespec ` that determines a date/time after which this rule + (Optional) A :ref:`datespec ` that determines a date/time after which this rule applies. .. attribute:: if_before - A :ref:`datespec ` that determines a date/time before which this rule + (Optional) A :ref:`datespec ` that determines a date/time before which this rule applies. .. attribute:: if_has_role - A list of a subset of ``[unenrolled, ta, student, instructor]``. + (Optional) A list of a subset of ``[unenrolled, ta, student, instructor]``. + + .. attribute:: if_has_participation_tags_any + + (Optional) A list of participation tags. Rule applies when the + participation has at least one tag in this list. + + .. attribute:: if_has_participation_tags_all + + (Optional) A list of participation tags. Rule applies if only the + participation's tags include all items in this list. .. attribute:: if_in_facility - Name of a facility known to the RELATE web page. This rule allows + (Optional) Name of a facility known to the RELATE web page. This rule allows (for example) showing chunks based on whether a user is physically located in a computer-based testing center (which RELATE can recognize based on IP ranges). .. attribute:: shown - A boolean (``true`` or ``false``) indicating whether the chunk + (Optional) A boolean (``true`` or ``false``) indicating whether the chunk should be shown. diff --git a/doc/flow.rst b/doc/flow.rst index 5a5b25cd..abfcbd24 100644 --- a/doc/flow.rst +++ b/doc/flow.rst @@ -304,6 +304,16 @@ Rules for starting new sessions (Optional) A list of a subset of ``[unenrolled, ta, student, instructor]``. + .. attribute:: if_has_participation_tags_any + + (Optional) A list of participation tags. Rule applies when the + participation has at least one tag in this list. + + .. attribute:: if_has_participation_tags_all + + (Optional) A list of participation tags. Rule applies if only the + participation's tags include all items in this list. + .. attribute:: if_in_facility (Optional) Name of a facility known to the RELATE web page. This rule allows @@ -389,6 +399,16 @@ Rules about accessing and interacting with a flow (Optional) A list of a subset of ``[unenrolled, ta, student, instructor]``. + .. attribute:: if_has_participation_tags_any + + (Optional) A list of participation tags. Rule applies when the + participation has at least one tag in this list. + + .. attribute:: if_has_participation_tags_all + + (Optional) A list of participation tags. Rule applies if only the + participation's tags include all items in this list. + .. attribute:: if_in_facility (Optional) Name of a facility known to the RELATE web page. This rule allows @@ -465,6 +485,16 @@ Determining how final (overall) grades of flows are computed (Optional) A list of a subset of ``[unenrolled, ta, student, instructor]``. + .. attribute:: if_has_participation_tags_any + + (Optional) A list of participation tags. Rule applies when the + participation has at least one tag in this list. + + .. attribute:: if_has_participation_tags_all + + (Optional) A list of participation tags. Rule applies if only the + participation's tags include all items in this list. + .. attribute:: if_started_before (Optional) A :ref:`datespec `. Rule applies if the session was started before -- GitLab