diff --git a/.circleci/config.yml b/.circleci/config.yml
index d30eb272..97a7d97b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -5,7 +5,7 @@ orbs:
jobs:
install:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- restore_cache:
@@ -33,7 +33,7 @@ jobs:
- lunes_cms.egg-info
compile-translations:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
@@ -56,7 +56,7 @@ jobs:
- lunes_cms/locale/de/LC_MESSAGES/djangojs.mo
pylint:
docker:
- - image: "cimg/python:3.9"
+ - image: "cimg/python:3.11"
steps:
- checkout
- attach_workspace:
@@ -84,7 +84,7 @@ jobs:
- cc-test-reporter
test:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
environment:
LUNES_CMS_SECRET_KEY: circleci-dummy-key
- image: cimg/postgres:14.1
@@ -137,7 +137,7 @@ jobs:
./cc-test-reporter sum-coverage -o - coverage/codeclimate.*.json | ./cc-test-reporter upload-coverage --debug --input -
black:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
@@ -149,7 +149,7 @@ jobs:
black --check .
check-translations:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
@@ -162,7 +162,7 @@ jobs:
command: ./tools/check_translations.sh
build-documentation:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
@@ -172,7 +172,7 @@ jobs:
command: ./tools/build_documentation.sh
bump-dev-version:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
@@ -217,7 +217,7 @@ jobs:
- lunes_cms/__init__.py
bump-version:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
@@ -267,7 +267,7 @@ jobs:
command: git checkout develop && git merge main --commit --no-edit && git push
build-package:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
@@ -284,7 +284,7 @@ jobs:
- dist
publish-package:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
@@ -293,10 +293,10 @@ jobs:
name: Publish lunes-cms package to (Test-)PyPI
command: |
source .venv/bin/activate
- twine upload --non-interactive ./dist/lunes-cms-*.tar.gz
+ twine upload --non-interactive ./dist/lunes_cms-*.tar.gz
create-release:
docker:
- - image: cimg/python:3.9
+ - image: cimg/python:3.11
steps:
- checkout
- attach_workspace:
diff --git a/.circleci/scripts/create_release.py b/.circleci/scripts/create_release.py
index 5f984e66..4e3be694 100755
--- a/.circleci/scripts/create_release.py
+++ b/.circleci/scripts/create_release.py
@@ -34,9 +34,9 @@ def main():
logo_url = "https://raw.githubusercontent.com/digitalfabrik/lunes-cms/c242f6b723da80e5d9f9ed7ca9c6f961bb095386/.github/logo.png"
compare_url = "https://github.com/digitalfabrik/lunes-cms/compare"
body = (
- f"### ![]({logo_url}) lunes-cms `{args.tag}`"
- f"\n\n### Changelog\n\n{args.changelog}\n\n"
- f"Compare changes: [{args.prev_tag} → {args.tag}]({compare_url}/{args.prev_tag}...{args.tag})"
+ f"### ![]({logo_url}) lunes-cms `{args.tag}`\n\n###"
+ f" Changelog\n\n{args.changelog}\n\nCompare changes: [{args.prev_tag} →"
+ f" {args.tag}]({compare_url}/{args.prev_tag}...{args.tag})"
)
# Create release
diff --git a/.circleci/scripts/get_access_token.py b/.circleci/scripts/get_access_token.py
index e4c5a04b..bf52f217 100755
--- a/.circleci/scripts/get_access_token.py
+++ b/.circleci/scripts/get_access_token.py
@@ -22,7 +22,8 @@ def main():
deliverino_private_key = os.environ["DELIVERINO_PRIVATE_KEY"]
except KeyError as e:
raise RuntimeError(
- "Please make sure this step has access to the 'deliverino' CircleCI context."
+ "Please make sure this step has access to the 'deliverino' CircleCI"
+ " context."
) from e
# Generate payload for the JWT
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index b7f76fff..0f99f3cc 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -7,7 +7,7 @@ version: 2
build:
os: ubuntu-20.04
tools:
- python: "3.9"
+ python: "3.11"
jobs:
pre_build:
- ./tools/build_documentation.sh
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d0a28872..79fa9d2c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
UNRELEASED
----------
+* [ [#512](https://github.com/digitalfabrik/lunes-cms/issues/512) ] Gender neutral language
+* [ [#532](https://github.com/digitalfabrik/lunes-cms/issues/532) ] Fix color of feedback list in dark mode
+* [ [#544](https://github.com/digitalfabrik/lunes-cms/issues/544) ] Refactoring of toggle_plural_field.js
+
2024.6.0
--------
diff --git a/README.md b/README.md
index c90a99c8..e5de84ff 100644
--- a/README.md
+++ b/README.md
@@ -19,12 +19,12 @@ For more information please see our [GoVolunteer ad](https://translate.google.co
Following packages are required before installing the project (install them with your package manager):
-* python3.8 or higher
-* python3-pip
-* python3-venv
-* libpq-dev to compile psycopg2
-* gettext and pcregrep to use the translation features
-* ffmpeg for audio processing
+* `python3.11` or higher
+* `python3-pip`
+* `python3-venv`
+* `libpq-dev` to compile psycopg2
+* `gettext` and `pcregrep` to use the translation features
+* `ffmpeg` for audio processing
E.g. on Debian-based distributions, use:
diff --git a/docs/src/conf.py b/docs/src/conf.py
index 13038acb..35b5e649 100644
--- a/docs/src/conf.py
+++ b/docs/src/conf.py
@@ -38,7 +38,7 @@
#: The project author
author = "Lunes"
#: The full version, including alpha/beta/rc tags
-release = "2024.6.0"
+release = "2024.11.0"
#: GitHub username
github_username = "digitalfabrik"
#: GitHub repository name
diff --git a/docs/src/installation.rst b/docs/src/installation.rst
index 82162f8c..a7f8d4d1 100644
--- a/docs/src/installation.rst
+++ b/docs/src/installation.rst
@@ -13,7 +13,7 @@ Prerequisites
Followings are required before installing the project (install them with your manager):
* `git `_
-* `python3.8 `_ or higher
+* `python3.11 `_ or higher
* `python3-pip `_ (`Debian-based distributions `_, e.g. `Ubuntu `__) / `python-pip `_ (`Arch-based distributions `_)
* `python3-venv `__
* `libpq-dev `__ to compile `psycopg2 `__
diff --git a/lunes_cms/README.md b/lunes_cms/README.md
index 98aa9d2f..f1fb3825 100644
--- a/lunes_cms/README.md
+++ b/lunes_cms/README.md
@@ -19,7 +19,7 @@ This is the content management system for the vocabulary trainer app Lunes, whic
Following packages are required before installing the project (install them with your package manager):
-* python3.8 or greater
+* python3.11 or greater
* python3-pip
* python3-venv
* ffmpeg
diff --git a/lunes_cms/api/exception_handler.py b/lunes_cms/api/exception_handler.py
new file mode 100644
index 00000000..f8326013
--- /dev/null
+++ b/lunes_cms/api/exception_handler.py
@@ -0,0 +1,16 @@
+from rest_framework.views import exception_handler
+from rest_framework import status
+
+
+def custom_exception_handler(exc, context):
+ """
+ Extend the default exception_handler of rest_framework.
+ Purpose: Simplify testing by ensuring that the detail message is the same for all 404 responses.
+ """
+
+ response = exception_handler(exc, context)
+
+ if response.status_code == status.HTTP_404_NOT_FOUND:
+ response.data["detail"] = "Not found."
+
+ return response
diff --git a/lunes_cms/api/utils.py b/lunes_cms/api/utils.py
index e48e1724..c410cf70 100644
--- a/lunes_cms/api/utils.py
+++ b/lunes_cms/api/utils.py
@@ -5,7 +5,7 @@
from django.core.exceptions import PermissionDenied
from django.db.models import Count, Q
from django.http import JsonResponse
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from rest_framework import routers
@@ -181,9 +181,11 @@ def find_duplicates_for_word(request, word):
result = {
"message": _("This word is already registered in the system."),
"word": document_to_string(duplicate) + " (" + duplicate.word_type + ")",
- "definition": _("Definition: ") + duplicate.definition
- if duplicate.definition
- else _("Definition: ") + _("No definition is provided for this word."),
+ "definition": (
+ _("Definition: ") + duplicate.definition
+ if duplicate.definition
+ else _("Definition: ") + _("No definition is provided for this word.")
+ ),
"training_sets": _("Training sets: ") + training_sets_description,
}
diff --git a/lunes_cms/api/v1/serializers/feedback_serializer.py b/lunes_cms/api/v1/serializers/feedback_serializer.py
index a37eaf91..95015f81 100644
--- a/lunes_cms/api/v1/serializers/feedback_serializer.py
+++ b/lunes_cms/api/v1/serializers/feedback_serializer.py
@@ -1,6 +1,6 @@
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from ....cms.models import Feedback
@@ -19,7 +19,8 @@ class FeedbackSerializer(serializers.ModelSerializer):
),
error_messages={
"does_not_exist": _(
- "The content type must be either 'discipline', 'training set' or 'document'."
+ "The content type must be either 'discipline', 'training set' or"
+ " 'document'."
),
},
)
diff --git a/lunes_cms/api/v1/urls.py b/lunes_cms/api/v1/urls.py
index e0450fd3..51570e0f 100644
--- a/lunes_cms/api/v1/urls.py
+++ b/lunes_cms/api/v1/urls.py
@@ -14,7 +14,7 @@
router = OptionalSlashRouter()
router.register(r"disciplines", views.DisciplineViewSet, "disciplines")
router.register(
- "disciplines_by_level/",
+ "disciplines_by_level",
views.DisciplineFilteredViewSet,
"disciplines_overview",
)
@@ -41,7 +41,9 @@
)
router.register(
- r"document_by_id/(?P[0-9]+)", views.DocumentByIdViewSet, "documents"
+ r"document_by_id/(?P[0-9]+)",
+ views.DocumentByIdViewSet,
+ "document_by_id",
)
router.register(r"words", views.WordViewSet, "words")
router.register(r"group_info", views.GroupViewSet, "group_by_id")
diff --git a/lunes_cms/cms/admin.py b/lunes_cms/cms/admin.py
index d7ef6468..ad00be00 100644
--- a/lunes_cms/cms/admin.py
+++ b/lunes_cms/cms/admin.py
@@ -5,7 +5,7 @@
from __future__ import absolute_import, unicode_literals
from django.contrib import admin
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .admins import (
DisciplineAdmin,
diff --git a/lunes_cms/cms/admins/discipline_admin.py b/lunes_cms/cms/admins/discipline_admin.py
index cc2f77ca..d50b4a79 100644
--- a/lunes_cms/cms/admins/discipline_admin.py
+++ b/lunes_cms/cms/admins/discipline_admin.py
@@ -10,7 +10,7 @@
from django.http import HttpResponse
from django.urls import reverse
from django.utils.safestring import mark_safe
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from mptt.admin import DraggableMPTTAdmin
from tablib import Dataset
diff --git a/lunes_cms/cms/admins/document_admin.py b/lunes_cms/cms/admins/document_admin.py
index 35161ae2..54ebab65 100644
--- a/lunes_cms/cms/admins/document_admin.py
+++ b/lunes_cms/cms/admins/document_admin.py
@@ -2,7 +2,7 @@
from django.contrib import admin
from django.db.models import Case, Exists, IntegerField, OuterRef, Value, When
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from ..list_filter import (
ApprovedImageListFilter,
@@ -258,5 +258,5 @@ class Media:
css = {"all": ("css/document_form.css",)}
js = (
"js/image_preview.js",
- "js/toggle_plural_field.js",
+ "js/toggle_document_form_fields.js",
)
diff --git a/lunes_cms/cms/admins/document_resource.py b/lunes_cms/cms/admins/document_resource.py
index a998e3da..847c3128 100644
--- a/lunes_cms/cms/admins/document_resource.py
+++ b/lunes_cms/cms/admins/document_resource.py
@@ -1,4 +1,4 @@
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from import_export import fields, resources
from import_export.admin import ExportActionMixin
@@ -103,4 +103,13 @@ class Meta:
"""
model = Document
- fields = ()
+ fields = (
+ "word",
+ "word_type",
+ "singular_article",
+ "plural_article",
+ "has_audio",
+ "example_sentence",
+ "creation_date",
+ "training_sets",
+ )
diff --git a/lunes_cms/cms/admins/sponsor_admin.py b/lunes_cms/cms/admins/sponsor_admin.py
index 165706c7..078a94da 100644
--- a/lunes_cms/cms/admins/sponsor_admin.py
+++ b/lunes_cms/cms/admins/sponsor_admin.py
@@ -1,5 +1,5 @@
from django.contrib import admin
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from ..utils import get_image_tag
diff --git a/lunes_cms/cms/admins/training_set_admin.py b/lunes_cms/cms/admins/training_set_admin.py
index 485600c1..419a39c8 100644
--- a/lunes_cms/cms/admins/training_set_admin.py
+++ b/lunes_cms/cms/admins/training_set_admin.py
@@ -6,7 +6,7 @@
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import ngettext
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from mptt.admin import DraggableMPTTAdmin
from ..forms import TrainingSetForm
@@ -217,8 +217,10 @@ def make_released(self, request, queryset):
messages.error(
request,
ngettext(
- "The training set {} could not be released because it contains less than {} vocabulary words with confirmed images.",
- "The training sets {} could not be released because they contain less than {} vocabulary words with confirmed images.",
+ "The training set {} could not be released because it contains less"
+ " than {} vocabulary words with confirmed images.",
+ "The training sets {} could not be released because they contain"
+ " less than {} vocabulary words with confirmed images.",
len(invalid_trainingsets),
).format(
iter_to_string(invalid_trainingsets),
diff --git a/lunes_cms/cms/apps.py b/lunes_cms/cms/apps.py
index 2275f3a5..bda3d6b3 100644
--- a/lunes_cms/cms/apps.py
+++ b/lunes_cms/cms/apps.py
@@ -1,5 +1,5 @@
from django.apps import AppConfig
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
class CmsConfig(AppConfig):
diff --git a/lunes_cms/cms/forms.py b/lunes_cms/cms/forms.py
index 11a14091..e6dba5b4 100644
--- a/lunes_cms/cms/forms.py
+++ b/lunes_cms/cms/forms.py
@@ -2,7 +2,7 @@
from django.conf import settings
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.db import models
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .models import Discipline, Document, TrainingSet
from .widgets import ManyToManyOverlay
@@ -52,7 +52,9 @@ class Meta:
widget=ManyToManyOverlay(verbose_name=(_("vocabulary")), is_stacked=False),
label=_("vocabulary"),
help_text=_(
- "Please select some vocabularies for your training set. To see a preview of the corresponding image and audio files, press the alt key while selecting."
+ "Please select some vocabularies for your training set. To see a preview of"
+ " the corresponding image and audio files, press the alt key while"
+ " selecting."
),
)
@@ -74,7 +76,8 @@ def clean(self):
self.add_error(
"released",
_(
- "You can only release a training set that contains at least {} vocabulary words with confirmed images."
+ "You can only release a training set that contains at least {}"
+ " vocabulary words with confirmed images."
).format(settings.TRAININGSET_MIN_DOCS),
)
return cleaned_data
diff --git a/lunes_cms/cms/list_filter.py b/lunes_cms/cms/list_filter.py
index 59ebbd77..ed10bd60 100644
--- a/lunes_cms/cms/list_filter.py
+++ b/lunes_cms/cms/list_filter.py
@@ -1,6 +1,6 @@
from django.contrib import admin
from django.db.models import F
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .models import Discipline, TrainingSet
diff --git a/lunes_cms/cms/migrations/0001_initial.py b/lunes_cms/cms/migrations/0001_initial.py
index 991011df..b414291d 100644
--- a/lunes_cms/cms/migrations/0001_initial.py
+++ b/lunes_cms/cms/migrations/0001_initial.py
@@ -242,7 +242,10 @@ class Migration(migrations.Migration):
"name",
models.CharField(
default=None,
- help_text="A free-form name for the API key. Need not be unique. 50 characters max.",
+ help_text=(
+ "A free-form name for the API key. Need not be unique. 50"
+ " characters max."
+ ),
max_length=50,
),
),
@@ -251,14 +254,19 @@ class Migration(migrations.Migration):
models.BooleanField(
blank=True,
default=False,
- help_text="If the API key is revoked, clients cannot use it anymore. (This cannot be undone.)",
+ help_text=(
+ "If the API key is revoked, clients cannot use it anymore."
+ " (This cannot be undone.)"
+ ),
),
),
(
"expiry_date",
models.DateTimeField(
blank=True,
- help_text="Once API key expires, clients cannot use it anymore.",
+ help_text=(
+ "Once API key expires, clients cannot use it anymore."
+ ),
null=True,
verbose_name="Expires",
),
diff --git a/lunes_cms/cms/migrations/0004_feedback.py b/lunes_cms/cms/migrations/0004_feedback.py
index 68ad9538..c3d10256 100644
--- a/lunes_cms/cms/migrations/0004_feedback.py
+++ b/lunes_cms/cms/migrations/0004_feedback.py
@@ -62,7 +62,10 @@ class Migration(migrations.Migration):
"read_by",
models.ForeignKey(
blank=True,
- help_text="The user who marked this feedback as read. If the feedback is unread, this field is empty.",
+ help_text=(
+ "The user who marked this feedback as read. If the feedback"
+ " is unread, this field is empty."
+ ),
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="feedback",
diff --git a/lunes_cms/cms/migrations/0006_convert_group_api_key.py b/lunes_cms/cms/migrations/0006_convert_group_api_key.py
index 73a9fca9..84c576df 100644
--- a/lunes_cms/cms/migrations/0006_convert_group_api_key.py
+++ b/lunes_cms/cms/migrations/0006_convert_group_api_key.py
@@ -60,7 +60,9 @@ class Migration(migrations.Migration):
name="token",
field=models.CharField(
default=lunes_cms.cms.models.group_api_key.generate_default_token,
- help_text="10-50 characters, only digits and upper case letters allowed.",
+ help_text=(
+ "10-50 characters, only digits and upper case letters allowed."
+ ),
max_length=50,
unique=True,
validators=[
diff --git a/lunes_cms/cms/models/alternative_word.py b/lunes_cms/cms/models/alternative_word.py
index 323ce7c2..155434c6 100644
--- a/lunes_cms/cms/models/alternative_word.py
+++ b/lunes_cms/cms/models/alternative_word.py
@@ -1,5 +1,5 @@
from django.db import models
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .document import Document
from .static import Static
diff --git a/lunes_cms/cms/models/discipline.py b/lunes_cms/cms/models/discipline.py
index 3b8349aa..94b546e2 100644
--- a/lunes_cms/cms/models/discipline.py
+++ b/lunes_cms/cms/models/discipline.py
@@ -2,7 +2,7 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.db.models.deletion import CASCADE
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey
from ..utils import get_child_count, get_image_tag
diff --git a/lunes_cms/cms/models/document.py b/lunes_cms/cms/models/document.py
index ead06a6b..c1f5f5b4 100644
--- a/lunes_cms/cms/models/document.py
+++ b/lunes_cms/cms/models/document.py
@@ -4,7 +4,7 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.core.files import File
from django.db import models
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from pydub import AudioSegment
from ..utils import document_to_string
diff --git a/lunes_cms/cms/models/document_image.py b/lunes_cms/cms/models/document_image.py
index 78414065..7cddcaee 100644
--- a/lunes_cms/cms/models/document_image.py
+++ b/lunes_cms/cms/models/document_image.py
@@ -1,5 +1,5 @@
from django.db import models
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from PIL import Image, ImageFilter
from ..utils import get_image_tag
@@ -44,6 +44,7 @@ def save_original_img(self):
:rtype: None
"""
name_elements = self.image.path.split(".")
+ new_path = ""
for elem in name_elements:
if elem != name_elements[-1]:
new_path = elem + "_"
diff --git a/lunes_cms/cms/models/feedback.py b/lunes_cms/cms/models/feedback.py
index 315b97a5..2c4c3675 100644
--- a/lunes_cms/cms/models/feedback.py
+++ b/lunes_cms/cms/models/feedback.py
@@ -43,7 +43,8 @@ class Feedback(models.Model):
related_name="feedback",
verbose_name=_("marked as read by"),
help_text=_(
- "The user who marked this feedback as read. If the feedback is unread, this field is empty."
+ "The user who marked this feedback as read. If the feedback is unread, this"
+ " field is empty."
),
)
diff --git a/lunes_cms/cms/models/group.py b/lunes_cms/cms/models/group.py
index dee718f6..c161aec3 100644
--- a/lunes_cms/cms/models/group.py
+++ b/lunes_cms/cms/models/group.py
@@ -1,6 +1,6 @@
from django.contrib.auth.models import Group
from django.db import models
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .static import convert_umlaute_images
diff --git a/lunes_cms/cms/models/group_api_key.py b/lunes_cms/cms/models/group_api_key.py
index 7dc5a42a..eb3189db 100644
--- a/lunes_cms/cms/models/group_api_key.py
+++ b/lunes_cms/cms/models/group_api_key.py
@@ -9,7 +9,7 @@
from django.utils.crypto import get_random_string
from django.utils.html import mark_safe
from django.utils.timezone import now
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from qr_code.qrcode.maker import make_qr_code_url_with_args
@@ -99,7 +99,8 @@ def qr_code_link(self, link, title=None):
:rtype: str
"""
return mark_safe(
- f'{link}'
+ f'{link}'
)
def qr_code_download_link(self):
@@ -119,7 +120,8 @@ def qr_code(self):
return (
self.qr_code_link(
mark_safe(
- f''
+ f'"
),
escape(_("Download QR code")),
)
diff --git a/lunes_cms/cms/models/sponsor.py b/lunes_cms/cms/models/sponsor.py
index 75940b5d..6a79ec13 100644
--- a/lunes_cms/cms/models/sponsor.py
+++ b/lunes_cms/cms/models/sponsor.py
@@ -1,5 +1,5 @@
from django.db import models
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from ..validators import validate_multiple_extensions
from .static import upload_sponsor_logos
diff --git a/lunes_cms/cms/models/training_set.py b/lunes_cms/cms/models/training_set.py
index 9f2c5ad1..8bb0a4ca 100644
--- a/lunes_cms/cms/models/training_set.py
+++ b/lunes_cms/cms/models/training_set.py
@@ -4,7 +4,7 @@
from django.db import models
from django.db.models.deletion import CASCADE
from django.utils.html import format_html
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey
from ..utils import get_image_tag
@@ -76,7 +76,8 @@ def save(self, *args, **kwargs):
"""
if self.parent:
msg = _(
- "It is not possible to create child elements for training sets (unlike disciplines)."
+ "It is not possible to create child elements for training sets (unlike"
+ " disciplines)."
)
raise ValidationError(msg)
super().save(*args, **kwargs)
diff --git a/lunes_cms/cms/urls.py b/lunes_cms/cms/urls.py
index 013e6ac8..df38ba47 100644
--- a/lunes_cms/cms/urls.py
+++ b/lunes_cms/cms/urls.py
@@ -7,7 +7,7 @@
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from django.views.i18n import JavaScriptCatalog
#: The url patterns of this module (see :doc:`django:topics/http/urls`)
diff --git a/lunes_cms/cms/utils.py b/lunes_cms/cms/utils.py
index 59700596..0a25357e 100644
--- a/lunes_cms/cms/utils.py
+++ b/lunes_cms/cms/utils.py
@@ -10,7 +10,7 @@
from django.utils.crypto import get_random_string
from django.utils.html import mark_safe
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext as _
def create_resource_path(parent_dir, filename):
@@ -61,7 +61,7 @@ def document_to_string(doc):
:rtype: str
"""
alt_words = [str(elem) for elem in doc.alternatives.all()]
- has_foto = "\U0001F4F7" if doc.document_image.all() else "\U000026A0"
+ has_foto = "\U0001f4f7" if doc.document_image.all() else "\U000026a0"
if len(alt_words) > 0:
alt_words = "(" + ", ".join(alt_words) + ")"
diff --git a/lunes_cms/cms/validators.py b/lunes_cms/cms/validators.py
index d8dd81fc..943d25ed 100644
--- a/lunes_cms/cms/validators.py
+++ b/lunes_cms/cms/validators.py
@@ -1,7 +1,7 @@
import os
from django.core.exceptions import ValidationError
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
def validate_file_extension(value):
diff --git a/lunes_cms/core/settings.py b/lunes_cms/core/settings.py
index 0602a83f..04638912 100644
--- a/lunes_cms/core/settings.py
+++ b/lunes_cms/core/settings.py
@@ -12,12 +12,11 @@
import os
-from distutils.util import strtobool
-
from django.core.exceptions import ImproperlyConfigured
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .logging_formatter import ColorFormatter
+from .utils import strtobool
###################
# CUSTOM SETTINGS #
@@ -137,7 +136,8 @@
}
except KeyError as e:
raise ImproperlyConfigured(
- f"The database backend {os.environ.get('LUNES_CMS_DB_BACKEND')!r} is not supported, must be one of {DATABASE_CHOICES.keys()}."
+ f"The database backend {os.environ.get('LUNES_CMS_DB_BACKEND')!r} is not"
+ f" supported, must be one of {DATABASE_CHOICES.keys()}."
) from e
#: Default primary key field type to use for models that don’t have a field with primary_key=True.
@@ -171,7 +171,9 @@
#: See Password validation for more details. By default, no validation is performed and all passwords are accepted.
AUTH_PASSWORD_VALIDATORS = [
{
- "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
+ "NAME": (
+ "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
+ ),
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
@@ -333,7 +335,10 @@
"style": "{",
},
"email": {
- "format": "Date and time: {asctime}\nSeverity: {levelname}\nLogger: {name}\nMessage: {message}\nFile: {funcName}() in {pathname}:{lineno}",
+ "format": (
+ "Date and time: {asctime}\nSeverity: {levelname}\nLogger:"
+ " {name}\nMessage: {message}\nFile: {funcName}() in {pathname}:{lineno}"
+ ),
"datefmt": "%Y-%m-%d %H:%M:%S",
"style": "{",
},
@@ -416,6 +421,7 @@
"ALLOWED_VERSIONS": ("v1", "v2"),
"DEFAULT_VERSION": "default",
"DEFAULT_API_URL": "http://localhost:8080/api/",
+ "EXCEPTION_HANDLER": "lunes_cms.api.exception_handler.custom_exception_handler",
}
SPECTACULAR_SETTINGS = {
diff --git a/lunes_cms/core/urls.py b/lunes_cms/core/urls.py
index 059783da..787e636d 100644
--- a/lunes_cms/core/urls.py
+++ b/lunes_cms/core/urls.py
@@ -20,12 +20,10 @@
"""
from django.conf import settings
-from django.conf.urls import url
from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static
from django.templatetags.static import static as get_static_url
-from django.urls import include, path, reverse_lazy
-from django.utils.translation import ugettext_lazy as _
+from django.urls import include, path, re_path, reverse_lazy
from django.views.generic.base import RedirectView
#: The url patterns of this module (see :doc:`django:topics/http/urls`)
@@ -37,7 +35,7 @@
),
path("api/", include("lunes_cms.api.urls", namespace="api")),
path("", include("lunes_cms.help.urls")),
- url(r"^i18n/", include("django.conf.urls.i18n")),
+ re_path(r"^i18n/", include("django.conf.urls.i18n")),
path("qr_code/", include("qr_code.urls", namespace="qr_code")),
]
diff --git a/lunes_cms/core/utils.py b/lunes_cms/core/utils.py
new file mode 100644
index 00000000..ac605631
--- /dev/null
+++ b/lunes_cms/core/utils.py
@@ -0,0 +1,13 @@
+def strtobool(val):
+ """Convert a string representation of truth to true (1) or false (0).
+
+ True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
+ are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
+ 'val' is anything else.
+ """
+ val = val.lower()
+ if val in ("y", "yes", "t", "true", "on", "1"):
+ return 1
+ if val in ("n", "no", "f", "false", "off", "0"):
+ return 0
+ raise ValueError(f"invalid truth value {val}")
diff --git a/lunes_cms/locale/de/LC_MESSAGES/django.po b/lunes_cms/locale/de/LC_MESSAGES/django.po
index 75d29862..c201300e 100644
--- a/lunes_cms/locale/de/LC_MESSAGES/django.po
+++ b/lunes_cms/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-19 14:40+0000\n"
+"POT-Creation-Date: 2024-10-27 22:23+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -29,19 +29,19 @@ msgstr "Das word ist zu keinem Modul zugeordnet."
msgid "This word is already registered in the system."
msgstr "Das Wort ist schon im System hinterlegt."
-#: api/utils.py:184 api/utils.py:186
+#: api/utils.py:185 api/utils.py:187
msgid "Definition: "
msgstr "Definition: "
-#: api/utils.py:186
+#: api/utils.py:187
msgid "No definition is provided for this word."
msgstr "Für dieses Wort ist keine Definition hinterlegt."
-#: api/utils.py:187
+#: api/utils.py:189
msgid "Training sets: "
msgstr "Module: "
-#: api/utils.py:191
+#: api/utils.py:193
msgid "This word is not yet registered in the system"
msgstr "Das Wort ist noch nicht im System hinterlegt."
@@ -52,17 +52,17 @@ msgstr ""
"Der Inhaltstyp muss entweder 'discipline', 'trainingset' oder 'document' "
"sein."
-#: api/v1/serializers/feedback_serializer.py:35
+#: api/v1/serializers/feedback_serializer.py:36
msgid "{} with id {} does not exist."
msgstr "{} mit der ID {} existiert nicht."
#: cms/admin.py:35 cms/admins/document_admin.py:162
-#: cms/admins/training_set_admin.py:359 cms/forms.py:46 cms/forms.py:48
+#: cms/admins/training_set_admin.py:361 cms/forms.py:46 cms/forms.py:48
#: cms/list_filter.py:14 cms/models/discipline.py:101
msgid "disciplines"
msgstr "Modulgruppen"
-#: cms/admin.py:36 cms/list_filter.py:103 cms/models/training_set.py:91
+#: cms/admin.py:36 cms/list_filter.py:103 cms/models/training_set.py:92
msgid "training sets"
msgstr "Module"
@@ -75,7 +75,7 @@ msgstr "Vokabeln"
msgid "api keys"
msgstr "API Keys"
-#: cms/admin.py:39 cms/models/feedback.py:89
+#: cms/admin.py:39 cms/models/feedback.py:90
msgid "feedback"
msgstr "Feedback"
@@ -104,12 +104,12 @@ msgid "published words in released modules"
msgstr "veröffentlichte Vokabeln in veröffentlichten Modulen"
#: cms/admins/discipline_admin.py:387 cms/admins/document_admin.py:179
-#: cms/admins/training_set_admin.py:376
+#: cms/admins/training_set_admin.py:378
msgid "creator group"
msgstr "Besitzergruppe"
#: cms/admins/document_admin.py:142 cms/models/training_set.py:25
-#: cms/models/training_set.py:90
+#: cms/models/training_set.py:91
msgid "training set"
msgstr "Modul"
@@ -117,7 +117,7 @@ msgstr "Modul"
msgid "audio"
msgstr "Audio"
-#: cms/admins/document_admin.py:216 cms/models/document_image.py:123
+#: cms/admins/document_admin.py:216 cms/models/document_image.py:124
msgid "image"
msgstr "Bild"
@@ -206,43 +206,43 @@ msgstr[1] ""
"Die Module {} konnten nicht veröffentlicht werden, da sie weniger als {} "
"Vokabeln mit bestätigten Bildern enthalten."
-#: cms/admins/training_set_admin.py:233
+#: cms/admins/training_set_admin.py:235
msgid "The training set {} is already released."
msgid_plural "The training sets {} are already released."
msgstr[0] "Das Modul {} ist bereits veröffentlicht."
msgstr[1] "Die Module {} sind bereits veröffentlicht."
-#: cms/admins/training_set_admin.py:247
+#: cms/admins/training_set_admin.py:249
msgid "The training set {} was successfully released."
msgid_plural "The training sets {} were successfully released."
msgstr[0] "Das Modul {} wurde erfolgreich veröffentlicht."
msgstr[1] "Die Module {} wurden erfolgreich veröffentlicht."
-#: cms/admins/training_set_admin.py:253
+#: cms/admins/training_set_admin.py:255
msgid "Unrelease selected training sets"
msgstr "Ausgewählte Module zurückhalten"
-#: cms/admins/training_set_admin.py:277
+#: cms/admins/training_set_admin.py:279
msgid "The training set {} is already unreleased."
msgid_plural "The training sets {} are already unreleased."
msgstr[0] "Das Modul {} ist bereits zurückgehalten."
msgstr[1] "Die Module {} sind bereits zurückgehalten."
-#: cms/admins/training_set_admin.py:288
+#: cms/admins/training_set_admin.py:290
msgid "The training set {} was successfully unreleased."
msgid_plural "The training sets {} were successfully unreleased."
msgstr[0] "Das Modul {} wurde erfolgreich zurückgehalten."
msgstr[1] "Die Module {} wurden erfolgreich zurückgehalten."
-#: cms/admins/training_set_admin.py:295
+#: cms/admins/training_set_admin.py:297
msgid "words"
msgstr "Vokabeln"
-#: cms/admins/training_set_admin.py:313
+#: cms/admins/training_set_admin.py:315
msgid "published words"
msgstr "veröffentlichte Vokabeln"
-#: cms/admins/training_set_admin.py:331
+#: cms/admins/training_set_admin.py:333
msgid "unpublished words"
msgstr "unveröffentlichte Vokabeln"
@@ -259,7 +259,7 @@ msgstr ""
"Bilder und Audio-Dateien zu sehen, drücke die Alt-Taste, während du ein "
"Element auswählst."
-#: cms/forms.py:77
+#: cms/forms.py:79
msgid ""
"You can only release a training set that contains at least {} vocabulary "
"words with confirmed images."
@@ -395,7 +395,7 @@ msgstr "Zusätzliche Bedeutung 1"
msgid "additional meaning 2"
msgstr "Zusätzliche Bedeutung 2"
-#: cms/models/document_image.py:124
+#: cms/models/document_image.py:125
msgid "images"
msgstr "Bilder"
@@ -415,7 +415,7 @@ msgstr "Objekt-ID"
msgid "The id of the object this feedback entry refers to."
msgstr "Die ID des Objekts, auf das sich dieser Feedback-Eintrag bezieht."
-#: cms/models/feedback.py:32 cms/models/feedback.py:72
+#: cms/models/feedback.py:32 cms/models/feedback.py:73
msgid "refers to"
msgstr "bezieht sich auf"
@@ -432,18 +432,18 @@ msgid ""
"The user who marked this feedback as read. If the feedback is unread, this "
"field is empty."
msgstr ""
-"Der Benutzer, der dieses Feedback als gelesen markiert hat. Wenn das "
+"Die nutzende Person, die dieses Feedback als gelesen markiert hat. Wenn das "
"Feedback ungelesen ist, ist dieses Feld leer."
-#: cms/models/feedback.py:52
+#: cms/models/feedback.py:53
msgid "submitted on"
msgstr "übermittelt am"
-#: cms/models/feedback.py:53
+#: cms/models/feedback.py:54
msgid "The time and date when the feedback was submitted."
msgstr "Uhrzeit und Datum, an dem das Feedback eingereicht wurde."
-#: cms/models/feedback.py:91
+#: cms/models/feedback.py:92
msgid "feedback entries"
msgstr "Feedback-Einträge"
@@ -487,27 +487,27 @@ msgstr ""
msgid "valid"
msgstr "gültig"
-#: cms/models/group_api_key.py:110
+#: cms/models/group_api_key.py:111
msgid "Download"
msgstr "Herunterladen"
-#: cms/models/group_api_key.py:112 cms/models/group_api_key.py:130
+#: cms/models/group_api_key.py:113 cms/models/group_api_key.py:132
msgid "QR code"
msgstr "QR-Code"
-#: cms/models/group_api_key.py:124
+#: cms/models/group_api_key.py:126
msgid "Download QR code"
msgstr "QR-Code herunterladen"
-#: cms/models/group_api_key.py:156
+#: cms/models/group_api_key.py:158
msgid "{}: Token for the group \"{}\""
msgstr "{}: Token für die Gruppe \"{}\""
-#: cms/models/group_api_key.py:163
+#: cms/models/group_api_key.py:165
msgid "API Key"
msgstr "API-Key"
-#: cms/models/group_api_key.py:164
+#: cms/models/group_api_key.py:166
msgid "API Keys"
msgstr "API-Keys"
@@ -521,11 +521,11 @@ msgstr "URL"
#: cms/models/sponsor.py:43
msgid "sponsor"
-msgstr "Sponsor"
+msgstr "Sponsor:in"
#: cms/models/sponsor.py:44
msgid "sponsors"
-msgstr "Sponsoren"
+msgstr "Sponsor:innen"
#: cms/models/training_set.py:33
msgid "document"
@@ -639,19 +639,19 @@ msgstr "Datei zu groß! Max. 5 MB"
msgid "Only use one file extension!"
msgstr "Nur maximal ein Dateityp erlaubt!"
-#: core/settings.py:204
+#: core/settings.py:206
msgid "English"
msgstr "Englisch"
-#: core/settings.py:205
+#: core/settings.py:207
msgid "German"
msgstr "Deutsch"
-#: core/settings.py:445 core/settings.py:446 core/settings.py:448
+#: core/settings.py:451 core/settings.py:452 core/settings.py:454
msgid "Lunes Administration"
msgstr "Lunes-Verwaltung"
-#: core/settings.py:447
+#: core/settings.py:453
msgid "Welcome to the vocabulary management of Lunes!"
msgstr "Willkommen bei der Vokabelverwaltung von Lunes!"
diff --git a/lunes_cms/static/css/feedback.css b/lunes_cms/static/css/feedback.css
index a070e35b..4df69f7b 100644
--- a/lunes_cms/static/css/feedback.css
+++ b/lunes_cms/static/css/feedback.css
@@ -22,6 +22,28 @@ thead tr {
font-weight: bold;
}
+@media (prefers-color-scheme: dark) {
+
+ .table-striped tbody tr:nth-of-type(odd){
+ background-color: #000e38;
+
+ }
+
+ .table-striped thead tr {
+ background-color: #47476e;
+ font-weight: bold;
+ }
+
+ thead th .text {
+ color: #8ba6fa;
+ }
+
+ tbody {
+ color: #8ba6fa;
+ }
+
+}
+
/* Checkbox column */
thead th:nth-child(1) {
width: 3%;
diff --git a/lunes_cms/static/js/toggle_document_form_fields.js b/lunes_cms/static/js/toggle_document_form_fields.js
new file mode 100644
index 00000000..c7cc655b
--- /dev/null
+++ b/lunes_cms/static/js/toggle_document_form_fields.js
@@ -0,0 +1,103 @@
+if (!$) {
+ $ = django.jQuery;
+}
+
+// Toggle the plural field depending on the chosen word type
+$(document).ready(() => {
+ const togglePluralWordInput = () => {
+ const isNoun = $("#id_word_type").val() === "Nomen";
+ $("#id_plural").closest("div.row").toggle(isNoun);
+ };
+
+ $("#id_word_type").change(togglePluralWordInput);
+ togglePluralWordInput();
+});
+
+// Toggle the drop down for grammatical gender depending on the chosen word type
+$(document).ready(() => {
+ const toggleGenderDropdown = () => {
+ const isNoun = $("#id_word_type").val() === "Nomen";
+ $("#id_grammatical_gender").closest("div.row").toggle(isNoun);
+ };
+
+ $("#id_word_type").change(toggleGenderDropdown);
+ toggleGenderDropdown();
+});
+
+
+// Detect changes in the "word" field and search for a duplicate
+$(document).ready(() => {
+ $("#id_word").change((event) => {
+ if ($(event.target).val().length > 0) {
+ $.ajax({
+ type: 'GET',
+ url: '/api/search_duplicate/' + $(event.target).val(),
+ dataType: "json",
+ success: function(data) {
+ showDuplicates(data, $(event.target).closest(".card-body"));
+ }
+ })
+ } else {
+ removeDuplicationCheckMessage();
+ }
+ });
+ $("#id_word_type").trigger("change");
+});
+
+// Create and show message whether there is a possible duplicate
+function showDuplicates(data, parent) {
+ removeDuplicationCheckMessage();
+
+ if (data["word"]) {
+ // If there is a duplicated word, use orange background
+ result = createSearchResultField("#ffbb4a");
+
+ // Show alert message
+ addDetailInfo(result, data["message"], "normal");
+
+ // Show the duplicated word with its word type
+ addDetailInfo(result, data["word"], "bold");
+
+ // Show its definition too for more detail
+ addDetailInfo(result, data["definition"], "normal");
+
+ // Show related training sets
+ addDetailInfo(result, data["training_sets"], "normal");
+ } else {
+ // If there is no duplicate, use green background
+ result = createSearchResultField("#72f399");
+
+ // Show message that no duplicate was found
+ addDetailInfo(result, data["message"], "normal");
+ }
+
+ parent.prepend(result);
+}
+
+// Create a zone in which result duplicate search will be shown
+function createSearchResultField(backgroundColor){
+ messageField = document.createElement("div");
+ messageField.setAttribute("id", "result");
+ messageField.style.padding = "25px";
+ messageField.style.backgroundColor = backgroundColor;
+
+ return messageField;
+}
+
+// Create and add detail information
+function addDetailInfo(resultField, info, fontWeight) {
+ detailInfoField = document.createElement("div");
+ detailInfoField.style.fontWeight = fontWeight;
+ detailInfo = document.createTextNode(info);
+ detailInfoField.append(detailInfo);
+ resultField.append(detailInfoField);
+}
+
+// Remove the duplicate search result
+function removeDuplicationCheckMessage(){
+ const existingMessage = document.getElementById("result");
+
+ if (existingMessage) {
+ existingMessage.remove();
+ }
+}
diff --git a/lunes_cms/static/js/toggle_plural_field.js b/lunes_cms/static/js/toggle_plural_field.js
deleted file mode 100644
index cb110a57..00000000
--- a/lunes_cms/static/js/toggle_plural_field.js
+++ /dev/null
@@ -1,97 +0,0 @@
-if (!$) {
- $ = django.jQuery;
-}
-
-//Toggle the plural field depending on the chosen word type
-$(document).ready(() => {
- $("#id_word_type").change((event) =>
- $("#id_plural")
- .closest("div.row")
- .toggle($(event.target).val() == "Nomen"),
- );
- $("#id_word_type").trigger("change");
-});
-
-//Toggle the drop down for grammatical gender depending on the chosen word type
-$(document).ready(() => {
- $("#id_word_type").change((event) =>
- $("#id_grammatical_gender")
- .closest("div.row")
- .toggle($(event.target).val() == "Nomen"),
- );
- $("#id_word_type").trigger("change");
-});
-
-function removeDuplicationCheckMessage(){
- var existingMessage = document.getElementById("result");
-
- if (existingMessage) {
- existingMessage.remove();
- }
-}
-
-function showDuplicates(data, parent) {
- removeDuplicationCheckMessage();
-
- result = document.createElement("div");
- result.setAttribute("id", "result");
-
-
- if (data["word"]) {
- // If there is a duplicated word, use orange background
- result.style.backgroundColor = "#ffbb4a";
- result.style.padding = "25px"
-
- // Show alert message
- messageBox = document.createElement("div");
- message = document.createTextNode(data["message"]);
- messageBox.append(message);
- result.append(messageBox);
- // Show the duplicated word with its word type
- wordBox = document.createElement("div");
- word = document.createTextNode(data["word"]);
- wordBox.style.fontWeight = "bold";
- wordBox.append(word);
- result.append(wordBox);
- // Show its definition too for more detail
- definitionBox = document.createElement("div");
- definition = document.createTextNode(data["definition"]);
- definitionBox.append(definition);
- result.append(definitionBox);
- // Show related training sets
- trainingSetsBox = document.createElement("div");
- trainingSets = document.createTextNode(data["training_sets"]);
- trainingSetsBox.append(trainingSets);
- result.append(trainingSetsBox);
- } else {
- // If there is no duplicate, use green background
- result.style.backgroundColor = "#72f399";
- result.style.padding = "25px"
- // Show message that no duplicate was found
- messageBox = document.createElement("div");
- message = document.createTextNode(data["message"]);
- messageBox.append(message);
- result.append(messageBox);
- }
-
- parent.prepend(result);
-}
-
-
-$(document).ready(() => {
- $("#id_word").change((event) => {
- if ($(event.target).val().length > 0) {
- $.ajax({
- type: 'GET',
- url: '/api/search_duplicate/' + $(event.target).val(),
- dataType: "json",
- success: function(data) {
- showDuplicates(data, $(event.target).closest(".card-body"));
- }
- })
- } else {
- removeDuplicationCheckMessage();
- }
- });
- $("#id_word_type").trigger("change");
-});
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index 867a331f..7034cfac 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = lunes-cms
-version = 2024.6.0
+version = 2024.11.0
author = Tür an Tür – Digitalfabrik gGmbH
author_email = tech@integreat-app.de
description = Content Management System for the Lunes Vocabulary Trainer App
@@ -21,11 +21,10 @@ classifiers =
License :: OSI Approved :: Apache Software License
Operating System :: OS Independent
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.8
- Programming Language :: Python :: 3.9
- Programming Language :: Python :: 3.10
+ Programming Language :: Python :: 3.11
+ Programming Language :: Python :: 3.12
Framework :: Django
- Framework :: Django :: 3.2
+ Framework :: Django :: 5.1
Operating System :: POSIX :: Linux
Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System
Topic :: Internet :: WWW/HTTP :: WSGI :: Application
@@ -45,16 +44,16 @@ keywords =
packages =
lunes_cms
install_requires =
- django==3.2.21
- django-import-export==3.3.7
- django-jazzmin==2.6.0
- django-mptt==0.15.0
- django-qr-code==3.1.1
- djangorestframework==3.14.0
- drf-spectacular==0.26.5
- ipython==8.16.0
- pillow==10.0.1
- psycopg2==2.9.8
+ django==5.1.2
+ django-import-export==4.2.0
+ django-jazzmin==3.0.1
+ django-mptt==0.16.0
+ django-qr-code==4.1.0
+ djangorestframework==3.15.2
+ drf-spectacular==0.27.2
+ ipython==8.29.0
+ pillow==11.0.0
+ psycopg2==2.9.10
pydub==0.25.1
python_requires = >=3.8
include_package_data = True
@@ -63,24 +62,24 @@ scripts = lunes_cms/lunes-cms-cli
[options.extras_require]
dev =
black==23.9.1
- bumpver==2023.1126
- pre-commit==3.4.0
- pyjwt==2.8.0
- pylint-django==2.5.3
- pylint-runner==0.6.0
- shellcheck-py==0.9.0.6
- twine==4.0.2
+ bumpver==2023.1129
+ pre-commit==4.0.1
+ pyjwt==2.9.0
+ pylint-django==2.6.1
+ pylint-runner==0.7.0
+ shellcheck-py==0.10.0.1
+ twine==5.1.1
doc =
- sphinx==7.2.6
- sphinx-rtd-theme==1.3.0
- sphinx-last-updated-by-git==0.3.6
+ sphinx==8.1.3
+ sphinx-rtd-theme==3.0.1
+ sphinx-last-updated-by-git==0.3.8
sphinxcontrib-django==2.5
test =
pytest-circleci-parallelized==0.1.0
- pytest-cov==4.1.0
- pytest-django==4.5.2
- pytest-icdiff==0.8
- pytest-xdist==3.3.1
+ pytest-cov==5.0.0
+ pytest-django==4.9.0
+ pytest-icdiff==0.9
+ pytest-xdist==3.6.1
[bumpver]
current_version = 2024.6.0
diff --git a/tests/api/api_config.py b/tests/api/api_config.py
index d6309444..f69ece48 100644
--- a/tests/api/api_config.py
+++ b/tests/api/api_config.py
@@ -169,22 +169,30 @@
},
{
"endpoint": "/api/training_set/15/",
- "expected_result": "tests/api/expected-results/training_sets_by_discipline_15.json",
+ "expected_result": (
+ "tests/api/expected-results/training_sets_by_discipline_15.json"
+ ),
},
{
"endpoint": "/api/training_set/15/",
"api_key": "VALIDTOKEN",
- "expected_result": "tests/api/expected-results/training_sets_by_discipline_15.json",
+ "expected_result": (
+ "tests/api/expected-results/training_sets_by_discipline_15.json"
+ ),
},
{
"endpoint": "/api/training_set/15/",
"api_key": "INVALIDTOKEN",
- "expected_result": "tests/api/expected-results/training_sets_by_discipline_15.json",
+ "expected_result": (
+ "tests/api/expected-results/training_sets_by_discipline_15.json"
+ ),
},
{
"endpoint": "/api/training_set/20/",
"api_key": "VALIDTOKEN",
- "expected_result": "tests/api/expected-results/training_sets_by_discipline_20.json",
+ "expected_result": (
+ "tests/api/expected-results/training_sets_by_discipline_20.json"
+ ),
},
{
"endpoint": "/api/training_set/20/",
@@ -203,22 +211,30 @@
VOCABULARY_ENDPOINTS = [
{
"endpoint": "/api/documents/7/",
- "expected_result": "tests/api/expected-results/documents_by_training_set_7.json",
+ "expected_result": (
+ "tests/api/expected-results/documents_by_training_set_7.json"
+ ),
},
{
"endpoint": "/api/documents/7/",
"api_key": "VALIDTOKEN",
- "expected_result": "tests/api/expected-results/documents_by_training_set_7.json",
+ "expected_result": (
+ "tests/api/expected-results/documents_by_training_set_7.json"
+ ),
},
{
"endpoint": "/api/documents/7/",
"api_key": "INVALIDTOKEN",
- "expected_result": "tests/api/expected-results/documents_by_training_set_7.json",
+ "expected_result": (
+ "tests/api/expected-results/documents_by_training_set_7.json"
+ ),
},
{
"endpoint": "/api/documents/108/",
"api_key": "VALIDTOKEN",
- "expected_result": "tests/api/expected-results/documents_by_training_set_108.json",
+ "expected_result": (
+ "tests/api/expected-results/documents_by_training_set_108.json"
+ ),
},
{
"endpoint": "/api/documents/108/",
@@ -351,7 +367,9 @@
{
"endpoint": "/api/feedback/",
"post_data": {"comment": "invalid", "content_type": "invalid", "object_id": 1},
- "expected_result": "tests/api/expected-results/feedback_invalid_content_type.json",
+ "expected_result": (
+ "tests/api/expected-results/feedback_invalid_content_type.json"
+ ),
"expected_status_code": 400,
},
{
@@ -361,7 +379,9 @@
"content_type": "document",
"object_id": 1,
},
- "expected_result": "tests/api/expected-results/feedback_document_not_found.json",
+ "expected_result": (
+ "tests/api/expected-results/feedback_document_not_found.json"
+ ),
"expected_status_code": 400,
},
]
diff --git a/tools/install.sh b/tools/install.sh
index cacd989a..633fbd71 100755
--- a/tools/install.sh
+++ b/tools/install.sh
@@ -10,7 +10,7 @@ echo "Checking system requirements..." | print_info
# Check if requirements are satisfied
# Define the required python version
-required_python_version="8"
+required_python_version="11"
if [[ ! -x "$(command -v python3)" ]]; then echo "Python3 is not installed. Please install version 3.${required_python_version} or later manually and run this script again." | print_error
exit 1
fi