diff --git a/course/models.py b/course/models.py index 3be2edb8f..ef54d3df3 100644 --- a/course/models.py +++ b/course/models.py @@ -51,7 +51,6 @@ from exercise.exercise_models import LearningObject from threshold.models import CourseModuleRequirement - logger = logging.getLogger('aplus.course') # Read pseudonymization data from file @@ -347,6 +346,37 @@ def is_valid_slug(self, slug_candidate): # pylint: disable=arguments-renamed return not qs.exists() +class SubmissionTag(UrlMixin, ColorTag): + course_instance = models.ForeignKey('CourseInstance', + verbose_name=_('LABEL_COURSE_INSTANCE'), + on_delete=models.CASCADE, + related_name="submissiontags", + ) + + visible_to_students = models.BooleanField( + verbose_name=_('LABEL_VISIBLE_TO_STUDENTS'), + default=False, + ) + + class Meta: + verbose_name = _('MODEL_NAME_SUBMISSION_TAG') + verbose_name_plural = _('MODEL_NAME_SUBMISSION_TAG_PLURAL') + ordering = ['course_instance', 'name'] + + def get_url_kwargs(self): + return dict(tag_id=self.id, **self.course_instance.get_url_kwargs()) # pylint: disable=use-dict-literal + + def is_valid_slug(self, slug_candidate): # pylint: disable=arguments-renamed + assert self.course_instance + if not slug_candidate: + return False + qs = self.__class__.objects.filter( + course_instance=self.course_instance, slug=slug_candidate) + if self.pk is not None: + qs = qs.exclude(pk=self.pk) + return not qs.exists() + + class HardcodedUserTag(UserTag): class Meta: verbose_name = _('MODEL_NAME_HARDCODED_USER_TAG') diff --git a/edit_course/course_forms.py b/edit_course/course_forms.py index a92b13818..ce5a1f2f3 100644 --- a/edit_course/course_forms.py +++ b/edit_course/course_forms.py @@ -12,9 +12,10 @@ from django_colortag.forms import ColorTagForm from aplus.api import api_reverse -from course.models import LearningObjectCategory, CourseModule, CourseInstance, UserTag +from course.models import LearningObjectCategory, CourseModule, CourseInstance, UserTag, SubmissionTag from course.sis import get_sis_configuration, StudentInfoSystem from exercise.models import CourseChapter +from exercise.submission_models import SubmissionTagging from lib.validators import generate_url_key_validator from lib.fields import UsersSearchSelectField from lib.widgets import DateTimeLocalInput @@ -267,6 +268,13 @@ class CloneInstanceForm(forms.Form): required=False, initial=True, ) + submissiontags = forms.BooleanField( + label=_('LABEL_SUBMISSION_TAGS'), + help_text=_('LABEL_SUBMISSION_TAGS_HELPTEXT'), + required=False, + initial=True, + ) + if settings.GITMANAGER_URL: key_year = forms.IntegerField( label=_('LABEL_YEAR'), @@ -400,6 +408,30 @@ def get_base_object(self, course_instance): return obj +class SubmissionTagForm(ColorTagForm): + + class Meta(ColorTagForm.Meta): + model = SubmissionTag + fields = [ + 'name', + 'slug', + 'description', + 'color', + ] + labels = { + 'name': _('LABEL_NAME'), + 'slug': _('LABEL_SLUG'), + 'description': _('LABEL_DESCRIPTION'), + 'color': _('LABEL_COLOR'), + } + + @classmethod + def get_base_object(self, course_instance): + obj = self.Meta.model() + obj.course_instance = course_instance + return obj + + class SelectUsersForm(forms.Form): user = UsersSearchSelectField(queryset=UserProfile.objects.none(), initial_queryset=UserProfile.objects.none()) @@ -412,6 +444,19 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.fields['user'].queryset = course_instance.get_student_profiles() +class SubmissionTaggingForm(forms.ModelForm): + class Meta(ColorTagForm.Meta): + model = SubmissionTagging + fields = [ + 'tag', + 'submission', + ] + labels = { + 'tag': _('LABEL_SUBMISSION_TAG'), + 'submission': _('LABEL_SUBMISSION'), + } + + class GitmanagerForm(forms.Form): key = forms.SlugField( label=_('LABEL_KEY'), diff --git a/edit_course/templates/edit_course/edit_course_base.html b/edit_course/templates/edit_course/edit_course_base.html index a52187088..f075dee7e 100644 --- a/edit_course/templates/edit_course/edit_course_base.html +++ b/edit_course/templates/edit_course/edit_course_base.html @@ -53,6 +53,11 @@ {% translate "TAGS" %} + +
+ + + {% translate "ADD_NEW_SUBMISSION_TAG" %} + +
+{% translate "TAG" %} | +{% translate "SLUG" %} | +{% translate "DESCRIPTION" %} | +{% translate "ACTIONS" %} | +
---|---|---|---|
{{ tag|colortag }} | +{{ tag.slug }} | +{{ tag.description }} | ++ + + {% translate "EDIT" %} + + + + {% translate "REMOVE" %} + + | +
{% translate "NO_SUBMISSION_TAGS" %} | +