Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

master to live #52

Merged
merged 5 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions django/account/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@
from .models import User, UsersImportSpreadsheet


def delete_users(modeladmin, request, queryset):
def users_active(modeladmin, request, queryset):
"""
Sets all selected items in queryset to published
Sets all selected users in queryset as 'active'
"""
queryset.delete()
queryset.update(is_active=True)


delete_users.short_description = "PERMANENTLY DELETE selected users from database"
users_active.short_description = "Make selected users 'active' (they can login)"


def users_inactive(modeladmin, request, queryset):
"""
Sets all selected users in queryset as 'inactive'
"""
queryset.update(is_active=False)


users_inactive.short_description = "Make selected users 'inactive' (they can not login)"


class UsersImportSpreadsheetAdmin(admin.ModelAdmin):
Expand Down Expand Up @@ -59,12 +69,15 @@ class UserAdmin(DjangoUserAdmin):
'date_joined',
'last_login')
fieldsets = None
actions = (delete_users,)
actions = (users_active, users_inactive)
ordering = ['first_name', 'last_name']

def has_add_permission(self, request, obj=None):
return False

def has_delete_permission(self, request, obj=None):
return False

def has_change_permission(self, request, obj=None):
# Only allow changes to the password
if '/password/' in request.get_full_path():
Expand Down
8 changes: 6 additions & 2 deletions django/core/static/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ select {

.popup {
display: none;
background: rgba(0, 0, 0, 0.8);
background: rgba(0, 0, 0, 0.2);
position: fixed;
top: 0;
left: 0;
Expand Down Expand Up @@ -563,6 +563,10 @@ main {
margin: 2em auto;
}

#exercise-detail-instructions-video {
margin: 2em auto;
}

#exercise-detail-ownership {
font-size: 0.8em;
margin-top: -3em;
Expand Down Expand Up @@ -916,7 +920,7 @@ main {
.exerciseformat-sentencebuilder-undo,
.exerciseformat-sentencebuilder-checkanswer,
.exerciseformat-sentencebuilder-restart {
float: left;
float: right;
clear: both;
font-size: 0.95em;
color: #505050;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.16 on 2024-09-18 09:46

from django.db import migrations
import embed_video.fields


class Migration(migrations.Migration):

dependencies = [
('exercises', '0004_fontsize_exercise_collaborators_and_more'),
]

operations = [
migrations.AddField(
model_name='exercise',
name='instructions_video_url',
field=embed_video.fields.EmbedVideoField(blank=True, help_text='(Optional) Provide the URL of a YouTube or Vimeo video to include the embedded video in the exercise instructions.', null=True),
),
]
4 changes: 3 additions & 1 deletion django/exercises/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.urls import reverse
from account.models import User, UserRole
from datetime import date
from embed_video.fields import EmbedVideoField
import random
import re

Expand Down Expand Up @@ -51,7 +52,7 @@ def make_urls_clickable(text):
Find all urls in text and add suitable html <a> tag to make them 'clickable' on the website
"""
# If a valid string with content
if type(text) == str and text != '':
if type(text) is str and text != '':
# Regex to find all urls in the provided text
urls = re.findall(r'''(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))''', text) # NOQA
# Loop through all urls found in text
Expand Down Expand Up @@ -244,6 +245,7 @@ class Exercise(models.Model):
instructions_image = models.ImageField(upload_to='exercises-exercise-instructions', blank=True, null=True, help_text=OPTIONAL_HELP_TEXT + " Include an image here to illustrate the instructions for the entire exercise. E.g. if all questions relate to this image.")
instructions_image_url = models.URLField(blank=True, null=True, help_text=f'{OPTIONAL_HELP_TEXT} {IMAGE_URL_HELP_TEXT}')
instructions_image_width_percent = models.IntegerField(blank=True, null=True, help_text="Optional. Set the percentage width of the instructions box. Images will fill width of instructions box by default.", verbose_name="Instructions image width (%)")
instructions_video_url = EmbedVideoField(blank=True, null=True, help_text=f'{OPTIONAL_HELP_TEXT} Provide the URL of a YouTube or Vimeo video to include the embedded video in the exercise instructions.')
is_a_formal_assessment = models.BooleanField(default=False, help_text="Marking this as a formal assessment (i.e. a test that counts to the student's grade) will put restrictions on this exercise, like preventing students from being able to check answers and only allowing a single attempt")
is_published = models.BooleanField(default=True, verbose_name="Published")
owned_by = models.ForeignKey(User, related_name="exercise_owned_by", on_delete=models.SET_NULL, blank=True, null=True, help_text="The person who is mainly responsible for managing this exercise")
Expand Down
15 changes: 11 additions & 4 deletions django/exercises/templates/exercises/exercise-detail.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{% load static %}
{% load static embed_video_tags %}

{% block main %}

Expand Down Expand Up @@ -96,6 +96,14 @@
{% if exercise.instructions_image_path %}
<a href="{{ exercise.instructions_image_path }}"><img src="{{ exercise.instructions_image_path }}" alt="exercise instructions image"{% if exercise.instructions_image_width_percent %} style="width: {{ exercise.instructions_image_width_percent }}%;"{% endif %}></a>
{% endif %}
<!-- Video -->
{% if exercise.instructions_video_url %}
<div id="exercise-detail-instructions-video" class="video-container">
{% video exercise.instructions_video_url as instructions_video %}
{% video instructions_video %}
{% endvideo %}
</div>
{% endif %}
</div>
{% if user.is_staff %}
<!-- Show exercise creator & owner info, if user is staff (teacher or admin) -->
Expand Down Expand Up @@ -312,11 +320,12 @@

// Click to prompt confirm submission of exercise attempt
$('#exercise-detail-footer-completed').on('click', function(){
$('.exerciseformat-showanswer, .answer').show();
{% if exercise.exercise_format.is_marked_automatically_by_system %}
// Add score to popup
$('#exercise-attempt-popup-score').text(totalScorePercent);
// Set score val in exercise attempt form
if(totalScorePercent == '0') totalScorePercent=0.1 // 0 is ignored by backend, so change to 0.1 (will be rounded to 0)
if(totalScorePercent == '0') totalScorePercent=0.1; // 0 is ignored by backend, so change to 0.1 (will be rounded to 0)
$('#exercise-attempt-form-score').val(totalScorePercent);
{% endif %}

Expand Down Expand Up @@ -380,8 +389,6 @@
});
// Store the hidden value as a JSON string, ready to pass to back-end
if (attempt_detail.length) $('#exercise-attempt-form-attemptdetail').val(JSON.stringify(attempt_detail))

console.log(attempt_detail)
});

// Calculate how long it takes the user to attempt the exercise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,8 @@
let newSentence = sentenceWords.join(" ");
// Set the new sentence text (remove the last word)
thisSentence.text(newSentence);
// Create the new word button
let newButtonHtml = `<button value="${ sentenceLastWord }" dir="auto">${ sentenceLastWord }</button>`
$(this).parent().find('.exerciseformat-sentencebuilder-words').append(newButtonHtml);
// Show the last word button
$(this).parent().find(`.exerciseformat-sentencebuilder-words button[value="${ sentenceLastWord }"]`).prop('disabled', false);
// Clear results for this answer
$(this).parent().parent().find('.exerciseformat-sentencebuilder-result').html('');
}
Expand Down
9 changes: 7 additions & 2 deletions django/exercises/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class ExerciseCreateView(PermissionRequiredMixin, CreateView):

template_name = 'exercises/exercise-add.html'
model = models.Exercise
fields = ['name', 'language', 'exercise_format', 'exercise_format_reverse_image_match', 'theme', 'difficulty', 'font_size', 'instructions', 'instructions_image', 'instructions_image_url', 'instructions_image_width_percent', 'is_a_formal_assessment', 'is_published']
fields = ['name', 'language', 'exercise_format', 'exercise_format_reverse_image_match', 'theme', 'difficulty', 'font_size', 'instructions', 'instructions_image', 'instructions_image_url', 'instructions_image_width_percent', 'instructions_video_url', 'is_a_formal_assessment', 'is_published']
permission_required = ('exercises.add_exercise')
success_url = reverse_lazy('exercises:list')

Expand All @@ -151,7 +151,12 @@ class ExerciseUpdateView(PermissionRequiredMixin, UpdateView):

template_name = 'exercises/exercise-edit.html'
model = models.Exercise
fields = ['name', 'language', 'exercise_format_reverse_image_match', 'theme', 'difficulty', 'font_size', 'instructions', 'instructions_image', 'instructions_image_url', 'instructions_image_width_percent', 'is_a_formal_assessment', 'owned_by', 'collaborators', 'is_published']
fields = [
'name', 'language', 'exercise_format_reverse_image_match', 'theme', 'difficulty',
'font_size', 'instructions', 'instructions_image', 'instructions_image_url',
'instructions_image_width_percent', 'instructions_video_url', 'is_a_formal_assessment',
'owned_by', 'collaborators', 'is_published'
]
permission_required = ('exercises.change_exercise')

def get_object(self):
Expand Down
10 changes: 5 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
# For Django versioning we use ~= (e.g. Django~=3.2.0) which will keep the minor version up to date (e.g. the latest version prior to 3.3.0)

Django~=4.2.0
psycopg~=3.1.8
flake8~=4.0.1
psycopg~=3.2.2
flake8~=7.1.1
Pillow~=10.4.0
gunicorn~=20.1.0
gunicorn~=23.0.0
django-recaptcha~=3.0.0
django-embed-video~=1.4.0
django-embed-video~=1.4.10
pandas~=2.2.2
openpyxl~=3.1.5
XlsxWriter~=3.2.0
django-cleanup~=6.0.0
django-cleanup~=8.1.0
Loading