Skip to content

Commit

Permalink
Merge branch 'apluslms:master' into warnings_fix
Browse files Browse the repository at this point in the history
  • Loading branch information
mikaelGusse authored May 23, 2024
2 parents 76eb282 + 917851a commit f1b807b
Show file tree
Hide file tree
Showing 34 changed files with 961 additions and 44 deletions.
2 changes: 1 addition & 1 deletion assets/css/main.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/css/main.css.map

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions assets/sass/protocols/_quiz1.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ $questionnaire-focus-bg: #bce8f1;
margin-bottom: 20px;
}

.question-area > label:first-of-type {
display: inline;
}

.question-description {
margin-bottom: 5px;
}
Expand Down
4 changes: 4 additions & 0 deletions course/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ def __recurse_exercises(
'max_submissions': child.max_submissions,
'hierarchical_name': child.hierarchical_name,
'difficulty': child.difficulty,
'has_submittable_files': child.has_submittable_files,
}
exercises.append(exercise_dictionary)

Expand All @@ -244,6 +245,9 @@ def __module_to_dict(self, module: ModuleContent, **kwargs) -> Dict[str, Any]:
module.opening_time,
module.closing_time,
),
'reading_opening_time': module.reading_opening_time,
'opening_time': module.opening_time,
'closing_time': module.closing_time,
}
module_dictionary['exercises'] = self.__recurse_exercises(module, [])
return module_dictionary
Expand Down
78 changes: 78 additions & 0 deletions course/migrations/0059_submissiontag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Generated by Django 4.2.11 on 2024-04-30 12:49

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"
),
),
(
"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": ["id"],
},
bases=(lib.models.UrlMixin, models.Model),
),
]
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 = ['id']

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
35 changes: 35 additions & 0 deletions course/templates/course/course.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,45 @@
{% block view_tag %}home{% endblock %}
{% block breadcrumb %}{% endblock %}

{% block scripts %}
{{ block.super }}
<script>
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
};

$(function () {
const lastVisitedObject = JSON.parse(localStorage.getItem("lastVisitedReminder"));
if (lastVisitedObject) {
const lastVisited = lastVisitedObject[$("li.menu-home > a").attr("href")];
if (lastVisited) {
const alert = $('#left-off-reminder');
alert.removeClass("hidden");

// Add link child
const link = $("<a></a>");
link.attr("href", escapeHtml(lastVisited.url));
link.text(escapeHtml(lastVisited.title));
alert.append(link);
}
}
});
</script>
{% endblock %}

{% block coursecontent %}

{% include "course/_enroll_form.html" %}

<div id="left-off-reminder" class="alert alert-info hidden">
<h4>{% translate "LEFT_OFF_REMINDER" %}</h4>
</div>

<div class="index">
{{ instance.description|safe }}
{% if instance.index_mode == instance.INDEX_TYPE.LAST %}
Expand Down
11 changes: 11 additions & 0 deletions course/templatetags/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from django import template
from django.conf import settings
from django.urls import resolve
from django.urls.exceptions import Resolver404
from django.utils.safestring import mark_safe
from django.utils.text import format_lazy
from django.utils.translation import get_language, gettext_lazy as _
Expand All @@ -28,6 +30,15 @@ def get_date(cont, key):
cont[key] = data
return data

@register.simple_tag(takes_context=True)
def login_next(context):
request = context["request"]
try:
matched_url_name = resolve(request.path).url_name
next_path = f"?next={request.path}" if matched_url_name != 'logout' else ""
return next_path
except Resolver404:
return ""

@register.simple_tag
def brand_name():
Expand Down
13 changes: 5 additions & 8 deletions e2e_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,20 @@ def django_server():
# pylint: disable-next=consider-using-with
server_process = subprocess.Popen([os.path.join(path, "run_servers.sh")], stdin=subprocess.PIPE)

# Wait 60 seconds for the server to be ready
max_retries = 60
retries = 0
while retries < max_retries:
# Wait 2 minutes for the server to be ready
max_retries = 120
for _ in range(max_retries):
try:
response = requests.get("http://localhost:8000/") # pylint: disable=missing-timeout
if response.status_code == 200:
# Wait a bit more to let the server settle (tests may fail otherwise)
time.sleep(10)
break
except requests.ConnectionError:
pass

retries += 1
time.sleep(1)

# Wait a bit more to let the server settle (tests may fail otherwise)
time.sleep(10)

# Run tests
yield

Expand Down
33 changes: 32 additions & 1 deletion edit_course/course_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
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 lib.validators import generate_url_key_validator
Expand Down Expand Up @@ -267,6 +267,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 +407,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 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_NEW_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-submission-tags' }}">{% translate "SUBMISSION_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 %}
Loading

0 comments on commit f1b807b

Please sign in to comment.