Skip to content

Commit

Permalink
Add submission tags
Browse files Browse the repository at this point in the history
  • Loading branch information
mikaelGusse committed Apr 24, 2024
1 parent ce46d6f commit beae0d7
Show file tree
Hide file tree
Showing 21 changed files with 615 additions and 6 deletions.
56 changes: 56 additions & 0 deletions assets/js/submissions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
function submissions_list() {
var checkedButtons = []; // Array to store data-slug of checked buttons

$('button').on('click', function(event) {
event.preventDefault();
var icon = $(this).find('.glyphicon');

if (icon.hasClass('glyphicon-unchecked')) {
icon.removeClass('glyphicon-unchecked').addClass('glyphicon-check');
$(this).attr("data-status", "checked"); // set data-status to 'checked'
checkedButtons.push($(this).attr("data-slug")); // add data-slug of the button to the array
} else {
icon.removeClass('glyphicon-check').addClass('glyphicon-unchecked');
$(this).attr("data-status", "unchecked"); // set data-status to 'unchecked'
var index = checkedButtons.indexOf($(this).attr("data-slug")); // get the index of the button in the array
if (index !== -1) {
checkedButtons.splice(index, 1); // remove it from the array if it exists
}
}

refresh_filters();
});

var filter_items = function (submissions) {
const filterTags = $.makeArray($('.filter-users button.filter-tag:has(.glyphicon-check)'))
.map(function (elem) {
return $(elem).attr('data-tagslug');
});
const filterStatuses = $.makeArray($('.filter-users button.filter-status:has(.glyphicon-check)'))
.map(function (elem) {
return $(elem).attr('data-status');
});
return submissions.map(function (submission) {
// Set intercetion tags ∩ filters
const intersectTags = submission.tag_slugs.filter(function (tag) {
return filterTags.indexOf(tag) >= 0;
});
return intersectTags.length === filterTags.length
});
};

function refresh_filters() {
$('.filter-submissions table tr').each(function(){
$(this).hide(); // hide all rows by default

$(this).find('tagging.tag').each(function(){
if (checkedButtons.includes($(this).attr("data-tagslug"))) {
$(this).closest('tr').show(); // show the row if it has a checked button
return false; // exit the inner loop
}
});
});
}

refresh_filters();
}
84 changes: 84 additions & 0 deletions course/migrations/0059_submissiontag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Generated by Django 4.2.11 on 2024-04-24 11:06

import colorfield
from django.db import migrations, models
import django.db.models.deletion
import lib.models


class Migration(migrations.Migration):

dependencies = [
("course", "0058_coursemodule_model_answer_and_more"),
]

operations = [
migrations.CreateModel(
name="SubmissionTag",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"name",
models.CharField(help_text="Display name for tag", max_length=20),
),
(
"slug",
models.SlugField(
help_text="Slug key for tag. If left blank, one is created from name",
max_length=20,
),
),
(
"description",
models.CharField(
blank=True,
help_text="Describe the usage or meaning of this tag",
max_length=155,
),
),
(
"color",
colorfield.ColorField(
default="#CD0000",
help_text="Color that is used as background for this tag",
max_length=7,
),
),
(
"visible_to_students",
models.BooleanField(
default=False, verbose_name="LABEL_VISIBLE_TO_STUDENTS"
),
),
(
"filter_status",
models.BooleanField(
default=False, verbose_name="LABEL_FILTER_STATUS"
),
),
(
"course_instance",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="submissiontags",
to="course.courseinstance",
verbose_name="LABEL_COURSE_INSTANCE",
),
),
],
options={
"verbose_name": "MODEL_NAME_SUBMISSION_TAG",
"verbose_name_plural": "MODEL_NAME_SUBMISSION_TAG_PLURAL",
"ordering": ["course_instance", "name"],
},
bases=(lib.models.UrlMixin, models.Model),
),
]
17 changes: 17 additions & 0 deletions course/migrations/0060_remove_submissiontag_filter_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.11 on 2024-04-24 11:13

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("course", "0059_submissiontag"),
]

operations = [
migrations.RemoveField(
model_name="submissiontag",
name="filter_status",
),
]
32 changes: 31 additions & 1 deletion course/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand Down
47 changes: 46 additions & 1 deletion edit_course/course_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'),
Expand Down Expand Up @@ -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())
Expand All @@ -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'),
Expand Down
5 changes: 5 additions & 0 deletions edit_course/templates/edit_course/edit_course_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
<a href="{{ instance|url:'course-tags' }}">
{% translate "TAGS" %}
</a>
</li>
<li role="presentation" class="menu-course-submission-tags">
<a href="{{ instance|url:'course-submission-tags' }}">
{% translate "SUBMISSION_TAGS" %}
</a>
</li>
<li role="presentation" class="menu-batch-assess">
<a href="{{ instance|url:'batch-assess' }}">
Expand Down
27 changes: 27 additions & 0 deletions edit_course/templates/edit_course/submissiontag_add.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% extends "edit_course/edit_course_base.html" %}
{% load i18n %}
{% load bootstrap %}
{% load course %}
{% load editcourse %}

{% block editbreadcrumblist %}
{{ block.super }}
<li><a href="{{ instance|url:'course-submission-tags' }}">{% translate "SUBMISSION_TAGS" %}</a></li>
<li class="active">{% translate "ADD_NEW_SUBMISSION_TAG" %}</li>
{% endblock %}
{% block view_tag %}edit-course,course-submission-tags{% endblock %}

{% block coursecontent %}
<br />
<form method="post" class="well form-horizontal">
{% csrf_token %}
<legend>{% translate "ADD_SUBMISSION_TAG" %}</legend>
{{ form|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-10 col-sm-offset-2">
<button type="submit" class="aplus-button--default aplus-button--md">{% translate "SAVE" %}</button>
<a href="{{ instance|url:'course-submission-tags' }}" class="aplus-button--secondary aplus-button--md" role="button">{% translate "CANCEL" %}</a>
</div>
</div>
</form>
{% endblock %}
36 changes: 36 additions & 0 deletions edit_course/templates/edit_course/submissiontag_delete.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% extends "edit_course/edit_course_base.html" %}
{% load i18n %}
{% load bootstrap %}
{% load course %}
{% load editcourse %}
{% load colortag %}

{% block editbreadcrumblist %}
{{ block.super }}
<li><a href="{{ instance|url:'course-tags' }}">{% translate "TAGS" %}</a></li>
<li class="active">{% translate "REMOVE_SUBMISSION_TAG" %}</li>
{% endblock %}
{% block view_tag %}edit-course,course-submission-tags{% endblock %}

{% block coursecontent %}
<br />
<form method="post" class="well form-horizontal">
{% csrf_token %}
<legend>
{% translate "REMOVE_SUBMISSION_TAG_CONFIRMATION" %}
</legend>

<div class="alert alert-danger">
{% translate "REMOVE_SUBMISSION_TAG_CONFIRMATION_ALERT" %}
{{ object|colortag }}
</div>

<div class="form-group">
<input class="aplus-button--danger aplus-button--md" type="submit" value="{% translate 'REMOVE' %}" />
<a class="aplus-button--secondary aplus-button--md" role="button" href="{{ instance|url:'course-submission-tags' }}">
{% translate "CANCEL" %}
</a>
</div>
</form>

{% endblock %}
27 changes: 27 additions & 0 deletions edit_course/templates/edit_course/submissiontag_edit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% extends "edit_course/edit_course_base.html" %}
{% load i18n %}
{% load bootstrap %}
{% load course %}
{% load editcourse %}

{% block editbreadcrumblist %}
{{ block.super }}
<li><a href="{{ instance|url:'course-submission-tags' }}">{% translate "SUBMISSION_TAGS" %}</a></li>
<li class="active">{% translate "EDIT_SUBMISSION_TAG" %}</li>
{% endblock %}
{% block view_tag %}edit-course,course-submission-tags{% endblock %}

{% block coursecontent %}
<br />
<form method="post" class="well form-horizontal">
{% csrf_token %}
<legend>{% translate "EDIT_SUBMISSION_TAG" %}</legend>
{{ form|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-10 col-sm-offset-2">
<button type="submit" class="aplus-button--default aplus-button--md">{% translate "SAVE" %}</button>
<a href="{{ instance|url:'course-submission-tags' }}" role="button" class="aplus-button--secondary aplus-button--md">{% translate "CANCEL" %}</a>
</div>
</div>
</form>
{% endblock %}
Loading

0 comments on commit beae0d7

Please sign in to comment.