Skip to content

Commit

Permalink
Merge pull request #52 from bear-rsg/master
Browse files Browse the repository at this point in the history
master to live
  • Loading branch information
mike-allaway authored Sep 25, 2024
2 parents e1e2588 + 29537f7 commit 9dcce1a
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 22 deletions.
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

0 comments on commit 9dcce1a

Please sign in to comment.