diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 955750958d170e723e9714295750d0e1204e878f..b2516b9ec034623bc679680e228e6b96bbc0ef91 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,8 +2,14 @@ include: - project: "AlekSIS/official/AlekSIS" file: /ci/general.yml - project: "AlekSIS/official/AlekSIS" - file: /ci/test.yml + file: /ci/test/test.yml - project: "AlekSIS/official/AlekSIS" - file: /ci/build_dist.yml + file: /ci/test/lint.yml - project: "AlekSIS/official/AlekSIS" - file: /ci/deploy_pypi.yml + file: /ci/test/security.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/build/dist.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/deploy/pages.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/deploy/pypi.yml diff --git a/aleksis/core/admin.py b/aleksis/core/admin.py index e35910f597983ea8cfaa957cd475e607c490dfa0..ae51f4bcfddec26f5b31af1fb79628ac6812476f 100644 --- a/aleksis/core/admin.py +++ b/aleksis/core/admin.py @@ -10,6 +10,7 @@ from .models import ( Announcement, AnnouncementRecipient, CustomMenuItem, + DataCheckResult, Group, Notification, Person, @@ -33,3 +34,4 @@ class AnnouncementAdmin(BaseModelAdmin, VersionAdmin): admin.site.register(Announcement, AnnouncementAdmin) +admin.site.register(DataCheckResult) diff --git a/aleksis/core/apps.py b/aleksis/core/apps.py index e88d812bde928fcdc2f38027b8323aa8f09ad07b..75feb14c3ed9e6d717d1bbd89bf0ef87070d34b3 100644 --- a/aleksis/core/apps.py +++ b/aleksis/core/apps.py @@ -1,12 +1,14 @@ from typing import Any, List, Optional, Tuple import django.apps +from django.apps import apps from django.conf import settings from django.db import OperationalError, ProgrammingError from django.http import HttpRequest from django.utils.module_loading import autodiscover_modules from dynamic_preferences.registries import preference_models +from health_check.plugins import plugin_dir from .registries import ( group_preferences_registry, @@ -52,6 +54,22 @@ class CoreConfig(AppConfig): self._refresh_authentication_backends() + self._load_data_checks() + + from .health_checks import DataChecksHealthCheckBackend + + plugin_dir.register(DataChecksHealthCheckBackend) + + @classmethod + def _load_data_checks(cls): + """Get all data checks from all loaded models.""" + from aleksis.core.data_checks import DataCheckRegistry + + data_checks = [] + for model in apps.get_models(): + data_checks += getattr(model, "data_checks", []) + DataCheckRegistry.data_checks = data_checks + @classmethod def _refresh_authentication_backends(cls): """Refresh config list of enabled authentication backends.""" diff --git a/aleksis/core/data_checks.py b/aleksis/core/data_checks.py new file mode 100644 index 0000000000000000000000000000000000000000..1279822f8d1f28bb40e040d9eaf0bb8126540940 --- /dev/null +++ b/aleksis/core/data_checks.py @@ -0,0 +1,257 @@ +import logging + +from django.contrib.contenttypes.models import ContentType +from django.db.models.aggregates import Count +from django.utils.functional import classproperty +from django.utils.translation import gettext as _ + +import reversion +from reversion import set_comment +from templated_email import send_templated_mail + +from .util.core_helpers import celery_optional, get_site_preferences + + +class SolveOption: + """Define a solve option for one or more data checks. + + Solve options are used in order to give the data admin typical + solutions to a data issue detected by a data check. + + Example definition + + .. code-block:: python + + from aleksis.core.data_checks import SolveOption + from django.utils.translation import gettext as _ + + class DeleteSolveOption(SolveOption): + name = "delete" # has to be unqiue + verbose_name = _("Delete") # should make use of i18n + + @classmethod + def solve(cls, check_result: "DataCheckResult"): + check_result.related_object.delete() + check_result.delete() + + After the solve option has been successfully executed, + the corresponding data check result has to be deleted. + """ + + name: str = "default" + verbose_name: str = "" + + @classmethod + def solve(cls, check_result: "DataCheckResult"): + pass + + +class IgnoreSolveOption(SolveOption): + """Mark the object with data issues as solved.""" + + name = "ignore" + verbose_name = _("Ignore problem") + + @classmethod + def solve(cls, check_result: "DataCheckResult"): + """Mark the object as solved without doing anything more.""" + check_result.solved = True + check_result.save() + + +class DataCheck: + """Define a data check. + + Data checks should be used to search objects of + any type which are broken or need some extra action. + + Defining data checks + -------------------- + Data checks are defined by inheriting from the class DataCheck + and registering the inherited class in the data check registry. + + Example: + + ``data_checks.py`` + ****************** + + .. code-block:: python + + from aleksis.core.data_checks import DataCheck, DATA_CHECK_REGISTRY + from django.utils.translation import gettext as _ + + class ExampleDataCheck(DataCheck): + name = "example" # has to be unique + verbose_name = _("Ensure that there are no examples.") + problem_name = _("There is an example.") # should both make use of i18n + + solve_options = { + IgnoreSolveOption.name: IgnoreSolveOption + } + + @classmethod + def check_data(cls): + from example_app.models import ExampleModel + + wrong_examples = ExampleModel.objects.filter(wrong_value=True) + + for example in wrong_examples: + cls.register_result(example) + + ``models.py`` + ************* + + .. code-block:: python + + from .data_checks import ExampleDataCheck + + # ... + + class ExampleModel(Model): + data_checks = [ExampleDataCheck] + + + Solve options are used in order to give the data admin typical solutions to this specific issue. + They are defined by inheriting from SolveOption. + More information about defining solve options can be find there. + + The dictionary ``solve_options`` should include at least the IgnoreSolveOption, + but preferably also own solve options. The keys in this dictionary + have to be ``<YourOption>SolveOption.name`` + and the values must be the corresponding solve option classes. + + The class method ``check_data`` does the actual work. In this method + your code should find all objects with issues and should register + them in the result database using the class method ``register_result``. + + Data checks have to be registered in their corresponding model. + This can be done by adding a list ``data_checks`` + containing the data check classes. + + Executing data checks + --------------------- + The data checks can be executed by using the + celery task named ``aleksis.core.data_checks.check_data``. + We recommend to create a periodic task in the backend + which executes ``check_data`` on a regular base (e. g. every day). + + .. warning:: + To use the option described above, you must have setup celery properly. + + Notifications about results + --------------------------- + The data check tasks can notify persons via email + if there are new data issues. You can set these persons + by adding them to the preference + ``Email recipients for data checks problem emails`` in the site configuration. + + To enable this feature, you also have to activate + the preference ``Send emails if data checks detect problems``. + """ # noqa: D412 + + name: str = "" + verbose_name: str = "" + problem_name: str = "" + + solve_options = {IgnoreSolveOption.name: IgnoreSolveOption} + + @classmethod + def check_data(cls): + """Find all objects with data issues and register them.""" + pass + + @classmethod + def solve(cls, check_result: "DataCheckResult", solve_option: str): + """Execute a solve option for an object detected by this check. + + :param check_result: The result item from database + :param solve_option: The name of the solve option that should be executed + """ + with reversion.create_revision(): + solve_option_obj = cls.solve_options[solve_option] + set_comment( + _( + f"Solve option '{solve_option_obj.verbose_name}' " + f"for data check '{cls.verbose_name}'" + ) + ) + solve_option_obj.solve(check_result) + + @classmethod + def register_result(cls, instance) -> "DataCheckResult": + """Register an object with data issues in the result database. + + :param instance: The affected object + :return: The database entry + """ + from aleksis.core.models import DataCheckResult + + ct = ContentType.objects.get_for_model(instance) + result = DataCheckResult.objects.get_or_create( + check=cls.name, content_type=ct, object_id=instance.id + ) + return result + + +class DataCheckRegistry: + """Create central registry for all data checks in AlekSIS.""" + + data_checks = [] + + @classproperty + def data_checks_by_name(cls): + return {check.name: check for check in cls.data_checks} + + @classproperty + def data_checks_choices(cls): + return [(check.name, check.verbose_name) for check in cls.data_checks] + + +@celery_optional +def check_data(): + """Execute all registered data checks and send email if activated.""" + for check in DataCheckRegistry.data_checks: + logging.info(f"Run check: {check.verbose_name}") + check.check_data() + + if get_site_preferences()["general__data_checks_send_emails"]: + send_emails_for_data_checks() + + +def send_emails_for_data_checks(): + """Notify one or more recipients about new problems with data. + + Recipients can be set in dynamic preferences. + """ + from .models import DataCheckResult # noqa + + results = DataCheckResult.objects.filter(solved=False, sent=False) + + if results.exists(): + results_by_check = results.values("check").annotate(count=Count("check")) + + results_with_checks = [] + for result in results_by_check: + results_with_checks.append( + (DataCheckRegistry.data_checks_by_name[result["check"]], result["count"]) + ) + + recipient_list = [ + p.mail_sender + for p in get_site_preferences()["general__data_checks_recipients"] + if p.email + ] + + for group in get_site_preferences()["general__data_checks_recipient_groups"]: + recipient_list += [p.mail_sender for p in group.announcement_recipients if p.email] + + send_templated_mail( + template_name="data_checks", + from_email=get_site_preferences()["mail__address"], + recipient_list=recipient_list, + context={"results": results_with_checks}, + ) + + logging.info("Sent notification email because of unsent data checks") + + results.update(sent=True) diff --git a/aleksis/core/health_checks.py b/aleksis/core/health_checks.py new file mode 100644 index 0000000000000000000000000000000000000000..e3239087a17e5fed6c59441e9c6a219e86deb006 --- /dev/null +++ b/aleksis/core/health_checks.py @@ -0,0 +1,18 @@ +from django.utils.translation import gettext as _ + +from health_check.backends import BaseHealthCheckBackend + +from aleksis.core.models import DataCheckResult + + +class DataChecksHealthCheckBackend(BaseHealthCheckBackend): + """Checks whether there are unresolved data problems.""" + + critical_service = False + + def check_status(self): + if DataCheckResult.objects.filter(solved=False).exists(): + self.add_error(_("There are unresolved data problems.")) + + def identifier(self): + return self.__class__.__name__ diff --git a/aleksis/core/locale/ar/LC_MESSAGES/django.po b/aleksis/core/locale/ar/LC_MESSAGES/django.po index a3e781c6b389c7e3c6814fe9491358e6810b1b41..26513bbf79d22e23b2c3f8f90b93b5474034a972 100644 --- a/aleksis/core/locale/ar/LC_MESSAGES/django.po +++ b/aleksis/core/locale/ar/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: AlekSIS (School Information System) 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -18,6 +18,15 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" +#: data_checks.py:53 +msgid "Ignore problem" +msgstr "" + +#: data_checks.py:174 +#, python-brace-format +msgid "Solve option '{solve_option_obj.verbose_name}' " +msgstr "" + #: filters.py:37 templates/core/base.html:77 templates/core/group/list.html:20 #: templates/core/person/list.html:24 templates/search/search.html:7 #: templates/search/search.html:22 @@ -32,92 +41,96 @@ msgstr "" msgid "Search by contact details" msgstr "" -#: forms.py:46 +#: forms.py:54 msgid "You cannot set a new username when also selecting an existing user." msgstr "" -#: forms.py:50 +#: forms.py:58 msgid "This username is already in use." msgstr "" -#: forms.py:74 +#: forms.py:82 msgid "Base data" msgstr "" -#: forms.py:80 +#: forms.py:88 msgid "Address" msgstr "" -#: forms.py:81 +#: forms.py:89 msgid "Contact data" msgstr "" -#: forms.py:83 +#: forms.py:91 msgid "Advanced personal data" msgstr "" -#: forms.py:114 +#: forms.py:134 msgid "New user" msgstr "" -#: forms.py:114 +#: forms.py:134 msgid "Create a new account" msgstr "" -#: forms.py:126 models.py:91 +#: forms.py:146 models.py:102 msgid "School term" msgstr "" -#: forms.py:127 +#: forms.py:147 msgid "Common data" msgstr "" -#: forms.py:128 forms.py:170 menus.py:152 models.py:105 +#: forms.py:148 forms.py:197 menus.py:169 models.py:116 #: templates/core/person/list.html:8 templates/core/person/list.html:9 msgid "Persons" msgstr "" -#: forms.py:129 +#: forms.py:149 msgid "Additional data" msgstr "" -#: forms.py:163 forms.py:166 models.py:35 +#: forms.py:189 forms.py:192 models.py:45 msgid "Date" msgstr "" -#: forms.py:164 forms.py:167 models.py:43 +#: forms.py:190 forms.py:193 models.py:53 msgid "Time" msgstr "" -#: forms.py:172 menus.py:160 models.py:319 templates/core/group/list.html:8 -#: templates/core/group/list.html:9 templates/core/person/full.html:136 +#: forms.py:210 menus.py:177 models.py:338 templates/core/group/list.html:8 +#: templates/core/group/list.html:9 templates/core/person/full.html:144 msgid "Groups" msgstr "" -#: forms.py:176 +#: forms.py:220 msgid "From when until when should the announcement be displayed?" msgstr "" -#: forms.py:179 +#: forms.py:223 msgid "Who should see the announcement?" msgstr "" -#: forms.py:180 +#: forms.py:224 msgid "Write your announcement:" msgstr "" -#: forms.py:219 +#: forms.py:263 msgid "You are not allowed to create announcements which are only valid in the past." msgstr "" -#: forms.py:223 +#: forms.py:267 msgid "The from date and time must be earlier then the until date and time." msgstr "" -#: forms.py:232 +#: forms.py:276 msgid "You need at least one recipient." msgstr "" +#: health_checks.py:15 +msgid "There are unresolved data problems." +msgstr "" + #: menus.py:7 templates/two_factor/core/login.html:6 #: templates/two_factor/core/login.html:10 #: templates/two_factor/core/login.html:86 @@ -156,539 +169,657 @@ msgstr "" msgid "Admin" msgstr "" -#: menus.py:75 models.py:564 templates/core/announcement/list.html:7 +#: menus.py:75 models.py:602 templates/core/announcement/list.html:7 #: templates/core/announcement/list.html:8 msgid "Announcements" msgstr "" -#: menus.py:86 models.py:92 templates/core/school_term/list.html:8 +#: menus.py:86 models.py:103 templates/core/school_term/list.html:8 #: templates/core/school_term/list.html:9 msgid "School terms" msgstr "" -#: menus.py:97 templates/core/management/data_management.html:6 +#: menus.py:97 templates/core/dashboard_widget/list.html:8 +#: templates/core/dashboard_widget/list.html:9 +msgid "Dashboard widgets" +msgstr "" + +#: menus.py:108 templates/core/management/data_management.html:6 #: templates/core/management/data_management.html:7 msgid "Data management" msgstr "" -#: menus.py:105 templates/core/pages/system_status.html:5 +#: menus.py:116 templates/core/pages/system_status.html:5 #: templates/core/pages/system_status.html:7 msgid "System status" msgstr "" -#: menus.py:116 +#: menus.py:127 msgid "Impersonation" msgstr "" -#: menus.py:124 +#: menus.py:135 msgid "Configuration" msgstr "" -#: menus.py:135 +#: menus.py:146 templates/core/data_check/list.html:9 +#: templates/core/data_check/list.html:10 +msgid "Data checks" +msgstr "" + +#: menus.py:152 msgid "Backend Admin" msgstr "" -#: menus.py:143 +#: menus.py:160 msgid "People" msgstr "" -#: menus.py:168 models.py:727 templates/core/group_type/list.html:8 +#: menus.py:185 models.py:812 templates/core/group_type/list.html:8 #: templates/core/group_type/list.html:9 msgid "Group types" msgstr "" -#: menus.py:179 +#: menus.py:196 msgid "Persons and accounts" msgstr "" -#: menus.py:190 +#: menus.py:207 msgid "Groups and child groups" msgstr "" -#: menus.py:201 models.py:363 templates/core/additional_field/list.html:8 +#: menus.py:218 models.py:385 templates/core/additional_field/list.html:8 #: templates/core/additional_field/list.html:9 msgid "Additional fields" msgstr "" -#: menus.py:216 templates/core/group/child_groups.html:7 +#: menus.py:233 templates/core/group/child_groups.html:7 #: templates/core/group/child_groups.html:9 msgid "Assign child groups to groups" msgstr "" -#: mixins.py:406 +#: mixins.py:384 msgid "Linked school term" msgstr "" -#: models.py:33 +#: models.py:43 msgid "Boolean (Yes/No)" msgstr "" -#: models.py:34 +#: models.py:44 msgid "Text (one line)" msgstr "" -#: models.py:36 +#: models.py:46 msgid "Date and time" msgstr "" -#: models.py:37 +#: models.py:47 msgid "Decimal number" msgstr "" -#: models.py:38 models.py:146 +#: models.py:48 models.py:157 msgid "E-mail address" msgstr "" -#: models.py:39 +#: models.py:49 msgid "Integer" msgstr "" -#: models.py:40 +#: models.py:50 msgid "IP address" msgstr "" -#: models.py:41 +#: models.py:51 msgid "Boolean or empty (Yes/No/Neither)" msgstr "" -#: models.py:42 +#: models.py:52 msgid "Text (multi-line)" msgstr "" -#: models.py:44 +#: models.py:54 msgid "URL / Link" msgstr "" -#: models.py:56 models.py:700 +#: models.py:66 models.py:785 msgid "Name" msgstr "" -#: models.py:58 +#: models.py:68 msgid "Start date" msgstr "" -#: models.py:59 +#: models.py:69 msgid "End date" msgstr "" -#: models.py:77 +#: models.py:88 msgid "The start date must be earlier than the end date." msgstr "" -#: models.py:84 +#: models.py:95 msgid "There is already a school term for this time or a part of this time." msgstr "" -#: models.py:104 templates/core/person/accounts.html:36 +#: models.py:115 models.py:744 templates/core/person/accounts.html:41 msgid "Person" msgstr "" -#: models.py:107 +#: models.py:118 msgid "Can view address" msgstr "" -#: models.py:108 +#: models.py:119 msgid "Can view contact details" msgstr "" -#: models.py:109 +#: models.py:120 msgid "Can view photo" msgstr "" -#: models.py:110 +#: models.py:121 msgid "Can view persons groups" msgstr "" -#: models.py:111 +#: models.py:122 msgid "Can view personal details" msgstr "" -#: models.py:116 +#: models.py:127 msgid "female" msgstr "" -#: models.py:116 +#: models.py:127 msgid "male" msgstr "" -#: models.py:124 +#: models.py:135 msgid "Linked user" msgstr "" -#: models.py:126 +#: models.py:137 msgid "Is person active?" msgstr "" -#: models.py:128 +#: models.py:139 msgid "First name" msgstr "" -#: models.py:129 +#: models.py:140 msgid "Last name" msgstr "" -#: models.py:131 +#: models.py:142 msgid "Additional name(s)" msgstr "" -#: models.py:135 models.py:332 +#: models.py:146 models.py:354 msgid "Short name" msgstr "" -#: models.py:138 +#: models.py:149 msgid "Street" msgstr "" -#: models.py:139 +#: models.py:150 msgid "Street number" msgstr "" -#: models.py:140 +#: models.py:151 msgid "Postal code" msgstr "" -#: models.py:141 +#: models.py:152 msgid "Place" msgstr "" -#: models.py:143 +#: models.py:154 msgid "Home phone" msgstr "" -#: models.py:144 +#: models.py:155 msgid "Mobile phone" msgstr "" -#: models.py:148 +#: models.py:159 msgid "Date of birth" msgstr "" -#: models.py:149 +#: models.py:160 msgid "Sex" msgstr "" -#: models.py:151 +#: models.py:162 msgid "Photo" msgstr "" -#: models.py:155 templates/core/person/full.html:129 +#: models.py:166 templates/core/person/full.html:137 msgid "Guardians / Parents" msgstr "" -#: models.py:162 +#: models.py:173 msgid "Primary group" msgstr "" -#: models.py:165 models.py:423 models.py:447 models.py:532 models.py:720 -#: templates/core/person/full.html:112 +#: models.py:176 models.py:461 models.py:485 models.py:570 models.py:805 +#: templates/core/person/full.html:120 msgid "Description" msgstr "" -#: models.py:296 +#: models.py:313 msgid "Title of field" msgstr "" -#: models.py:298 +#: models.py:315 msgid "Type of field" msgstr "" -#: models.py:305 +#: models.py:322 msgid "Addtitional field for groups" msgstr "" -#: models.py:306 +#: models.py:323 msgid "Addtitional fields for groups" msgstr "" -#: models.py:318 +#: models.py:337 msgid "Group" msgstr "" -#: models.py:320 +#: models.py:340 msgid "Can assign child groups to groups" msgstr "" -#: models.py:330 +#: models.py:341 +msgid "Can view statistics about group." +msgstr "" + +#: models.py:352 msgid "Long name" msgstr "" -#: models.py:340 templates/core/group/full.html:65 +#: models.py:362 templates/core/group/full.html:85 msgid "Members" msgstr "" -#: models.py:343 templates/core/group/full.html:62 +#: models.py:365 templates/core/group/full.html:82 msgid "Owners" msgstr "" -#: models.py:350 templates/core/group/full.html:54 +#: models.py:372 templates/core/group/full.html:55 msgid "Parent groups" msgstr "" -#: models.py:358 +#: models.py:380 msgid "Type of group" msgstr "" -#: models.py:419 +#: models.py:457 msgid "User" msgstr "" -#: models.py:422 models.py:446 models.py:531 +#: models.py:460 models.py:484 models.py:569 #: templates/core/announcement/list.html:18 msgid "Title" msgstr "" -#: models.py:425 +#: models.py:463 msgid "Application" msgstr "" -#: models.py:431 +#: models.py:469 msgid "Activity" msgstr "" -#: models.py:432 +#: models.py:470 msgid "Activities" msgstr "" -#: models.py:438 +#: models.py:476 msgid "Sender" msgstr "" -#: models.py:443 +#: models.py:481 msgid "Recipient" msgstr "" -#: models.py:448 models.py:701 +#: models.py:486 models.py:786 msgid "Link" msgstr "" -#: models.py:450 +#: models.py:488 msgid "Read" msgstr "" -#: models.py:451 +#: models.py:489 msgid "Sent" msgstr "" -#: models.py:464 +#: models.py:502 msgid "Notification" msgstr "" -#: models.py:465 +#: models.py:503 msgid "Notifications" msgstr "" -#: models.py:533 +#: models.py:571 msgid "Link to detailed view" msgstr "" -#: models.py:536 +#: models.py:574 msgid "Date and time from when to show" msgstr "" -#: models.py:539 +#: models.py:577 msgid "Date and time until when to show" msgstr "" -#: models.py:563 +#: models.py:601 msgid "Announcement" msgstr "" -#: models.py:601 +#: models.py:639 msgid "Announcement recipient" msgstr "" -#: models.py:602 +#: models.py:640 msgid "Announcement recipients" msgstr "" -#: models.py:652 +#: models.py:690 msgid "Widget Title" msgstr "" -#: models.py:653 +#: models.py:691 msgid "Activate Widget" msgstr "" -#: models.py:671 +#: models.py:694 +msgid "Size on mobile devices" +msgstr "" + +#: models.py:695 +msgid "<= 600 px, 12 columns" +msgstr "" + +#: models.py:700 +msgid "Size on tablet devices" +msgstr "" + +#: models.py:701 +msgid "> 600 px, 12 columns" +msgstr "" + +#: models.py:706 +msgid "Size on desktop devices" +msgstr "" + +#: models.py:707 +msgid "> 992 px, 12 columns" +msgstr "" + +#: models.py:712 +msgid "Size on large desktop devices" +msgstr "" + +#: models.py:713 +msgid "> 1200 px>, 12 columns" +msgstr "" + +#: models.py:734 +msgid "Can edit default dashboard" +msgstr "" + +#: models.py:735 msgid "Dashboard Widget" msgstr "" -#: models.py:672 +#: models.py:736 msgid "Dashboard Widgets" msgstr "" -#: models.py:678 +#: models.py:741 +msgid "Dashboard widget" +msgstr "" + +#: models.py:746 +msgid "Order" +msgstr "" + +#: models.py:747 +msgid "Part of the default dashboard" +msgstr "" + +#: models.py:755 +msgid "Dashboard widget order" +msgstr "" + +#: models.py:756 +msgid "Dashboard widget orders" +msgstr "" + +#: models.py:762 msgid "Menu ID" msgstr "" -#: models.py:690 +#: models.py:775 msgid "Custom menu" msgstr "" -#: models.py:691 +#: models.py:776 msgid "Custom menus" msgstr "" -#: models.py:698 +#: models.py:783 msgid "Menu" msgstr "" -#: models.py:702 +#: models.py:787 msgid "Icon" msgstr "" -#: models.py:708 +#: models.py:793 msgid "Custom menu item" msgstr "" -#: models.py:709 +#: models.py:794 msgid "Custom menu items" msgstr "" -#: models.py:719 +#: models.py:804 msgid "Title of type" msgstr "" -#: models.py:726 templates/core/group/full.html:46 +#: models.py:811 templates/core/group/full.html:47 msgid "Group type" msgstr "" -#: models.py:736 +#: models.py:821 msgid "Can view system status" msgstr "" -#: models.py:737 +#: models.py:822 msgid "Can link persons to accounts" msgstr "" -#: models.py:738 +#: models.py:823 msgid "Can manage data" msgstr "" -#: models.py:739 +#: models.py:824 msgid "Can impersonate" msgstr "" -#: models.py:740 +#: models.py:825 msgid "Can use search" msgstr "" -#: models.py:741 +#: models.py:826 msgid "Can change site preferences" msgstr "" -#: models.py:742 +#: models.py:827 msgid "Can change person preferences" msgstr "" -#: models.py:743 +#: models.py:828 msgid "Can change group preferences" msgstr "" -#: preferences.py:24 +#: models.py:864 +msgid "Related data check task" +msgstr "" + +#: models.py:872 +msgid "Issue solved" +msgstr "" + +#: models.py:873 +msgid "Notification sent" +msgstr "" + +#: models.py:886 +msgid "Data check result" +msgstr "" + +#: models.py:887 +msgid "Data check results" +msgstr "" + +#: models.py:889 +msgid "Can run data checks" +msgstr "" + +#: models.py:890 +msgid "Can solve data check problems" +msgstr "" + +#: preferences.py:27 msgid "Authentication" msgstr "" -#: preferences.py:33 +#: preferences.py:28 +msgid "Internationalisation" +msgstr "" + +#: preferences.py:37 msgid "Site title" msgstr "" -#: preferences.py:42 +#: preferences.py:46 msgid "Site description" msgstr "" -#: preferences.py:51 +#: preferences.py:55 msgid "Primary colour" msgstr "" -#: preferences.py:60 +#: preferences.py:64 msgid "Secondary colour" msgstr "" -#: preferences.py:68 +#: preferences.py:72 msgid "Logo" msgstr "" -#: preferences.py:76 +#: preferences.py:80 msgid "Favicon" msgstr "" -#: preferences.py:84 +#: preferences.py:88 msgid "PWA-Icon" msgstr "" -#: preferences.py:93 +#: preferences.py:97 msgid "Mail out name" msgstr "" -#: preferences.py:102 +#: preferences.py:106 msgid "Mail out address" msgstr "" -#: preferences.py:112 +#: preferences.py:116 msgid "Link to privacy policy" msgstr "" -#: preferences.py:122 +#: preferences.py:126 msgid "Link to imprint" msgstr "" -#: preferences.py:132 +#: preferences.py:136 msgid "Name format for addressing" msgstr "" -#: preferences.py:146 +#: preferences.py:150 msgid "Channels to use for notifications" msgstr "" -#: preferences.py:156 +#: preferences.py:160 msgid "Regular expression to match primary group, e.g. '^Class .*'" msgstr "" -#: preferences.py:165 +#: preferences.py:169 msgid "Field on person to match primary group against" msgstr "" -#: preferences.py:177 +#: preferences.py:181 msgid "Display name of the school" msgstr "" -#: preferences.py:186 +#: preferences.py:190 msgid "Official name of the school, e.g. as given by supervisory authority" msgstr "" -#: preferences.py:194 +#: preferences.py:198 msgid "Enabled custom authentication backends" msgstr "" -#: settings.py:300 +#: preferences.py:211 +msgid "Available languages" +msgstr "" + +#: preferences.py:223 +msgid "Send emails if data checks detect problems" +msgstr "" + +#: preferences.py:234 +msgid "Email recipients for data checks problem emails" +msgstr "" + +#: preferences.py:245 +msgid "Email recipient groups for data checks problem emails" +msgstr "" + +#: settings.py:322 msgid "English" msgstr "" -#: settings.py:301 +#: settings.py:323 msgid "German" msgstr "" -#: settings.py:302 +#: settings.py:324 msgid "French" msgstr "" -#: settings.py:303 +#: settings.py:325 msgid "Norwegian (bokmål)" msgstr "" #: tables.py:19 templates/core/announcement/list.html:36 -#: templates/core/group/full.html:23 templates/core/person/full.html:22 +#: templates/core/group/full.html:24 templates/core/person/full.html:23 msgid "Edit" msgstr "" -#: tables.py:21 templates/core/announcement/list.html:22 +#: tables.py:21 tables.py:89 templates/core/announcement/list.html:22 msgid "Actions" msgstr "" -#: tables.py:56 tables.py:57 tables.py:71 -#: templates/core/announcement/list.html:42 templates/core/group/full.html:30 -#: templates/core/pages/delete.html:22 templates/core/person/full.html:29 +#: tables.py:56 tables.py:57 tables.py:71 tables.py:87 +#: templates/core/announcement/list.html:42 templates/core/group/full.html:31 +#: templates/core/pages/delete.html:22 templates/core/person/full.html:30 msgid "Delete" msgstr "" @@ -760,21 +891,21 @@ msgstr "" msgid "Create additional field" msgstr "" -#: templates/core/announcement/form.html:10 -#: templates/core/announcement/form.html:17 +#: templates/core/announcement/form.html:14 +#: templates/core/announcement/form.html:21 msgid "Edit announcement" msgstr "" -#: templates/core/announcement/form.html:12 +#: templates/core/announcement/form.html:16 msgid "Publish announcement" msgstr "" -#: templates/core/announcement/form.html:19 +#: templates/core/announcement/form.html:23 #: templates/core/announcement/list.html:13 msgid "Publish new announcement" msgstr "" -#: templates/core/announcement/form.html:30 +#: templates/core/announcement/form.html:34 msgid "Save und publish announcement" msgstr "" @@ -814,6 +945,120 @@ msgstr "" msgid "Powered by AlekSIS" msgstr "" +#: templates/core/dashboard_widget/create.html:8 +#: templates/core/dashboard_widget/create.html:12 +#, python-format +msgid "Create %(widget)s" +msgstr "" + +#: templates/core/dashboard_widget/edit.html:8 +#: templates/core/dashboard_widget/edit.html:12 +#, python-format +msgid "Edit %(widget)s" +msgstr "" + +#: templates/core/dashboard_widget/list.html:17 +#, python-format +msgid "Create %(name)s" +msgstr "" + +#: templates/core/dashboard_widget/list.html:25 +#: templates/core/edit_dashboard.html:8 templates/core/edit_dashboard.html:15 +msgid "Edit default dashboard" +msgstr "" + +#: templates/core/data_check/list.html:15 +msgid "Check data again" +msgstr "" + +#: templates/core/data_check/list.html:22 +msgid "The system detected some problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:23 +msgid "" +"Please go through all data and check whether some extra action is\n" +" needed." +msgstr "" + +#: templates/core/data_check/list.html:31 +msgid "Everything is fine." +msgstr "" + +#: templates/core/data_check/list.html:32 +msgid "The system hasn't detected any problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:40 +msgid "Detected problems" +msgstr "" + +#: templates/core/data_check/list.html:45 +msgid "Affected object" +msgstr "" + +#: templates/core/data_check/list.html:46 +msgid "Detected problem" +msgstr "" + +#: templates/core/data_check/list.html:47 +msgid "Show details" +msgstr "" + +#: templates/core/data_check/list.html:48 +msgid "Options to solve the problem" +msgstr "" + +#: templates/core/data_check/list.html:62 +msgid "Show object" +msgstr "" + +#: templates/core/data_check/list.html:84 +msgid "Registered checks" +msgstr "" + +#: templates/core/data_check/list.html:88 +msgid "" +"\n" +" The system will check for the following problems:\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:6 templates/core/edit_dashboard.html:13 +#: templates/core/index.html:14 +msgid "Edit dashboard" +msgstr "" + +#: templates/core/edit_dashboard.html:24 +msgid "" +"\n" +" On this page you can arrange your personal dashboard. You can drag any items from \"Available widgets\" to \"Your\n" +" Dashboard\" or change the order by moving the widgets. After you have finished, please don't forget to click on\n" +" \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:30 +msgid "" +"\n" +" On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own\n" +" dashboard. You can drag any items from \"Available widgets\" to \"Default Dashboard\" or change the order\n" +" by moving the widgets. After you have finished, please don't forget to click on \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:48 +msgid "Available widgets" +msgstr "" + +#: templates/core/edit_dashboard.html:57 +msgid "Your dashboard" +msgstr "" + +#: templates/core/edit_dashboard.html:59 +msgid "Default dashboard" +msgstr "" + #: templates/core/group/child_groups.html:18 msgid "" "\n" @@ -886,14 +1131,38 @@ msgstr "" msgid "Save and next" msgstr "" -#: templates/core/group/edit.html:6 templates/core/group/edit.html:7 +#: templates/core/group/edit.html:11 templates/core/group/edit.html:12 msgid "Edit group" msgstr "" -#: templates/core/group/full.html:37 templates/core/person/full.html:36 +#: templates/core/group/full.html:38 templates/core/person/full.html:37 msgid "Change preferences" msgstr "" +#: templates/core/group/full.html:64 +msgid "Statistics" +msgstr "" + +#: templates/core/group/full.html:67 +msgid "Count of members" +msgstr "" + +#: templates/core/group/full.html:71 +msgid "Average age" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "Age range" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years to" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years " +msgstr "" + #: templates/core/group/list.html:14 msgid "Create group" msgstr "" @@ -922,23 +1191,31 @@ msgstr "" msgid "Home" msgstr "" -#: templates/core/index.html:42 +#: templates/core/index.html:50 +msgid "" +"\n" +" You didn't customise your dashboard so that you see the system default. Please click on \"Edit dashboard\" to\n" +" customise your personal dashboard.\n" +" " +msgstr "" + +#: templates/core/index.html:59 msgid "Last activities" msgstr "" -#: templates/core/index.html:60 +#: templates/core/index.html:77 msgid "No activities available yet." msgstr "" -#: templates/core/index.html:65 +#: templates/core/index.html:82 msgid "Recent notifications" msgstr "" -#: templates/core/index.html:81 +#: templates/core/index.html:98 msgid "More information →" msgstr "" -#: templates/core/index.html:88 +#: templates/core/index.html:105 msgid "No notifications available yet." msgstr "" @@ -1017,21 +1294,6 @@ msgid "" " " msgstr "" -#: templates/core/pages/offline.html:6 -msgid "" -"No internet\n" -" connection." -msgstr "" - -#: templates/core/pages/offline.html:10 -msgid "" -"\n" -" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" -" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" -" administrators:\n" -" " -msgstr "" - #: templates/core/pages/progress.html:27 msgid "" "\n" @@ -1151,37 +1413,19 @@ msgid "" " " msgstr "" -#: templates/core/partials/crud_events.html:10 -#, python-format -msgid "" -"\n" -" Created by %(person)s\n" -" " -msgstr "" - -#: templates/core/partials/crud_events.html:14 -#: templates/core/partials/crud_events.html:22 -#: templates/core/partials/crud_events.html:26 -#, python-format -msgid "" -"\n" -" Updated by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Changed by" msgstr "" -#: templates/core/partials/crud_events.html:18 -#, python-format -msgid "" -"\n" -" Deleted by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Unknown" msgstr "" -#: templates/core/partials/language_form.html:16 +#: templates/core/partials/language_form.html:15 msgid "Language" msgstr "" -#: templates/core/partials/language_form.html:28 +#: templates/core/partials/language_form.html:27 msgid "Select language" msgstr "" @@ -1202,11 +1446,12 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:7 templates/core/person/accounts.html:9 +#: templates/core/person/accounts.html:12 +#: templates/core/person/accounts.html:14 msgid "Link persons to accounts" msgstr "" -#: templates/core/person/accounts.html:16 +#: templates/core/person/accounts.html:21 msgid "" "\n" " You can use this form to assign user accounts to persons. Use the\n" @@ -1216,28 +1461,32 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:31 -#: templates/core/person/accounts.html:55 +#: templates/core/person/accounts.html:36 +#: templates/core/person/accounts.html:60 msgid "Update" msgstr "" -#: templates/core/person/accounts.html:37 +#: templates/core/person/accounts.html:42 msgid "Existing account" msgstr "" -#: templates/core/person/accounts.html:38 +#: templates/core/person/accounts.html:43 msgid "New account" msgstr "" -#: templates/core/person/edit.html:11 templates/core/person/edit.html:12 +#: templates/core/person/edit.html:12 templates/core/person/edit.html:13 msgid "Edit person" msgstr "" -#: templates/core/person/full.html:42 +#: templates/core/person/full.html:44 +msgid "Impersonate" +msgstr "" + +#: templates/core/person/full.html:50 msgid "Contact details" msgstr "" -#: templates/core/person/full.html:122 +#: templates/core/person/full.html:130 msgid "Children" msgstr "" @@ -1281,14 +1530,29 @@ msgstr "" msgid "Save preferences" msgstr "" -#: templates/dynamic_preferences/sections.html:7 -msgid "All" -msgstr "" - #: templates/impersonate/list_users.html:8 msgid "Impersonate user" msgstr "" +#: templates/offline.html:5 +msgid "Network error" +msgstr "" + +#: templates/offline.html:8 +msgid "" +"No internet\n" +" connection." +msgstr "" + +#: templates/offline.html:12 +msgid "" +"\n" +" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" +" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" +" administrators:\n" +" " +msgstr "" + #: templates/search/search.html:8 msgid "Global Search" msgstr "" @@ -1309,6 +1573,39 @@ msgstr "" msgid "Please enter a search term above." msgstr "" +#: templates/templated_email/data_checks.email:4 +msgid "The system detected some new problems with your data." +msgstr "" + +#: templates/templated_email/data_checks.email:8 +#: templates/templated_email/data_checks.email:24 +msgid "Hello," +msgstr "" + +#: templates/templated_email/data_checks.email:10 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:26 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:34 +msgid "Problem description" +msgstr "" + +#: templates/templated_email/data_checks.email:35 +msgid "Count of objects with new problems" +msgstr "" + #: templates/templated_email/notification.email:3 msgid "New notification for" msgstr "" @@ -1695,58 +1992,91 @@ msgstr "" msgid "SMS" msgstr "" -#: views.py:122 +#: views.py:141 msgid "The school term has been created." msgstr "" -#: views.py:133 +#: views.py:153 msgid "The school term has been saved." msgstr "" -#: views.py:273 +#: views.py:298 msgid "The child groups were successfully saved." msgstr "" -#: views.py:309 +#: views.py:336 msgid "The person has been saved." msgstr "" -#: views.py:346 +#: views.py:375 msgid "The group has been saved." msgstr "" -#: views.py:434 +#: views.py:467 msgid "The announcement has been saved." msgstr "" -#: views.py:450 +#: views.py:483 msgid "The announcement has been deleted." msgstr "" -#: views.py:521 +#: views.py:562 msgid "The preferences have been saved successfully." msgstr "" -#: views.py:544 +#: views.py:586 msgid "The person has been deleted." msgstr "" -#: views.py:557 +#: views.py:600 msgid "The group has been deleted." msgstr "" -#: views.py:588 +#: views.py:632 msgid "The additional_field has been saved." msgstr "" -#: views.py:622 +#: views.py:666 msgid "The additional field has been deleted." msgstr "" -#: views.py:646 +#: views.py:691 msgid "The group type has been saved." msgstr "" -#: views.py:676 +#: views.py:721 msgid "The group type has been deleted." msgstr "" + +#: views.py:749 +msgid "The data check has been started. Please note that it may take a while before you are able to fetch the data on this page." +msgstr "" + +#: views.py:754 +msgid "The data check has finished." +msgstr "" + +#: views.py:769 +#, python-brace-format +msgid "The solve option '{solve_option_obj.verbose_name}' " +msgstr "" + +#: views.py:811 +msgid "The dashboard widget has been saved." +msgstr "" + +#: views.py:841 +msgid "The dashboard widget has been created." +msgstr "" + +#: views.py:851 +msgid "The dashboard widget has been deleted." +msgstr "" + +#: views.py:914 +msgid "Your dashboard configuration has been saved successfully." +msgstr "" + +#: views.py:916 +msgid "The configuration of the default dashboard has been saved successfully." +msgstr "" diff --git a/aleksis/core/locale/ar/LC_MESSAGES/djangojs.po b/aleksis/core/locale/ar/LC_MESSAGES/djangojs.po index c48da556b093acc67242995ed684ce8bfa609426..126b0ef841fd5c5732a533ae0604af89fcfb093b 100644 --- a/aleksis/core/locale/ar/LC_MESSAGES/djangojs.po +++ b/aleksis/core/locale/ar/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -18,14 +18,18 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" -#: static/js/main.js:21 +#: static/js/main.js:15 msgid "Today" msgstr "" -#: static/js/main.js:22 +#: static/js/main.js:16 msgid "Cancel" msgstr "" -#: static/js/main.js:23 +#: static/js/main.js:17 msgid "OK" msgstr "" + +#: static/js/main.js:118 +msgid "This page may contain outdated information since there is no internet connection." +msgstr "" diff --git a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po index 6f6d8fc83f732fbd2abdedbb8f4793e2591c1ca1..f44814ae73c4edaaa27294f5468eb3cb5eb9e44f 100644 --- a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po +++ b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: AlekSIS (School Information System) 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" -"PO-Revision-Date: 2020-12-19 12:57+0000\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" +"PO-Revision-Date: 2021-01-11 20:42+0000\n" "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n" "Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis/" "de/>\n" @@ -17,7 +17,16 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.3.2\n" +"X-Generator: Weblate 4.4\n" + +#: data_checks.py:53 +msgid "Ignore problem" +msgstr "Problem ignorieren" + +#: data_checks.py:174 +#, python-brace-format +msgid "Solve option '{solve_option_obj.verbose_name}' " +msgstr "Lösungsoption \"{solve_option_obj.verbose_name}\" " #: filters.py:37 templates/core/base.html:77 templates/core/group/list.html:20 #: templates/core/person/list.html:24 templates/search/search.html:7 @@ -33,92 +42,96 @@ msgstr "Nach Namen suchen" msgid "Search by contact details" msgstr "Nach Kontaktdetails suchen" -#: forms.py:46 +#: forms.py:54 msgid "You cannot set a new username when also selecting an existing user." msgstr "Sie können keine neuen Benutzer erstellen, wenn Sie gleichzeitig einen existierenden Benutzer auswählen." -#: forms.py:50 +#: forms.py:58 msgid "This username is already in use." msgstr "Dieser Benutzername wird bereits genutzt." -#: forms.py:74 +#: forms.py:82 msgid "Base data" msgstr "Basisdaten" -#: forms.py:80 +#: forms.py:88 msgid "Address" msgstr "Adresse" -#: forms.py:81 +#: forms.py:89 msgid "Contact data" msgstr "Kontaktdaten" -#: forms.py:83 +#: forms.py:91 msgid "Advanced personal data" msgstr "Zusätzliche persönliche Daten" -#: forms.py:114 +#: forms.py:134 msgid "New user" msgstr "Neuer Benutzer" -#: forms.py:114 +#: forms.py:134 msgid "Create a new account" msgstr "Neues Benutzerkonto erstellen" -#: forms.py:126 models.py:91 +#: forms.py:146 models.py:102 msgid "School term" msgstr "Schuljahr" -#: forms.py:127 +#: forms.py:147 msgid "Common data" msgstr "Allgemeine Daten" -#: forms.py:128 forms.py:170 menus.py:152 models.py:105 +#: forms.py:148 forms.py:197 menus.py:169 models.py:116 #: templates/core/person/list.html:8 templates/core/person/list.html:9 msgid "Persons" msgstr "Personen" -#: forms.py:129 +#: forms.py:149 msgid "Additional data" msgstr "Zusätzliche Datne" -#: forms.py:163 forms.py:166 models.py:35 +#: forms.py:189 forms.py:192 models.py:45 msgid "Date" msgstr "Datum" -#: forms.py:164 forms.py:167 models.py:43 +#: forms.py:190 forms.py:193 models.py:53 msgid "Time" msgstr "Zeit" -#: forms.py:172 menus.py:160 models.py:319 templates/core/group/list.html:8 -#: templates/core/group/list.html:9 templates/core/person/full.html:136 +#: forms.py:210 menus.py:177 models.py:338 templates/core/group/list.html:8 +#: templates/core/group/list.html:9 templates/core/person/full.html:144 msgid "Groups" msgstr "Gruppen" -#: forms.py:176 +#: forms.py:220 msgid "From when until when should the announcement be displayed?" msgstr "Von wann bis wann soll die Ankündigung angezeigt werden?" -#: forms.py:179 +#: forms.py:223 msgid "Who should see the announcement?" msgstr "Wer soll die Ankündigung sehen?" -#: forms.py:180 +#: forms.py:224 msgid "Write your announcement:" msgstr "Schreiben Sie ihre Ankündigung:" -#: forms.py:219 +#: forms.py:263 msgid "You are not allowed to create announcements which are only valid in the past." msgstr "Sie dürfen keine Ankündigungen erstellen, die nur für die Vergangenheit gültig sind." -#: forms.py:223 +#: forms.py:267 msgid "The from date and time must be earlier then the until date and time." msgstr "Das Startdatum und die Startzeit müssen vor dem Enddatum und der Endzeit sein." -#: forms.py:232 +#: forms.py:276 msgid "You need at least one recipient." msgstr "Sie benötigen mindestens einen Empfänger." +#: health_checks.py:15 +msgid "There are unresolved data problems." +msgstr "Es gibt keine ungelösten Datenprobleme." + #: menus.py:7 templates/two_factor/core/login.html:6 #: templates/two_factor/core/login.html:10 #: templates/two_factor/core/login.html:86 @@ -157,540 +170,657 @@ msgstr "Einstellungen" msgid "Admin" msgstr "Admin" -#: menus.py:75 models.py:564 templates/core/announcement/list.html:7 +#: menus.py:75 models.py:602 templates/core/announcement/list.html:7 #: templates/core/announcement/list.html:8 msgid "Announcements" msgstr "Ankündigungen" -#: menus.py:86 models.py:92 templates/core/school_term/list.html:8 +#: menus.py:86 models.py:103 templates/core/school_term/list.html:8 #: templates/core/school_term/list.html:9 msgid "School terms" msgstr "Schuljahre" -#: menus.py:97 templates/core/management/data_management.html:6 +#: menus.py:97 templates/core/dashboard_widget/list.html:8 +#: templates/core/dashboard_widget/list.html:9 +msgid "Dashboard widgets" +msgstr "Dashboard-Widgets" + +#: menus.py:108 templates/core/management/data_management.html:6 #: templates/core/management/data_management.html:7 msgid "Data management" msgstr "Datenverwaltung" -#: menus.py:105 templates/core/pages/system_status.html:5 +#: menus.py:116 templates/core/pages/system_status.html:5 #: templates/core/pages/system_status.html:7 msgid "System status" msgstr "Systemstatus" -#: menus.py:116 +#: menus.py:127 msgid "Impersonation" msgstr "Verkleidung" -#: menus.py:124 +#: menus.py:135 msgid "Configuration" msgstr "Konfiguration" -#: menus.py:135 +#: menus.py:146 templates/core/data_check/list.html:9 +#: templates/core/data_check/list.html:10 +msgid "Data checks" +msgstr "Datenprüfungen" + +#: menus.py:152 msgid "Backend Admin" msgstr "Backend-Administration" -#: menus.py:143 +#: menus.py:160 msgid "People" msgstr "Leute" -#: menus.py:168 models.py:727 templates/core/group_type/list.html:8 +#: menus.py:185 models.py:812 templates/core/group_type/list.html:8 #: templates/core/group_type/list.html:9 msgid "Group types" msgstr "Gruppentypen" -#: menus.py:179 +#: menus.py:196 msgid "Persons and accounts" msgstr "Personen und Konten" -#: menus.py:190 +#: menus.py:207 msgid "Groups and child groups" msgstr "Gruppen und Kindgruppen" -#: menus.py:201 models.py:363 templates/core/additional_field/list.html:8 +#: menus.py:218 models.py:385 templates/core/additional_field/list.html:8 #: templates/core/additional_field/list.html:9 msgid "Additional fields" msgstr "Zusätzliche Felder" -#: menus.py:216 templates/core/group/child_groups.html:7 +#: menus.py:233 templates/core/group/child_groups.html:7 #: templates/core/group/child_groups.html:9 msgid "Assign child groups to groups" msgstr "Kindgruppen zu Gruppen zuordnen" -#: mixins.py:406 +#: mixins.py:384 msgid "Linked school term" msgstr "Zugeordnetes Schuljahr" -#: models.py:33 +#: models.py:43 msgid "Boolean (Yes/No)" msgstr "Boolean (Ja/Nein)" -#: models.py:34 +#: models.py:44 msgid "Text (one line)" msgstr "Text (eine Zeile)" -#: models.py:36 +#: models.py:46 msgid "Date and time" msgstr "Datum und Uhrzeit" -#: models.py:37 +#: models.py:47 msgid "Decimal number" msgstr "Dezimalzahl" -#: models.py:38 models.py:146 +#: models.py:48 models.py:157 msgid "E-mail address" msgstr "E-Mail-Adresse" -#: models.py:39 +#: models.py:49 msgid "Integer" msgstr "Ganze Zahl" -#: models.py:40 +#: models.py:50 msgid "IP address" msgstr "IP-Adresse" -#: models.py:41 +#: models.py:51 msgid "Boolean or empty (Yes/No/Neither)" msgstr "Boolean oder leer (Ja/Nein/weder)" -#: models.py:42 +#: models.py:52 msgid "Text (multi-line)" msgstr "Text (mehrzeilig)" -#: models.py:44 +#: models.py:54 msgid "URL / Link" msgstr "URL / Link" -#: models.py:56 models.py:700 +#: models.py:66 models.py:785 msgid "Name" msgstr "Name" -#: models.py:58 +#: models.py:68 msgid "Start date" msgstr "Startdatum" -#: models.py:59 +#: models.py:69 msgid "End date" msgstr "Enddatum" -#: models.py:77 +#: models.py:88 msgid "The start date must be earlier than the end date." msgstr "Das Startdatum muss vor dem Enddatum liegen." -#: models.py:84 +#: models.py:95 msgid "There is already a school term for this time or a part of this time." -msgstr "" -"Es gibt bereits ein Schuljahr für diesen Zeitraum oder einen Teilzeitraum." +msgstr "Es gibt bereits ein Schuljahr für diesen Zeitraum oder einen Teilzeitraum." -#: models.py:104 templates/core/person/accounts.html:36 +#: models.py:115 models.py:744 templates/core/person/accounts.html:41 msgid "Person" msgstr "Person" -#: models.py:107 +#: models.py:118 msgid "Can view address" msgstr "Kann Adresse sehen" -#: models.py:108 +#: models.py:119 msgid "Can view contact details" msgstr "Kann Kontaktdetails sehen" -#: models.py:109 +#: models.py:120 msgid "Can view photo" msgstr "Kann Foto sehen" -#: models.py:110 +#: models.py:121 msgid "Can view persons groups" msgstr "Kann Gruppen einer Person sehen" -#: models.py:111 +#: models.py:122 msgid "Can view personal details" msgstr "Kann persönliche Daten sehen" -#: models.py:116 +#: models.py:127 msgid "female" msgstr "weiblich" -#: models.py:116 +#: models.py:127 msgid "male" msgstr "männlich" -#: models.py:124 +#: models.py:135 msgid "Linked user" msgstr "Verknüpfter Benutzer" -#: models.py:126 +#: models.py:137 msgid "Is person active?" msgstr "Ist die Person aktiv?" -#: models.py:128 +#: models.py:139 msgid "First name" msgstr "Vorname" -#: models.py:129 +#: models.py:140 msgid "Last name" msgstr "Nachname" -#: models.py:131 +#: models.py:142 msgid "Additional name(s)" msgstr "Zusätzliche Namen" -#: models.py:135 models.py:332 +#: models.py:146 models.py:354 msgid "Short name" msgstr "Kurzname" -#: models.py:138 +#: models.py:149 msgid "Street" msgstr "Straße" -#: models.py:139 +#: models.py:150 msgid "Street number" msgstr "Hausnummer" -#: models.py:140 +#: models.py:151 msgid "Postal code" msgstr "Postleitzahl" -#: models.py:141 +#: models.py:152 msgid "Place" msgstr "Ort" -#: models.py:143 +#: models.py:154 msgid "Home phone" msgstr "Festnetz" -#: models.py:144 +#: models.py:155 msgid "Mobile phone" msgstr "Handy" -#: models.py:148 +#: models.py:159 msgid "Date of birth" msgstr "Geburtsdatum" -#: models.py:149 +#: models.py:160 msgid "Sex" msgstr "Geschlecht" -#: models.py:151 +#: models.py:162 msgid "Photo" msgstr "Foto" -#: models.py:155 templates/core/person/full.html:129 +#: models.py:166 templates/core/person/full.html:137 msgid "Guardians / Parents" msgstr "Erziehungsberechtigte / Eltern" -#: models.py:162 +#: models.py:173 msgid "Primary group" msgstr "Primärgruppe" -#: models.py:165 models.py:423 models.py:447 models.py:532 models.py:720 -#: templates/core/person/full.html:112 +#: models.py:176 models.py:461 models.py:485 models.py:570 models.py:805 +#: templates/core/person/full.html:120 msgid "Description" msgstr "Beschreibung" -#: models.py:296 +#: models.py:313 msgid "Title of field" msgstr "Feldtitel" -#: models.py:298 +#: models.py:315 msgid "Type of field" msgstr "Feldtyp" -#: models.py:305 +#: models.py:322 msgid "Addtitional field for groups" msgstr "Zusätzliche Felder für Gruppen" -#: models.py:306 +#: models.py:323 msgid "Addtitional fields for groups" msgstr "Zusätzliche Felder für Gruppen" -#: models.py:318 +#: models.py:337 msgid "Group" msgstr "Gruppe" -#: models.py:320 +#: models.py:340 msgid "Can assign child groups to groups" msgstr "Kann Kindgruppen zu Gruppen zuordnen" -#: models.py:330 +#: models.py:341 +msgid "Can view statistics about group." +msgstr "Kann Statistiken über Gruppen sehen." + +#: models.py:352 msgid "Long name" msgstr "Langname" -#: models.py:340 templates/core/group/full.html:65 +#: models.py:362 templates/core/group/full.html:85 msgid "Members" msgstr "Mitglieder" -#: models.py:343 templates/core/group/full.html:62 +#: models.py:365 templates/core/group/full.html:82 msgid "Owners" msgstr "Leiter/-innen" -#: models.py:350 templates/core/group/full.html:54 +#: models.py:372 templates/core/group/full.html:55 msgid "Parent groups" msgstr "Übergeordnete Gruppen" -#: models.py:358 +#: models.py:380 msgid "Type of group" msgstr "Gruppentyp" -#: models.py:419 +#: models.py:457 msgid "User" msgstr "Benutzer" -#: models.py:422 models.py:446 models.py:531 +#: models.py:460 models.py:484 models.py:569 #: templates/core/announcement/list.html:18 msgid "Title" msgstr "Titel" -#: models.py:425 +#: models.py:463 msgid "Application" msgstr "Anwendung" -#: models.py:431 +#: models.py:469 msgid "Activity" msgstr "Aktivität" -#: models.py:432 +#: models.py:470 msgid "Activities" msgstr "Aktivitäten" -#: models.py:438 +#: models.py:476 msgid "Sender" msgstr "Absender" -#: models.py:443 +#: models.py:481 msgid "Recipient" msgstr "Empfänger" -#: models.py:448 models.py:701 +#: models.py:486 models.py:786 msgid "Link" msgstr "Link" -#: models.py:450 +#: models.py:488 msgid "Read" msgstr "Gelesen" -#: models.py:451 +#: models.py:489 msgid "Sent" msgstr "Versandt" -#: models.py:464 +#: models.py:502 msgid "Notification" msgstr "Benachrichtigung" -#: models.py:465 +#: models.py:503 msgid "Notifications" msgstr "Benachrichtigungen" -#: models.py:533 +#: models.py:571 msgid "Link to detailed view" msgstr "Link zur detaillierten Ansicht" -#: models.py:536 +#: models.py:574 msgid "Date and time from when to show" msgstr "Datum und Uhrzeit des Anzeigestarts" -#: models.py:539 +#: models.py:577 msgid "Date and time until when to show" msgstr "Anzeigezeitraum" -#: models.py:563 +#: models.py:601 msgid "Announcement" msgstr "Ankündigung" -#: models.py:601 +#: models.py:639 msgid "Announcement recipient" msgstr "Empfänger der Ankündigung" -#: models.py:602 +#: models.py:640 msgid "Announcement recipients" msgstr "Empfänger der Ankündigung" -#: models.py:652 +#: models.py:690 msgid "Widget Title" msgstr "Widget-Titel" -#: models.py:653 +#: models.py:691 msgid "Activate Widget" msgstr "Widget aktivieren" -#: models.py:671 +#: models.py:694 +msgid "Size on mobile devices" +msgstr "Größe auf Mobilgeräten" + +#: models.py:695 +msgid "<= 600 px, 12 columns" +msgstr "<= 600 px, 12 Spalten" + +#: models.py:700 +msgid "Size on tablet devices" +msgstr "Größe auf Tablets" + +#: models.py:701 +msgid "> 600 px, 12 columns" +msgstr "> 600px, 12 Spalten" + +#: models.py:706 +msgid "Size on desktop devices" +msgstr "Größe auf Desktopgeräten" + +#: models.py:707 +msgid "> 992 px, 12 columns" +msgstr "> 992 px, 12 Spalten" + +#: models.py:712 +msgid "Size on large desktop devices" +msgstr "Größe auf großen Desktopgeräten" + +#: models.py:713 +msgid "> 1200 px>, 12 columns" +msgstr "> 1200 px, 12 Spalten" + +#: models.py:734 +msgid "Can edit default dashboard" +msgstr "Kann Standarddashboard bearbeiten" + +#: models.py:735 msgid "Dashboard Widget" msgstr "Dashboard-Widget" -#: models.py:672 +#: models.py:736 msgid "Dashboard Widgets" msgstr "Dashboard-Widgets" -#: models.py:678 +#: models.py:741 +msgid "Dashboard widget" +msgstr "Dashboard-Widget" + +#: models.py:746 +msgid "Order" +msgstr "Reihenfolge" + +#: models.py:747 +msgid "Part of the default dashboard" +msgstr "Teil des Standarddashboards" + +#: models.py:755 +msgid "Dashboard widget order" +msgstr "Reihenfolge der Dashboard-Widgets" + +#: models.py:756 +msgid "Dashboard widget orders" +msgstr "Reihenfolgen der Dashboard-Widgets" + +#: models.py:762 msgid "Menu ID" msgstr "Menü-ID" -#: models.py:690 +#: models.py:775 msgid "Custom menu" msgstr "Benutzerdefiniertes Menü" -#: models.py:691 +#: models.py:776 msgid "Custom menus" msgstr "Benutzerdefinierte Menüs" -#: models.py:698 +#: models.py:783 msgid "Menu" msgstr "Menü" -#: models.py:702 +#: models.py:787 msgid "Icon" msgstr "Icon" -#: models.py:708 +#: models.py:793 msgid "Custom menu item" msgstr "Benutzerdefiniertes Menüelement" -#: models.py:709 +#: models.py:794 msgid "Custom menu items" msgstr "Benutzerdefinierte Menüelemente" -#: models.py:719 +#: models.py:804 msgid "Title of type" msgstr "Titel des Typs" -#: models.py:726 templates/core/group/full.html:46 +#: models.py:811 templates/core/group/full.html:47 msgid "Group type" msgstr "Gruppentyp" -#: models.py:736 +#: models.py:821 msgid "Can view system status" msgstr "Kann Systemstatus sehen" -#: models.py:737 +#: models.py:822 msgid "Can link persons to accounts" msgstr "Kann Personen mit Benutzerkonten verknüpfen" -#: models.py:738 +#: models.py:823 msgid "Can manage data" msgstr "Kann Daten verwalten" -#: models.py:739 +#: models.py:824 msgid "Can impersonate" msgstr "Kann sich verkleiden" -#: models.py:740 +#: models.py:825 msgid "Can use search" msgstr "Kann Suche benutzen" -#: models.py:741 +#: models.py:826 msgid "Can change site preferences" msgstr "Kann Konfiguration ändern" -#: models.py:742 +#: models.py:827 msgid "Can change person preferences" msgstr "Kann Einstellungen einer Person verändern" -#: models.py:743 +#: models.py:828 msgid "Can change group preferences" msgstr "Kann Einstellungen einer Gruppe verändern" -#: preferences.py:24 +#: models.py:864 +msgid "Related data check task" +msgstr "Zugehörige Datenprüfungsaufgabe" + +#: models.py:872 +msgid "Issue solved" +msgstr "Problem gelöst" + +#: models.py:873 +msgid "Notification sent" +msgstr "Benachrichtigung gesendet" + +#: models.py:886 +msgid "Data check result" +msgstr "Datenprüfungsergebnis" + +#: models.py:887 +msgid "Data check results" +msgstr "Datenprüfungsergebnisse" + +#: models.py:889 +msgid "Can run data checks" +msgstr "Kann Datenprüfungen ausführen" + +#: models.py:890 +msgid "Can solve data check problems" +msgstr "Kann Datenprüfungsprobleme lösen" + +#: preferences.py:27 msgid "Authentication" msgstr "Authentifizierung" -#: preferences.py:33 +#: preferences.py:28 +msgid "Internationalisation" +msgstr "Internationalisierung" + +#: preferences.py:37 msgid "Site title" msgstr "Seitentitel" -#: preferences.py:42 +#: preferences.py:46 msgid "Site description" msgstr "Seitenbeschreibung" -#: preferences.py:51 +#: preferences.py:55 msgid "Primary colour" msgstr "Primärfarbe" -#: preferences.py:60 +#: preferences.py:64 msgid "Secondary colour" msgstr "Akzentfarbe" -#: preferences.py:68 +#: preferences.py:72 msgid "Logo" msgstr "Logo" -#: preferences.py:76 +#: preferences.py:80 msgid "Favicon" msgstr "Favicon" -#: preferences.py:84 +#: preferences.py:88 msgid "PWA-Icon" msgstr "PWA-Icon" -#: preferences.py:93 +#: preferences.py:97 msgid "Mail out name" msgstr "Ausgangsmailname" -#: preferences.py:102 +#: preferences.py:106 msgid "Mail out address" msgstr "E-Mail-Ausgangsadresse" -#: preferences.py:112 +#: preferences.py:116 msgid "Link to privacy policy" msgstr "Link zur Datenschutzerklärung" -#: preferences.py:122 +#: preferences.py:126 msgid "Link to imprint" msgstr "Link zum Impressum" -#: preferences.py:132 +#: preferences.py:136 msgid "Name format for addressing" msgstr "Namensformat für Anreden" -#: preferences.py:146 +#: preferences.py:150 msgid "Channels to use for notifications" msgstr "Aktivierte Benachrichtungskanäle" -#: preferences.py:156 +#: preferences.py:160 msgid "Regular expression to match primary group, e.g. '^Class .*'" msgstr "Regulärer Ausdruck um Primärgruppen zu finden, z. B. '^Class .*'" -#: preferences.py:165 +#: preferences.py:169 msgid "Field on person to match primary group against" msgstr "Feld um Primärgruppen zu finden" -#: preferences.py:177 +#: preferences.py:181 msgid "Display name of the school" msgstr "Sichtbarer Name der Schule" -#: preferences.py:186 +#: preferences.py:190 msgid "Official name of the school, e.g. as given by supervisory authority" msgstr "Offizieller Name der Schule, wie er z.B. von der Behörde vorgegeben ist" -#: preferences.py:194 +#: preferences.py:198 msgid "Enabled custom authentication backends" msgstr "Benutzerdefinierte Authentifizierungsbackends aktivieren" -#: settings.py:300 +#: preferences.py:211 +msgid "Available languages" +msgstr "Verfügbare Sprachen" + +#: preferences.py:223 +msgid "Send emails if data checks detect problems" +msgstr "E-Mails versenden, wenn Datenprüfungen Probleme finden" + +#: preferences.py:234 +msgid "Email recipients for data checks problem emails" +msgstr "E-Mailempfänger für Datenprüfungsproblem-E-Mails" + +#: preferences.py:245 +msgid "Email recipient groups for data checks problem emails" +msgstr "E-Mail-Empfängergruppen für Datenprüfungsproblem-E-Mails" + +#: settings.py:322 msgid "English" msgstr "Englisch" -#: settings.py:301 +#: settings.py:323 msgid "German" msgstr "Deutsch" -#: settings.py:302 +#: settings.py:324 msgid "French" msgstr "Französisch" -#: settings.py:303 +#: settings.py:325 msgid "Norwegian (bokmål)" msgstr "Norwegisch (bokmål)" #: tables.py:19 templates/core/announcement/list.html:36 -#: templates/core/group/full.html:23 templates/core/person/full.html:22 +#: templates/core/group/full.html:24 templates/core/person/full.html:23 msgid "Edit" msgstr "Bearbeiten" -#: tables.py:21 templates/core/announcement/list.html:22 +#: tables.py:21 tables.py:89 templates/core/announcement/list.html:22 msgid "Actions" msgstr "Aktionen" -#: tables.py:56 tables.py:57 tables.py:71 -#: templates/core/announcement/list.html:42 templates/core/group/full.html:30 -#: templates/core/pages/delete.html:22 templates/core/person/full.html:29 +#: tables.py:56 tables.py:57 tables.py:71 tables.py:87 +#: templates/core/announcement/list.html:42 templates/core/group/full.html:31 +#: templates/core/pages/delete.html:22 templates/core/person/full.html:30 msgid "Delete" msgstr "Löschen" @@ -785,21 +915,21 @@ msgstr "Zusätzliches Feld bearbeiten" msgid "Create additional field" msgstr "Zusätzliches Feld erstellen" -#: templates/core/announcement/form.html:10 -#: templates/core/announcement/form.html:17 +#: templates/core/announcement/form.html:14 +#: templates/core/announcement/form.html:21 msgid "Edit announcement" msgstr "Ankündigung bearbeiten" -#: templates/core/announcement/form.html:12 +#: templates/core/announcement/form.html:16 msgid "Publish announcement" msgstr "Ankündigung veröffentlichen" -#: templates/core/announcement/form.html:19 +#: templates/core/announcement/form.html:23 #: templates/core/announcement/list.html:13 msgid "Publish new announcement" msgstr "Neue Ankündigung veröffentlichen" -#: templates/core/announcement/form.html:30 +#: templates/core/announcement/form.html:34 msgid "Save und publish announcement" msgstr "Ankündigung speichern und veröffentlichen" @@ -839,6 +969,143 @@ msgstr "Datenschutzerklärung" msgid "Powered by AlekSIS" msgstr "Betrieben mit AlekSIS" +#: templates/core/dashboard_widget/create.html:8 +#: templates/core/dashboard_widget/create.html:12 +#, python-format +msgid "Create %(widget)s" +msgstr "%(widget)s erstellen" + +#: templates/core/dashboard_widget/edit.html:8 +#: templates/core/dashboard_widget/edit.html:12 +#, python-format +msgid "Edit %(widget)s" +msgstr "%(widget)s bearbeiten" + +#: templates/core/dashboard_widget/list.html:17 +#, python-format +msgid "Create %(name)s" +msgstr "%(name)s erstellen" + +#: templates/core/dashboard_widget/list.html:25 +#: templates/core/edit_dashboard.html:8 templates/core/edit_dashboard.html:15 +msgid "Edit default dashboard" +msgstr "Standard-Dashboard bearbeiten" + +#: templates/core/data_check/list.html:15 +msgid "Check data again" +msgstr "Daten erneut prüfen" + +#: templates/core/data_check/list.html:22 +msgid "The system detected some problems with your data." +msgstr "Das System hat einige Problemen mit Ihren Daten gefunden." + +#: templates/core/data_check/list.html:23 +msgid "" +"Please go through all data and check whether some extra action is\n" +" needed." +msgstr "" +"Bitte gehen Sie alle Daten durch und prüfen Sie, ob weitere Aktionen\n" +"notwendig sind." + +#: templates/core/data_check/list.html:31 +msgid "Everything is fine." +msgstr "Alles ist gut." + +#: templates/core/data_check/list.html:32 +msgid "The system hasn't detected any problems with your data." +msgstr "Das System hat keine Probleme mit Ihren Daten entdeckt." + +#: templates/core/data_check/list.html:40 +msgid "Detected problems" +msgstr "Gefundene Probleme" + +#: templates/core/data_check/list.html:45 +msgid "Affected object" +msgstr "Betroffenes Objekt" + +#: templates/core/data_check/list.html:46 +msgid "Detected problem" +msgstr "Entdecktes Problem" + +#: templates/core/data_check/list.html:47 +msgid "Show details" +msgstr "Details anzeigen" + +#: templates/core/data_check/list.html:48 +msgid "Options to solve the problem" +msgstr "Optionen, das Problem zu lösen" + +#: templates/core/data_check/list.html:62 +msgid "Show object" +msgstr "Objekt anzeigen" + +#: templates/core/data_check/list.html:84 +msgid "Registered checks" +msgstr "Registrierte Prüfungen" + +#: templates/core/data_check/list.html:88 +msgid "" +"\n" +" The system will check for the following problems:\n" +" " +msgstr "" +"\n" +" Das System wird nach folgenden Problemen suchen:\n" +" " + +#: templates/core/edit_dashboard.html:6 templates/core/edit_dashboard.html:13 +#: templates/core/index.html:14 +msgid "Edit dashboard" +msgstr "Dashboard bearbeiten" + +#: templates/core/edit_dashboard.html:24 +msgid "" +"\n" +" On this page you can arrange your personal dashboard. You can drag any items from \"Available widgets\" to \"Your\n" +" Dashboard\" or change the order by moving the widgets. After you have finished, please don't forget to click on\n" +" \"Save\".\n" +" " +msgstr "" +"\n" +" Auf dieser Seite können Sie Ihr persönliches Dashboard " +"zusammenstallen. Sie können beliebige Elemente von den \"Verfügbaren " +"Widgets\" \n" +"in \"Ihr Dashboard\" ziehen oder die Reihenfolge verändern, indem Sie die " +"Widgets bewegen. Wenn Sie fertig sind, vergessen Sie bitte nicht, \n" +"auf \"Speichern\" zu drücken.\n" +" " + +#: templates/core/edit_dashboard.html:30 +msgid "" +"\n" +" On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own\n" +" dashboard. You can drag any items from \"Available widgets\" to \"Default Dashboard\" or change the order\n" +" by moving the widgets. After you have finished, please don't forget to click on \"Save\".\n" +" " +msgstr "" +"\n" +" Auf dieser Seite können Sie Ihr das Standard-Dashboard " +"zusammenstallen, welches angezeigt wird, wenn ein Nutzer kein eigenes " +"definiert. \n" +"Sie können beliebige Elemente von den \"Verfügbaren Widgets\" in \"Standard-" +"Dashboard\" ziehen oder die Reihenfolge verändern, indem Sie die Widgets " +"bewegen. \n" +"Wenn Sie fertig sind, vergessen Sie bitte nicht, auf \"Speichern\" zu " +"drücken.\n" +" " + +#: templates/core/edit_dashboard.html:48 +msgid "Available widgets" +msgstr "Verfügbare Widgets" + +#: templates/core/edit_dashboard.html:57 +msgid "Your dashboard" +msgstr "Ihr Dashboard" + +#: templates/core/edit_dashboard.html:59 +msgid "Default dashboard" +msgstr "Standard-Dashboard" + #: templates/core/group/child_groups.html:18 msgid "" "\n" @@ -923,14 +1190,38 @@ msgstr "Speichern" msgid "Save and next" msgstr "Speichern und weiter" -#: templates/core/group/edit.html:6 templates/core/group/edit.html:7 +#: templates/core/group/edit.html:11 templates/core/group/edit.html:12 msgid "Edit group" msgstr "Gruppe editieren" -#: templates/core/group/full.html:37 templates/core/person/full.html:36 +#: templates/core/group/full.html:38 templates/core/person/full.html:37 msgid "Change preferences" msgstr "Einstellungen ändern" +#: templates/core/group/full.html:64 +msgid "Statistics" +msgstr "Statistiken" + +#: templates/core/group/full.html:67 +msgid "Count of members" +msgstr "Anzahl der Mitglieder" + +#: templates/core/group/full.html:71 +msgid "Average age" +msgstr "Durchschnittsalter" + +#: templates/core/group/full.html:76 +msgid "Age range" +msgstr "Altersbereich" + +#: templates/core/group/full.html:76 +msgid "years to" +msgstr "Jahre bis" + +#: templates/core/group/full.html:76 +msgid "years " +msgstr "Jahre " + #: templates/core/group/list.html:14 msgid "Create group" msgstr "Gruppe erstellen" @@ -959,23 +1250,37 @@ msgstr "Gruppentyp erstellen" msgid "Home" msgstr "Startseite" -#: templates/core/index.html:42 +#: templates/core/index.html:50 +msgid "" +"\n" +" You didn't customise your dashboard so that you see the system default. Please click on \"Edit dashboard\" to\n" +" customise your personal dashboard.\n" +" " +msgstr "" +"\n" +" Sie haben Ihr Dashboard nicht angepasst, sodass Sie das Standard-" +"Dashboard sehen.\n" +"Bitte klicken Sie auf \"Dashboard bearbeiten\", um Ihr persönliches " +"Dashboard anzupassen.\n" +" " + +#: templates/core/index.html:59 msgid "Last activities" msgstr "Letzte Aktivitäten" -#: templates/core/index.html:60 +#: templates/core/index.html:77 msgid "No activities available yet." msgstr "Aktuell keine Aktivitäten verfügbar." -#: templates/core/index.html:65 +#: templates/core/index.html:82 msgid "Recent notifications" msgstr "Letzte Benachrichtigungen" -#: templates/core/index.html:81 +#: templates/core/index.html:98 msgid "More information →" msgstr "Mehr Informationen →" -#: templates/core/index.html:88 +#: templates/core/index.html:105 msgid "No notifications available yet." msgstr "Aktuell keine Benachrichtigungen verfügbar." @@ -1070,28 +1375,6 @@ msgstr "" " Möchten Sie wirklich %(object_name)s \"%(object)s\" löschen?\n" " " -#: templates/core/pages/offline.html:6 -msgid "" -"No internet\n" -" connection." -msgstr "" -"Keine\n" -" Internetverbindung." - -#: templates/core/pages/offline.html:10 -msgid "" -"\n" -" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" -" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" -" administrators:\n" -" " -msgstr "" -"\n" -" Es ist ein Fehler beim Aufrufen der Seite aufgetreten. Eventuell haben Sie keine Internetverbindung. Bitte prüfen Sie, ob WLAN oder mobile Daten aktiv sind, \n" -" und probieren Sie es erneut. Wenn Sie der Meinung sind, dass Sie mit dem Internet verbunden sind, kontaktieren Sie bitte einen Ihrer \n" -" Systemadministratoren:\n" -" " - #: templates/core/pages/progress.html:27 msgid "" "\n" @@ -1099,8 +1382,7 @@ msgid "" " " msgstr "" "\n" -" Ohne aktiviertes JavaScript kann der Fortschritt leider nicht " -"aktualisiert werden.\n" +" Ohne aktiviertes JavaScript kann der Fortschritt leider nicht aktualisiert werden.\n" " " #: templates/core/pages/progress.html:47 @@ -1233,46 +1515,19 @@ msgstr "" " Gültig von %(from)s – %(until)s\n" " " -#: templates/core/partials/crud_events.html:10 -#, python-format -msgid "" -"\n" -" Created by %(person)s\n" -" " -msgstr "" -"\n" -" Erstellt von %(person)s\n" -" " - -#: templates/core/partials/crud_events.html:14 -#: templates/core/partials/crud_events.html:22 -#: templates/core/partials/crud_events.html:26 -#, python-format -msgid "" -"\n" -" Updated by %(person)s\n" -" " -msgstr "" -"\n" -" Aktualisiert von %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Changed by" +msgstr "Verändert von" -#: templates/core/partials/crud_events.html:18 -#, python-format -msgid "" -"\n" -" Deleted by %(person)s\n" -" " -msgstr "" -"\n" -" Gelöscht von %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Unknown" +msgstr "Unbekannt" -#: templates/core/partials/language_form.html:16 +#: templates/core/partials/language_form.html:15 msgid "Language" msgstr "Sprache" -#: templates/core/partials/language_form.html:28 +#: templates/core/partials/language_form.html:27 msgid "Select language" msgstr "Sprache auswählen" @@ -1302,11 +1557,12 @@ msgstr "" " die Verwaltenden von AlekSIS an Ihrer Schule.\n" " " -#: templates/core/person/accounts.html:7 templates/core/person/accounts.html:9 +#: templates/core/person/accounts.html:12 +#: templates/core/person/accounts.html:14 msgid "Link persons to accounts" msgstr "Personen mit Benutzerkonten verknüpfen" -#: templates/core/person/accounts.html:16 +#: templates/core/person/accounts.html:21 msgid "" "\n" " You can use this form to assign user accounts to persons. Use the\n" @@ -1322,28 +1578,32 @@ msgstr "" " eingegebenen Benutzernamen und kopiert alle anderen Daten der Person.\n" " " -#: templates/core/person/accounts.html:31 -#: templates/core/person/accounts.html:55 +#: templates/core/person/accounts.html:36 +#: templates/core/person/accounts.html:60 msgid "Update" msgstr "Aktualisieren" -#: templates/core/person/accounts.html:37 +#: templates/core/person/accounts.html:42 msgid "Existing account" msgstr "Existierendes Konto" -#: templates/core/person/accounts.html:38 +#: templates/core/person/accounts.html:43 msgid "New account" msgstr "Neues Konto" -#: templates/core/person/edit.html:11 templates/core/person/edit.html:12 +#: templates/core/person/edit.html:12 templates/core/person/edit.html:13 msgid "Edit person" msgstr "Person editieren" -#: templates/core/person/full.html:42 +#: templates/core/person/full.html:44 +msgid "Impersonate" +msgstr "Verkleiden" + +#: templates/core/person/full.html:50 msgid "Contact details" msgstr "Kontaktdetails" -#: templates/core/person/full.html:122 +#: templates/core/person/full.html:130 msgid "Children" msgstr "Kinder" @@ -1387,14 +1647,36 @@ msgstr "Einstellungen für %(instance)s" msgid "Save preferences" msgstr "Einstellungen speichern" -#: templates/dynamic_preferences/sections.html:7 -msgid "All" -msgstr "Alle" - #: templates/impersonate/list_users.html:8 msgid "Impersonate user" msgstr "Als Benutzer verkleiden" +#: templates/offline.html:5 +msgid "Network error" +msgstr "Netzwerkfehler" + +#: templates/offline.html:8 +msgid "" +"No internet\n" +" connection." +msgstr "" +"Keine\n" +" Internetverbindung." + +#: templates/offline.html:12 +msgid "" +"\n" +" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" +" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" +" administrators:\n" +" " +msgstr "" +"\n" +" Es ist ein Fehler beim Aufrufen der Seite aufgetreten. Eventuell haben Sie keine Internetverbindung. Bitte prüfen Sie, ob WLAN oder mobile Daten aktiv sind, \n" +" und probieren Sie es erneut. Wenn Sie der Meinung sind, dass Sie mit dem Internet verbunden sind, kontaktieren Sie bitte einen Ihrer \n" +" Systemadministratoren:\n" +" " + #: templates/search/search.html:8 msgid "Global Search" msgstr "Globale Suche" @@ -1415,6 +1697,47 @@ msgstr "Es konnten keine Suchergebnisse zu Ihrem Suchausdruck gefunden werden." msgid "Please enter a search term above." msgstr "Bitte geben Sie einen Suchausdruck ein." +#: templates/templated_email/data_checks.email:4 +msgid "The system detected some new problems with your data." +msgstr "Das System hat einige neue Probleme mit Ihren Daten entdeckt." + +#: templates/templated_email/data_checks.email:8 +#: templates/templated_email/data_checks.email:24 +msgid "Hello," +msgstr "Hallo," + +#: templates/templated_email/data_checks.email:10 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" +"\n" +" das System hat einige neue Probleme mit Ihren Daten entdeckt.\n" +"Bitte nehmen Sie sich etwas Zeit, diese zu überprüfen und sie zu lösen oder als ignoriert zu markieren.\n" +" " + +#: templates/templated_email/data_checks.email:26 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" +"\n" +" das System hat einige neue Probleme mit Ihren Daten entdeckt.\n" +"Bitte nehmen Sie sich etwas Zeit, diese zu überprüfen und sie zu lösen oder als ignoriert zu markieren.\n" +" " + +#: templates/templated_email/data_checks.email:34 +msgid "Problem description" +msgstr "Problembeschreibung" + +#: templates/templated_email/data_checks.email:35 +msgid "Count of objects with new problems" +msgstr "Anzahl der Objekte mit neuen Problemen" + #: templates/templated_email/notification.email:3 msgid "New notification for" msgstr "Neue Benachrichtigung für" @@ -1897,62 +2220,126 @@ msgstr "E-Mail" msgid "SMS" msgstr "SMS" -#: views.py:122 +#: views.py:141 msgid "The school term has been created." msgstr "Das Schuljahr wurde erstellt." -#: views.py:133 +#: views.py:153 msgid "The school term has been saved." msgstr "Das Schuljahr wurde gespeichert." -#: views.py:273 +#: views.py:298 msgid "The child groups were successfully saved." msgstr "Die Untergruppen wurden gespeichert." -#: views.py:309 +#: views.py:336 msgid "The person has been saved." msgstr "Die Person wurde gespeichert." -#: views.py:346 +#: views.py:375 msgid "The group has been saved." msgstr "Die Gruppe wurde gespeichert." -#: views.py:434 +#: views.py:467 msgid "The announcement has been saved." msgstr "Die Ankündigung wurde gespeichert." -#: views.py:450 +#: views.py:483 msgid "The announcement has been deleted." msgstr "Ankündigung wurde gelöscht." -#: views.py:521 +#: views.py:562 msgid "The preferences have been saved successfully." msgstr "Die Einstellungen wurde gespeichert." -#: views.py:544 +#: views.py:586 msgid "The person has been deleted." msgstr "Die Person wurde gelöscht." -#: views.py:557 +#: views.py:600 msgid "The group has been deleted." msgstr "Die Gruppe wurde gelöscht." -#: views.py:588 +#: views.py:632 msgid "The additional_field has been saved." msgstr "Das zusätzliche Feld wurde gespeichert." -#: views.py:622 +#: views.py:666 msgid "The additional field has been deleted." msgstr "Das zusätzliche Feld wurde gelöscht." -#: views.py:646 +#: views.py:691 msgid "The group type has been saved." msgstr "Der Gruppentyp wurde gespeichert." -#: views.py:676 +#: views.py:721 msgid "The group type has been deleted." msgstr "Der Gruppentyp wurde gelöscht." +#: views.py:749 +msgid "The data check has been started. Please note that it may take a while before you are able to fetch the data on this page." +msgstr "Die Datenüberprüfung wurde gestartet. Bitte beachten Sie, dass es eine Weile dauern kann, bevor Sie auf dieser Seite Ergebnisse abrufen können." + +#: views.py:754 +msgid "The data check has finished." +msgstr "Die Datenüberprüfung wurde beendet." + +#: views.py:769 +#, python-brace-format +msgid "The solve option '{solve_option_obj.verbose_name}' " +msgstr "Die Lösungsoption \"{solve_option_obj.verbose_name}\" " + +#: views.py:811 +msgid "The dashboard widget has been saved." +msgstr "Das Dashboard-Widget wurde gespeichert." + +#: views.py:841 +msgid "The dashboard widget has been created." +msgstr "Das Dashboard-Widget wurde erstellt." + +#: views.py:851 +msgid "The dashboard widget has been deleted." +msgstr "Das Dashboard-Widget wurde gelöscht." + +#: views.py:914 +msgid "Your dashboard configuration has been saved successfully." +msgstr "Ihre Dashboardkonfiguration wurde erfolgreich gespeichert." + +#: views.py:916 +msgid "The configuration of the default dashboard has been saved successfully." +msgstr "" +"Die Konfiguration des Standard-Dashboardes wurde erfolgreich gespeichert." + +#~ msgid "All" +#~ msgstr "Alle" + +#~ msgid "" +#~ "\n" +#~ " Created by %(person)s\n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ " Erstellt von %(person)s\n" +#~ " " + +#~ msgid "" +#~ "\n" +#~ " Updated by %(person)s\n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ " Aktualisiert von %(person)s\n" +#~ " " + +#~ msgid "" +#~ "\n" +#~ " Deleted by %(person)s\n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ " Gelöscht von %(person)s\n" +#~ " " + #~ msgid "Dear" #~ msgstr "Sehr geehrter" diff --git a/aleksis/core/locale/de_DE/LC_MESSAGES/djangojs.po b/aleksis/core/locale/de_DE/LC_MESSAGES/djangojs.po index 9dbef6e6f74f842b0e8f25e389edf1a852cd8d99..ea24fc3ff92afc211898fb8b426dccb97fce56ed 100644 --- a/aleksis/core/locale/de_DE/LC_MESSAGES/djangojs.po +++ b/aleksis/core/locale/de_DE/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,14 +17,18 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: static/js/main.js:21 +#: static/js/main.js:15 msgid "Today" msgstr "" -#: static/js/main.js:22 +#: static/js/main.js:16 msgid "Cancel" msgstr "" -#: static/js/main.js:23 +#: static/js/main.js:17 msgid "OK" msgstr "" + +#: static/js/main.js:118 +msgid "This page may contain outdated information since there is no internet connection." +msgstr "" diff --git a/aleksis/core/locale/fr/LC_MESSAGES/django.po b/aleksis/core/locale/fr/LC_MESSAGES/django.po index a8556727d98887123c05abb853a0bd46e829ead4..0938d0f9e68b50c905b6e0c232ade944ee0907a6 100644 --- a/aleksis/core/locale/fr/LC_MESSAGES/django.po +++ b/aleksis/core/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: AlekSIS (School Information System) 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: 2020-04-27 13:03+0000\n" "Last-Translator: Marlene Grundey <grundema@katharineum.de>\n" "Language-Team: French <https://translate.edugit.org/projects/aleksis/aleksis/fr/>\n" @@ -18,6 +18,15 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.0.1\n" +#: data_checks.py:53 +msgid "Ignore problem" +msgstr "" + +#: data_checks.py:174 +#, python-brace-format +msgid "Solve option '{solve_option_obj.verbose_name}' " +msgstr "" + #: filters.py:37 templates/core/base.html:77 templates/core/group/list.html:20 #: templates/core/person/list.html:24 templates/search/search.html:7 #: templates/search/search.html:22 @@ -34,104 +43,108 @@ msgstr "" msgid "Search by contact details" msgstr "Détails de contact" -#: forms.py:46 +#: forms.py:54 msgid "You cannot set a new username when also selecting an existing user." msgstr "" -#: forms.py:50 +#: forms.py:58 msgid "This username is already in use." msgstr "" -#: forms.py:74 +#: forms.py:82 msgid "Base data" msgstr "" -#: forms.py:80 +#: forms.py:88 msgid "Address" msgstr "" -#: forms.py:81 +#: forms.py:89 #, fuzzy #| msgid "Contact details" msgid "Contact data" msgstr "Détails de contact" -#: forms.py:83 +#: forms.py:91 #, fuzzy #| msgid "Contact details" msgid "Advanced personal data" msgstr "Détails de contact" -#: forms.py:114 +#: forms.py:134 msgid "New user" msgstr "" -#: forms.py:114 +#: forms.py:134 msgid "Create a new account" msgstr "" -#: forms.py:126 models.py:91 +#: forms.py:146 models.py:102 msgid "School term" msgstr "" -#: forms.py:127 +#: forms.py:147 #, fuzzy #| msgid "Contact details" msgid "Common data" msgstr "Détails de contact" -#: forms.py:128 forms.py:170 menus.py:152 models.py:105 +#: forms.py:148 forms.py:197 menus.py:169 models.py:116 #: templates/core/person/list.html:8 templates/core/person/list.html:9 #, fuzzy #| msgid "Person" msgid "Persons" msgstr "Personne" -#: forms.py:129 +#: forms.py:149 #, fuzzy #| msgid "Contact details" msgid "Additional data" msgstr "Détails de contact" -#: forms.py:163 forms.py:166 models.py:35 +#: forms.py:189 forms.py:192 models.py:45 msgid "Date" msgstr "Date" -#: forms.py:164 forms.py:167 models.py:43 +#: forms.py:190 forms.py:193 models.py:53 msgid "Time" msgstr "" -#: forms.py:172 menus.py:160 models.py:319 templates/core/group/list.html:8 -#: templates/core/group/list.html:9 templates/core/person/full.html:136 +#: forms.py:210 menus.py:177 models.py:338 templates/core/group/list.html:8 +#: templates/core/group/list.html:9 templates/core/person/full.html:144 #, fuzzy #| msgid "Group" msgid "Groups" msgstr "Groupe" -#: forms.py:176 +#: forms.py:220 msgid "From when until when should the announcement be displayed?" msgstr "" -#: forms.py:179 +#: forms.py:223 msgid "Who should see the announcement?" msgstr "" -#: forms.py:180 +#: forms.py:224 msgid "Write your announcement:" msgstr "" -#: forms.py:219 +#: forms.py:263 msgid "You are not allowed to create announcements which are only valid in the past." msgstr "" -#: forms.py:223 +#: forms.py:267 msgid "The from date and time must be earlier then the until date and time." msgstr "" -#: forms.py:232 +#: forms.py:276 msgid "You need at least one recipient." msgstr "" +#: health_checks.py:15 +msgid "There are unresolved data problems." +msgstr "" + #: menus.py:7 templates/two_factor/core/login.html:6 #: templates/two_factor/core/login.html:10 #: templates/two_factor/core/login.html:86 @@ -170,569 +183,689 @@ msgstr "" msgid "Admin" msgstr "" -#: menus.py:75 models.py:564 templates/core/announcement/list.html:7 +#: menus.py:75 models.py:602 templates/core/announcement/list.html:7 #: templates/core/announcement/list.html:8 msgid "Announcements" msgstr "" -#: menus.py:86 models.py:92 templates/core/school_term/list.html:8 +#: menus.py:86 models.py:103 templates/core/school_term/list.html:8 #: templates/core/school_term/list.html:9 msgid "School terms" msgstr "" -#: menus.py:97 templates/core/management/data_management.html:6 +#: menus.py:97 templates/core/dashboard_widget/list.html:8 +#: templates/core/dashboard_widget/list.html:9 +msgid "Dashboard widgets" +msgstr "" + +#: menus.py:108 templates/core/management/data_management.html:6 #: templates/core/management/data_management.html:7 msgid "Data management" msgstr "" -#: menus.py:105 templates/core/pages/system_status.html:5 +#: menus.py:116 templates/core/pages/system_status.html:5 #: templates/core/pages/system_status.html:7 msgid "System status" msgstr "" -#: menus.py:116 +#: menus.py:127 msgid "Impersonation" msgstr "" -#: menus.py:124 +#: menus.py:135 msgid "Configuration" msgstr "" -#: menus.py:135 +#: menus.py:146 templates/core/data_check/list.html:9 +#: templates/core/data_check/list.html:10 +msgid "Data checks" +msgstr "" + +#: menus.py:152 msgid "Backend Admin" msgstr "" -#: menus.py:143 +#: menus.py:160 msgid "People" msgstr "" -#: menus.py:168 models.py:727 templates/core/group_type/list.html:8 +#: menus.py:185 models.py:812 templates/core/group_type/list.html:8 #: templates/core/group_type/list.html:9 #, fuzzy #| msgid "Group" msgid "Group types" msgstr "Groupe" -#: menus.py:179 +#: menus.py:196 msgid "Persons and accounts" msgstr "" -#: menus.py:190 +#: menus.py:207 msgid "Groups and child groups" msgstr "" -#: menus.py:201 models.py:363 templates/core/additional_field/list.html:8 +#: menus.py:218 models.py:385 templates/core/additional_field/list.html:8 #: templates/core/additional_field/list.html:9 msgid "Additional fields" msgstr "" -#: menus.py:216 templates/core/group/child_groups.html:7 +#: menus.py:233 templates/core/group/child_groups.html:7 #: templates/core/group/child_groups.html:9 msgid "Assign child groups to groups" msgstr "" -#: mixins.py:406 +#: mixins.py:384 msgid "Linked school term" msgstr "" -#: models.py:33 +#: models.py:43 msgid "Boolean (Yes/No)" msgstr "" -#: models.py:34 +#: models.py:44 msgid "Text (one line)" msgstr "" -#: models.py:36 +#: models.py:46 msgid "Date and time" msgstr "" -#: models.py:37 +#: models.py:47 msgid "Decimal number" msgstr "" -#: models.py:38 models.py:146 +#: models.py:48 models.py:157 msgid "E-mail address" msgstr "" -#: models.py:39 +#: models.py:49 msgid "Integer" msgstr "" -#: models.py:40 +#: models.py:50 msgid "IP address" msgstr "" -#: models.py:41 +#: models.py:51 msgid "Boolean or empty (Yes/No/Neither)" msgstr "" -#: models.py:42 +#: models.py:52 msgid "Text (multi-line)" msgstr "" -#: models.py:44 +#: models.py:54 msgid "URL / Link" msgstr "" -#: models.py:56 models.py:700 +#: models.py:66 models.py:785 msgid "Name" msgstr "" -#: models.py:58 +#: models.py:68 #, fuzzy #| msgid "Contact details" msgid "Start date" msgstr "Détails de contact" -#: models.py:59 +#: models.py:69 msgid "End date" msgstr "" -#: models.py:77 +#: models.py:88 msgid "The start date must be earlier than the end date." msgstr "" -#: models.py:84 +#: models.py:95 msgid "There is already a school term for this time or a part of this time." msgstr "" -#: models.py:104 templates/core/person/accounts.html:36 +#: models.py:115 models.py:744 templates/core/person/accounts.html:41 msgid "Person" msgstr "Personne" -#: models.py:107 +#: models.py:118 #, fuzzy #| msgid "Contact details" msgid "Can view address" msgstr "Détails de contact" -#: models.py:108 +#: models.py:119 #, fuzzy #| msgid "Contact details" msgid "Can view contact details" msgstr "Détails de contact" -#: models.py:109 +#: models.py:120 #, fuzzy #| msgid "Contact details" msgid "Can view photo" msgstr "Détails de contact" -#: models.py:110 +#: models.py:121 #, fuzzy #| msgid "Contact details" msgid "Can view persons groups" msgstr "Détails de contact" -#: models.py:111 +#: models.py:122 #, fuzzy #| msgid "Contact details" msgid "Can view personal details" msgstr "Détails de contact" -#: models.py:116 +#: models.py:127 msgid "female" msgstr "" -#: models.py:116 +#: models.py:127 msgid "male" msgstr "" -#: models.py:124 +#: models.py:135 msgid "Linked user" msgstr "" -#: models.py:126 +#: models.py:137 msgid "Is person active?" msgstr "" -#: models.py:128 +#: models.py:139 msgid "First name" msgstr "Prénom" -#: models.py:129 +#: models.py:140 msgid "Last name" msgstr "Nom de famille" -#: models.py:131 +#: models.py:142 msgid "Additional name(s)" msgstr "" -#: models.py:135 models.py:332 +#: models.py:146 models.py:354 #, fuzzy #| msgid "First name" msgid "Short name" msgstr "Prénom" -#: models.py:138 +#: models.py:149 msgid "Street" msgstr "" -#: models.py:139 +#: models.py:150 msgid "Street number" msgstr "" -#: models.py:140 +#: models.py:151 msgid "Postal code" msgstr "" -#: models.py:141 +#: models.py:152 msgid "Place" msgstr "" -#: models.py:143 +#: models.py:154 msgid "Home phone" msgstr "" -#: models.py:144 +#: models.py:155 msgid "Mobile phone" msgstr "" -#: models.py:148 +#: models.py:159 msgid "Date of birth" msgstr "Date d'anniversaire" -#: models.py:149 +#: models.py:160 msgid "Sex" msgstr "Sexe" -#: models.py:151 +#: models.py:162 msgid "Photo" msgstr "" -#: models.py:155 templates/core/person/full.html:129 +#: models.py:166 templates/core/person/full.html:137 msgid "Guardians / Parents" msgstr "" -#: models.py:162 +#: models.py:173 msgid "Primary group" msgstr "" -#: models.py:165 models.py:423 models.py:447 models.py:532 models.py:720 -#: templates/core/person/full.html:112 +#: models.py:176 models.py:461 models.py:485 models.py:570 models.py:805 +#: templates/core/person/full.html:120 msgid "Description" msgstr "Description" -#: models.py:296 +#: models.py:313 msgid "Title of field" msgstr "" -#: models.py:298 +#: models.py:315 msgid "Type of field" msgstr "" -#: models.py:305 +#: models.py:322 msgid "Addtitional field for groups" msgstr "" -#: models.py:306 +#: models.py:323 msgid "Addtitional fields for groups" msgstr "" -#: models.py:318 +#: models.py:337 msgid "Group" msgstr "Groupe" -#: models.py:320 +#: models.py:340 msgid "Can assign child groups to groups" msgstr "" -#: models.py:330 +#: models.py:341 +#, fuzzy +#| msgid "Contact details" +msgid "Can view statistics about group." +msgstr "Détails de contact" + +#: models.py:352 #, fuzzy #| msgid "Last name" msgid "Long name" msgstr "Nom de famille" -#: models.py:340 templates/core/group/full.html:65 +#: models.py:362 templates/core/group/full.html:85 msgid "Members" msgstr "" -#: models.py:343 templates/core/group/full.html:62 +#: models.py:365 templates/core/group/full.html:82 msgid "Owners" msgstr "Propriétaires" -#: models.py:350 templates/core/group/full.html:54 +#: models.py:372 templates/core/group/full.html:55 msgid "Parent groups" msgstr "" -#: models.py:358 +#: models.py:380 msgid "Type of group" msgstr "" -#: models.py:419 +#: models.py:457 msgid "User" msgstr "" -#: models.py:422 models.py:446 models.py:531 +#: models.py:460 models.py:484 models.py:569 #: templates/core/announcement/list.html:18 msgid "Title" msgstr "" -#: models.py:425 +#: models.py:463 msgid "Application" msgstr "" -#: models.py:431 +#: models.py:469 msgid "Activity" msgstr "" -#: models.py:432 +#: models.py:470 msgid "Activities" msgstr "" -#: models.py:438 +#: models.py:476 msgid "Sender" msgstr "" -#: models.py:443 +#: models.py:481 msgid "Recipient" msgstr "" -#: models.py:448 models.py:701 +#: models.py:486 models.py:786 msgid "Link" msgstr "" -#: models.py:450 +#: models.py:488 msgid "Read" msgstr "" -#: models.py:451 +#: models.py:489 msgid "Sent" msgstr "" -#: models.py:464 +#: models.py:502 msgid "Notification" msgstr "" -#: models.py:465 +#: models.py:503 msgid "Notifications" msgstr "" -#: models.py:533 +#: models.py:571 msgid "Link to detailed view" msgstr "" -#: models.py:536 +#: models.py:574 msgid "Date and time from when to show" msgstr "" -#: models.py:539 +#: models.py:577 msgid "Date and time until when to show" msgstr "" -#: models.py:563 +#: models.py:601 msgid "Announcement" msgstr "" -#: models.py:601 +#: models.py:639 msgid "Announcement recipient" msgstr "" -#: models.py:602 +#: models.py:640 msgid "Announcement recipients" msgstr "" -#: models.py:652 +#: models.py:690 msgid "Widget Title" msgstr "" -#: models.py:653 +#: models.py:691 msgid "Activate Widget" msgstr "" -#: models.py:671 +#: models.py:694 +msgid "Size on mobile devices" +msgstr "" + +#: models.py:695 +msgid "<= 600 px, 12 columns" +msgstr "" + +#: models.py:700 +msgid "Size on tablet devices" +msgstr "" + +#: models.py:701 +msgid "> 600 px, 12 columns" +msgstr "" + +#: models.py:706 +msgid "Size on desktop devices" +msgstr "" + +#: models.py:707 +msgid "> 992 px, 12 columns" +msgstr "" + +#: models.py:712 +msgid "Size on large desktop devices" +msgstr "" + +#: models.py:713 +msgid "> 1200 px>, 12 columns" +msgstr "" + +#: models.py:734 +msgid "Can edit default dashboard" +msgstr "" + +#: models.py:735 msgid "Dashboard Widget" msgstr "" -#: models.py:672 +#: models.py:736 msgid "Dashboard Widgets" msgstr "" -#: models.py:678 +#: models.py:741 +msgid "Dashboard widget" +msgstr "" + +#: models.py:746 +msgid "Order" +msgstr "" + +#: models.py:747 +msgid "Part of the default dashboard" +msgstr "" + +#: models.py:755 +msgid "Dashboard widget order" +msgstr "" + +#: models.py:756 +msgid "Dashboard widget orders" +msgstr "" + +#: models.py:762 msgid "Menu ID" msgstr "" -#: models.py:690 +#: models.py:775 msgid "Custom menu" msgstr "" -#: models.py:691 +#: models.py:776 msgid "Custom menus" msgstr "" -#: models.py:698 +#: models.py:783 msgid "Menu" msgstr "" -#: models.py:702 +#: models.py:787 msgid "Icon" msgstr "" -#: models.py:708 +#: models.py:793 msgid "Custom menu item" msgstr "" -#: models.py:709 +#: models.py:794 msgid "Custom menu items" msgstr "" -#: models.py:719 +#: models.py:804 msgid "Title of type" msgstr "" -#: models.py:726 templates/core/group/full.html:46 +#: models.py:811 templates/core/group/full.html:47 #, fuzzy #| msgid "Group" msgid "Group type" msgstr "Groupe" -#: models.py:736 +#: models.py:821 #, fuzzy #| msgid "Contact details" msgid "Can view system status" msgstr "Détails de contact" -#: models.py:737 +#: models.py:822 #, fuzzy #| msgid "Contact details" msgid "Can link persons to accounts" msgstr "Détails de contact" -#: models.py:738 +#: models.py:823 msgid "Can manage data" msgstr "" -#: models.py:739 +#: models.py:824 #, fuzzy #| msgid "Contact details" msgid "Can impersonate" msgstr "Détails de contact" -#: models.py:740 +#: models.py:825 msgid "Can use search" msgstr "" -#: models.py:741 +#: models.py:826 msgid "Can change site preferences" msgstr "" -#: models.py:742 +#: models.py:827 msgid "Can change person preferences" msgstr "" -#: models.py:743 +#: models.py:828 msgid "Can change group preferences" msgstr "" -#: preferences.py:24 +#: models.py:864 +msgid "Related data check task" +msgstr "" + +#: models.py:872 +msgid "Issue solved" +msgstr "" + +#: models.py:873 +msgid "Notification sent" +msgstr "" + +#: models.py:886 +msgid "Data check result" +msgstr "" + +#: models.py:887 +msgid "Data check results" +msgstr "" + +#: models.py:889 +msgid "Can run data checks" +msgstr "" + +#: models.py:890 +msgid "Can solve data check problems" +msgstr "" + +#: preferences.py:27 msgid "Authentication" msgstr "" -#: preferences.py:33 +#: preferences.py:28 +msgid "Internationalisation" +msgstr "" + +#: preferences.py:37 msgid "Site title" msgstr "" -#: preferences.py:42 +#: preferences.py:46 #, fuzzy #| msgid "Description" msgid "Site description" msgstr "Description" -#: preferences.py:51 +#: preferences.py:55 msgid "Primary colour" msgstr "" -#: preferences.py:60 +#: preferences.py:64 msgid "Secondary colour" msgstr "" -#: preferences.py:68 +#: preferences.py:72 msgid "Logo" msgstr "" -#: preferences.py:76 +#: preferences.py:80 msgid "Favicon" msgstr "" -#: preferences.py:84 +#: preferences.py:88 msgid "PWA-Icon" msgstr "" -#: preferences.py:93 +#: preferences.py:97 #, fuzzy #| msgid "Last name" msgid "Mail out name" msgstr "Nom de famille" -#: preferences.py:102 +#: preferences.py:106 msgid "Mail out address" msgstr "" -#: preferences.py:112 +#: preferences.py:116 msgid "Link to privacy policy" msgstr "" -#: preferences.py:122 +#: preferences.py:126 msgid "Link to imprint" msgstr "" -#: preferences.py:132 +#: preferences.py:136 msgid "Name format for addressing" msgstr "" -#: preferences.py:146 +#: preferences.py:150 msgid "Channels to use for notifications" msgstr "" -#: preferences.py:156 +#: preferences.py:160 msgid "Regular expression to match primary group, e.g. '^Class .*'" msgstr "" -#: preferences.py:165 +#: preferences.py:169 msgid "Field on person to match primary group against" msgstr "" -#: preferences.py:177 +#: preferences.py:181 msgid "Display name of the school" msgstr "" -#: preferences.py:186 +#: preferences.py:190 msgid "Official name of the school, e.g. as given by supervisory authority" msgstr "" -#: preferences.py:194 +#: preferences.py:198 msgid "Enabled custom authentication backends" msgstr "" -#: settings.py:300 +#: preferences.py:211 +msgid "Available languages" +msgstr "" + +#: preferences.py:223 +msgid "Send emails if data checks detect problems" +msgstr "" + +#: preferences.py:234 +msgid "Email recipients for data checks problem emails" +msgstr "" + +#: preferences.py:245 +msgid "Email recipient groups for data checks problem emails" +msgstr "" + +#: settings.py:322 msgid "English" msgstr "" -#: settings.py:301 +#: settings.py:323 msgid "German" msgstr "" -#: settings.py:302 +#: settings.py:324 msgid "French" msgstr "" -#: settings.py:303 +#: settings.py:325 msgid "Norwegian (bokmål)" msgstr "" #: tables.py:19 templates/core/announcement/list.html:36 -#: templates/core/group/full.html:23 templates/core/person/full.html:22 +#: templates/core/group/full.html:24 templates/core/person/full.html:23 msgid "Edit" msgstr "" -#: tables.py:21 templates/core/announcement/list.html:22 +#: tables.py:21 tables.py:89 templates/core/announcement/list.html:22 msgid "Actions" msgstr "" -#: tables.py:56 tables.py:57 tables.py:71 -#: templates/core/announcement/list.html:42 templates/core/group/full.html:30 -#: templates/core/pages/delete.html:22 templates/core/person/full.html:29 +#: tables.py:56 tables.py:57 tables.py:71 tables.py:87 +#: templates/core/announcement/list.html:42 templates/core/group/full.html:31 +#: templates/core/pages/delete.html:22 templates/core/person/full.html:30 msgid "Delete" msgstr "" @@ -804,21 +937,21 @@ msgstr "" msgid "Create additional field" msgstr "" -#: templates/core/announcement/form.html:10 -#: templates/core/announcement/form.html:17 +#: templates/core/announcement/form.html:14 +#: templates/core/announcement/form.html:21 msgid "Edit announcement" msgstr "" -#: templates/core/announcement/form.html:12 +#: templates/core/announcement/form.html:16 msgid "Publish announcement" msgstr "" -#: templates/core/announcement/form.html:19 +#: templates/core/announcement/form.html:23 #: templates/core/announcement/list.html:13 msgid "Publish new announcement" msgstr "" -#: templates/core/announcement/form.html:30 +#: templates/core/announcement/form.html:34 msgid "Save und publish announcement" msgstr "" @@ -858,6 +991,124 @@ msgstr "" msgid "Powered by AlekSIS" msgstr "" +#: templates/core/dashboard_widget/create.html:8 +#: templates/core/dashboard_widget/create.html:12 +#, fuzzy, python-format +#| msgid "Contact details" +msgid "Create %(widget)s" +msgstr "Détails de contact" + +#: templates/core/dashboard_widget/edit.html:8 +#: templates/core/dashboard_widget/edit.html:12 +#, python-format +msgid "Edit %(widget)s" +msgstr "" + +#: templates/core/dashboard_widget/list.html:17 +#, fuzzy, python-format +#| msgid "Contact details" +msgid "Create %(name)s" +msgstr "Détails de contact" + +#: templates/core/dashboard_widget/list.html:25 +#: templates/core/edit_dashboard.html:8 templates/core/edit_dashboard.html:15 +msgid "Edit default dashboard" +msgstr "" + +#: templates/core/data_check/list.html:15 +msgid "Check data again" +msgstr "" + +#: templates/core/data_check/list.html:22 +msgid "The system detected some problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:23 +msgid "" +"Please go through all data and check whether some extra action is\n" +" needed." +msgstr "" + +#: templates/core/data_check/list.html:31 +msgid "Everything is fine." +msgstr "" + +#: templates/core/data_check/list.html:32 +msgid "The system hasn't detected any problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:40 +msgid "Detected problems" +msgstr "" + +#: templates/core/data_check/list.html:45 +msgid "Affected object" +msgstr "" + +#: templates/core/data_check/list.html:46 +msgid "Detected problem" +msgstr "" + +#: templates/core/data_check/list.html:47 +#, fuzzy +#| msgid "Contact details" +msgid "Show details" +msgstr "Détails de contact" + +#: templates/core/data_check/list.html:48 +msgid "Options to solve the problem" +msgstr "" + +#: templates/core/data_check/list.html:62 +msgid "Show object" +msgstr "" + +#: templates/core/data_check/list.html:84 +msgid "Registered checks" +msgstr "" + +#: templates/core/data_check/list.html:88 +msgid "" +"\n" +" The system will check for the following problems:\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:6 templates/core/edit_dashboard.html:13 +#: templates/core/index.html:14 +msgid "Edit dashboard" +msgstr "" + +#: templates/core/edit_dashboard.html:24 +msgid "" +"\n" +" On this page you can arrange your personal dashboard. You can drag any items from \"Available widgets\" to \"Your\n" +" Dashboard\" or change the order by moving the widgets. After you have finished, please don't forget to click on\n" +" \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:30 +msgid "" +"\n" +" On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own\n" +" dashboard. You can drag any items from \"Available widgets\" to \"Default Dashboard\" or change the order\n" +" by moving the widgets. After you have finished, please don't forget to click on \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:48 +msgid "Available widgets" +msgstr "" + +#: templates/core/edit_dashboard.html:57 +msgid "Your dashboard" +msgstr "" + +#: templates/core/edit_dashboard.html:59 +msgid "Default dashboard" +msgstr "" + #: templates/core/group/child_groups.html:18 msgid "" "\n" @@ -930,14 +1181,38 @@ msgstr "" msgid "Save and next" msgstr "" -#: templates/core/group/edit.html:6 templates/core/group/edit.html:7 +#: templates/core/group/edit.html:11 templates/core/group/edit.html:12 msgid "Edit group" msgstr "" -#: templates/core/group/full.html:37 templates/core/person/full.html:36 +#: templates/core/group/full.html:38 templates/core/person/full.html:37 msgid "Change preferences" msgstr "" +#: templates/core/group/full.html:64 +msgid "Statistics" +msgstr "" + +#: templates/core/group/full.html:67 +msgid "Count of members" +msgstr "" + +#: templates/core/group/full.html:71 +msgid "Average age" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "Age range" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years to" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years " +msgstr "" + #: templates/core/group/list.html:14 msgid "Create group" msgstr "" @@ -970,23 +1245,31 @@ msgstr "Groupe" msgid "Home" msgstr "" -#: templates/core/index.html:42 +#: templates/core/index.html:50 +msgid "" +"\n" +" You didn't customise your dashboard so that you see the system default. Please click on \"Edit dashboard\" to\n" +" customise your personal dashboard.\n" +" " +msgstr "" + +#: templates/core/index.html:59 msgid "Last activities" msgstr "" -#: templates/core/index.html:60 +#: templates/core/index.html:77 msgid "No activities available yet." msgstr "" -#: templates/core/index.html:65 +#: templates/core/index.html:82 msgid "Recent notifications" msgstr "" -#: templates/core/index.html:81 +#: templates/core/index.html:98 msgid "More information →" msgstr "" -#: templates/core/index.html:88 +#: templates/core/index.html:105 msgid "No notifications available yet." msgstr "" @@ -1065,21 +1348,6 @@ msgid "" " " msgstr "" -#: templates/core/pages/offline.html:6 -msgid "" -"No internet\n" -" connection." -msgstr "" - -#: templates/core/pages/offline.html:10 -msgid "" -"\n" -" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" -" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" -" administrators:\n" -" " -msgstr "" - #: templates/core/pages/progress.html:27 msgid "" "\n" @@ -1201,37 +1469,19 @@ msgid "" " " msgstr "" -#: templates/core/partials/crud_events.html:10 -#, python-format -msgid "" -"\n" -" Created by %(person)s\n" -" " -msgstr "" - -#: templates/core/partials/crud_events.html:14 -#: templates/core/partials/crud_events.html:22 -#: templates/core/partials/crud_events.html:26 -#, python-format -msgid "" -"\n" -" Updated by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Changed by" msgstr "" -#: templates/core/partials/crud_events.html:18 -#, python-format -msgid "" -"\n" -" Deleted by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Unknown" msgstr "" -#: templates/core/partials/language_form.html:16 +#: templates/core/partials/language_form.html:15 msgid "Language" msgstr "" -#: templates/core/partials/language_form.html:28 +#: templates/core/partials/language_form.html:27 msgid "Select language" msgstr "" @@ -1252,11 +1502,12 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:7 templates/core/person/accounts.html:9 +#: templates/core/person/accounts.html:12 +#: templates/core/person/accounts.html:14 msgid "Link persons to accounts" msgstr "" -#: templates/core/person/accounts.html:16 +#: templates/core/person/accounts.html:21 msgid "" "\n" " You can use this form to assign user accounts to persons. Use the\n" @@ -1266,28 +1517,34 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:31 -#: templates/core/person/accounts.html:55 +#: templates/core/person/accounts.html:36 +#: templates/core/person/accounts.html:60 msgid "Update" msgstr "" -#: templates/core/person/accounts.html:37 +#: templates/core/person/accounts.html:42 msgid "Existing account" msgstr "" -#: templates/core/person/accounts.html:38 +#: templates/core/person/accounts.html:43 msgid "New account" msgstr "" -#: templates/core/person/edit.html:11 templates/core/person/edit.html:12 +#: templates/core/person/edit.html:12 templates/core/person/edit.html:13 msgid "Edit person" msgstr "" -#: templates/core/person/full.html:42 +#: templates/core/person/full.html:44 +#, fuzzy +#| msgid "Contact details" +msgid "Impersonate" +msgstr "Détails de contact" + +#: templates/core/person/full.html:50 msgid "Contact details" msgstr "Détails de contact" -#: templates/core/person/full.html:122 +#: templates/core/person/full.html:130 msgid "Children" msgstr "" @@ -1333,14 +1590,29 @@ msgstr "" msgid "Save preferences" msgstr "" -#: templates/dynamic_preferences/sections.html:7 -msgid "All" -msgstr "" - #: templates/impersonate/list_users.html:8 msgid "Impersonate user" msgstr "" +#: templates/offline.html:5 +msgid "Network error" +msgstr "" + +#: templates/offline.html:8 +msgid "" +"No internet\n" +" connection." +msgstr "" + +#: templates/offline.html:12 +msgid "" +"\n" +" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" +" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" +" administrators:\n" +" " +msgstr "" + #: templates/search/search.html:8 msgid "Global Search" msgstr "" @@ -1361,6 +1633,41 @@ msgstr "" msgid "Please enter a search term above." msgstr "" +#: templates/templated_email/data_checks.email:4 +msgid "The system detected some new problems with your data." +msgstr "" + +#: templates/templated_email/data_checks.email:8 +#: templates/templated_email/data_checks.email:24 +msgid "Hello," +msgstr "" + +#: templates/templated_email/data_checks.email:10 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:26 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:34 +#, fuzzy +#| msgid "Description" +msgid "Problem description" +msgstr "Description" + +#: templates/templated_email/data_checks.email:35 +msgid "Count of objects with new problems" +msgstr "" + #: templates/templated_email/notification.email:3 msgid "New notification for" msgstr "" @@ -1743,58 +2050,91 @@ msgstr "" msgid "SMS" msgstr "" -#: views.py:122 +#: views.py:141 msgid "The school term has been created." msgstr "" -#: views.py:133 +#: views.py:153 msgid "The school term has been saved." msgstr "" -#: views.py:273 +#: views.py:298 msgid "The child groups were successfully saved." msgstr "" -#: views.py:309 +#: views.py:336 msgid "The person has been saved." msgstr "" -#: views.py:346 +#: views.py:375 msgid "The group has been saved." msgstr "" -#: views.py:434 +#: views.py:467 msgid "The announcement has been saved." msgstr "" -#: views.py:450 +#: views.py:483 msgid "The announcement has been deleted." msgstr "" -#: views.py:521 +#: views.py:562 msgid "The preferences have been saved successfully." msgstr "" -#: views.py:544 +#: views.py:586 msgid "The person has been deleted." msgstr "" -#: views.py:557 +#: views.py:600 msgid "The group has been deleted." msgstr "" -#: views.py:588 +#: views.py:632 msgid "The additional_field has been saved." msgstr "" -#: views.py:622 +#: views.py:666 msgid "The additional field has been deleted." msgstr "" -#: views.py:646 +#: views.py:691 msgid "The group type has been saved." msgstr "" -#: views.py:676 +#: views.py:721 msgid "The group type has been deleted." msgstr "" + +#: views.py:749 +msgid "The data check has been started. Please note that it may take a while before you are able to fetch the data on this page." +msgstr "" + +#: views.py:754 +msgid "The data check has finished." +msgstr "" + +#: views.py:769 +#, python-brace-format +msgid "The solve option '{solve_option_obj.verbose_name}' " +msgstr "" + +#: views.py:811 +msgid "The dashboard widget has been saved." +msgstr "" + +#: views.py:841 +msgid "The dashboard widget has been created." +msgstr "" + +#: views.py:851 +msgid "The dashboard widget has been deleted." +msgstr "" + +#: views.py:914 +msgid "Your dashboard configuration has been saved successfully." +msgstr "" + +#: views.py:916 +msgid "The configuration of the default dashboard has been saved successfully." +msgstr "" diff --git a/aleksis/core/locale/fr/LC_MESSAGES/djangojs.po b/aleksis/core/locale/fr/LC_MESSAGES/djangojs.po index 62da02ce6b9e58455b9ed6547a5d1380b2b062ac..83a1c140c8889eef6a1633f00a741cd110c52e05 100644 --- a/aleksis/core/locale/fr/LC_MESSAGES/djangojs.po +++ b/aleksis/core/locale/fr/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -18,14 +18,18 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: static/js/main.js:21 +#: static/js/main.js:15 msgid "Today" msgstr "" -#: static/js/main.js:22 +#: static/js/main.js:16 msgid "Cancel" msgstr "" -#: static/js/main.js:23 +#: static/js/main.js:17 msgid "OK" msgstr "" + +#: static/js/main.js:118 +msgid "This page may contain outdated information since there is no internet connection." +msgstr "" diff --git a/aleksis/core/locale/la/LC_MESSAGES/django.po b/aleksis/core/locale/la/LC_MESSAGES/django.po index 6fa38602c123109a48b5f34b07415b76a666f5a5..5fb260aac6828ea32031b9b03bd64424984cd8ab 100644 --- a/aleksis/core/locale/la/LC_MESSAGES/django.po +++ b/aleksis/core/locale/la/LC_MESSAGES/django.po @@ -7,11 +7,10 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: 2020-12-19 12:57+0000\n" "Last-Translator: Julian <leuckerj@gmail.com>\n" -"Language-Team: Latin <https://translate.edugit.org/projects/aleksis/aleksis/" -"la/>\n" +"Language-Team: Latin <https://translate.edugit.org/projects/aleksis/aleksis/la/>\n" "Language: la\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,6 +18,15 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.3.2\n" +#: data_checks.py:53 +msgid "Ignore problem" +msgstr "" + +#: data_checks.py:174 +#, python-brace-format +msgid "Solve option '{solve_option_obj.verbose_name}' " +msgstr "" + #: filters.py:37 templates/core/base.html:77 templates/core/group/list.html:20 #: templates/core/person/list.html:24 templates/search/search.html:7 #: templates/search/search.html:22 @@ -35,100 +43,106 @@ msgstr "Quaerere cum breve nomine" msgid "Search by contact details" msgstr "Inscriptio electronica" -#: forms.py:46 +#: forms.py:54 msgid "You cannot set a new username when also selecting an existing user." msgstr "" -#: forms.py:50 +#: forms.py:58 msgid "This username is already in use." msgstr "" -#: forms.py:74 +#: forms.py:82 msgid "Base data" msgstr "" -#: forms.py:80 +#: forms.py:88 #, fuzzy #| msgid "E-mail address" msgid "Address" msgstr "Inscriptio electronica" -#: forms.py:81 +#: forms.py:89 msgid "Contact data" msgstr "" -#: forms.py:83 +#: forms.py:91 msgid "Advanced personal data" msgstr "" -#: forms.py:114 +#: forms.py:134 msgid "New user" msgstr "" -#: forms.py:114 +#: forms.py:134 #, fuzzy #| msgid "Persons and accounts" msgid "Create a new account" msgstr "Personae et computi" -#: forms.py:126 models.py:91 +#: forms.py:146 models.py:102 msgid "School term" msgstr "Anus scolae" -#: forms.py:127 +#: forms.py:147 #, fuzzy #| msgid "Data management" msgid "Common data" msgstr "Adminstratio datarum" -#: forms.py:128 forms.py:170 menus.py:152 models.py:105 +#: forms.py:148 forms.py:197 menus.py:169 models.py:116 #: templates/core/person/list.html:8 templates/core/person/list.html:9 msgid "Persons" msgstr "personae" -#: forms.py:129 +#: forms.py:149 #, fuzzy #| msgid "Additional name(s)" msgid "Additional data" msgstr "addita nomines" -#: forms.py:163 forms.py:166 models.py:35 +#: forms.py:189 forms.py:192 models.py:45 msgid "Date" msgstr "dies" -#: forms.py:164 forms.py:167 models.py:43 +#: forms.py:190 forms.py:193 models.py:53 msgid "Time" msgstr "tempus" -#: forms.py:172 menus.py:160 models.py:319 templates/core/group/list.html:8 -#: templates/core/group/list.html:9 templates/core/person/full.html:136 +#: forms.py:210 menus.py:177 models.py:338 templates/core/group/list.html:8 +#: templates/core/group/list.html:9 templates/core/person/full.html:144 msgid "Groups" msgstr "Greges" -#: forms.py:176 +#: forms.py:220 msgid "From when until when should the announcement be displayed?" msgstr "" -#: forms.py:179 +#: forms.py:223 msgid "Who should see the announcement?" msgstr "Quis nuntium videatne?" -#: forms.py:180 +#: forms.py:224 msgid "Write your announcement:" msgstr "Scribe nuntium:" -#: forms.py:219 +#: forms.py:263 msgid "You are not allowed to create announcements which are only valid in the past." msgstr "" -#: forms.py:223 +#: forms.py:267 msgid "The from date and time must be earlier then the until date and time." msgstr "" -#: forms.py:232 +#: forms.py:276 msgid "You need at least one recipient." msgstr "" +#: health_checks.py:15 +#, fuzzy +#| msgid "Write your announcement:" +msgid "There are unresolved data problems." +msgstr "Scribe nuntium:" + #: menus.py:7 templates/two_factor/core/login.html:6 #: templates/two_factor/core/login.html:10 #: templates/two_factor/core/login.html:86 @@ -167,603 +181,739 @@ msgstr "" msgid "Admin" msgstr "Administratio" -#: menus.py:75 models.py:564 templates/core/announcement/list.html:7 +#: menus.py:75 models.py:602 templates/core/announcement/list.html:7 #: templates/core/announcement/list.html:8 msgid "Announcements" msgstr "Nuntii" -#: menus.py:86 models.py:92 templates/core/school_term/list.html:8 +#: menus.py:86 models.py:103 templates/core/school_term/list.html:8 #: templates/core/school_term/list.html:9 msgid "School terms" msgstr "ani scolae" -#: menus.py:97 templates/core/management/data_management.html:6 +#: menus.py:97 templates/core/dashboard_widget/list.html:8 +#: templates/core/dashboard_widget/list.html:9 +#, fuzzy +#| msgid "Dashboard" +msgid "Dashboard widgets" +msgstr "Forum" + +#: menus.py:108 templates/core/management/data_management.html:6 #: templates/core/management/data_management.html:7 msgid "Data management" msgstr "Adminstratio datarum" -#: menus.py:105 templates/core/pages/system_status.html:5 +#: menus.py:116 templates/core/pages/system_status.html:5 #: templates/core/pages/system_status.html:7 msgid "System status" msgstr "Status systemae" -#: menus.py:116 +#: menus.py:127 msgid "Impersonation" msgstr "Simulare aliquem" -#: menus.py:124 +#: menus.py:135 #, fuzzy #| msgid "Notification" msgid "Configuration" msgstr "Nuntius" -#: menus.py:135 +#: menus.py:146 templates/core/data_check/list.html:9 +#: templates/core/data_check/list.html:10 +#, fuzzy +#| msgid "System status" +msgid "Data checks" +msgstr "Status systemae" + +#: menus.py:152 msgid "Backend Admin" msgstr "" -#: menus.py:143 +#: menus.py:160 msgid "People" msgstr "Personae" -#: menus.py:168 models.py:727 templates/core/group_type/list.html:8 +#: menus.py:185 models.py:812 templates/core/group_type/list.html:8 #: templates/core/group_type/list.html:9 #, fuzzy #| msgid "Groups" msgid "Group types" msgstr "Greges" -#: menus.py:179 +#: menus.py:196 msgid "Persons and accounts" msgstr "Personae et computi" -#: menus.py:190 +#: menus.py:207 msgid "Groups and child groups" msgstr "" -#: menus.py:201 models.py:363 templates/core/additional_field/list.html:8 +#: menus.py:218 models.py:385 templates/core/additional_field/list.html:8 #: templates/core/additional_field/list.html:9 #, fuzzy #| msgid "Additional name(s)" msgid "Additional fields" msgstr "addita nomines" -#: menus.py:216 templates/core/group/child_groups.html:7 +#: menus.py:233 templates/core/group/child_groups.html:7 #: templates/core/group/child_groups.html:9 msgid "Assign child groups to groups" msgstr "" -#: mixins.py:406 +#: mixins.py:384 #, fuzzy #| msgid "Edit school term" msgid "Linked school term" msgstr "Muta anum scolae" -#: models.py:33 +#: models.py:43 msgid "Boolean (Yes/No)" msgstr "" -#: models.py:34 +#: models.py:44 msgid "Text (one line)" msgstr "" -#: models.py:36 +#: models.py:46 msgid "Date and time" msgstr "Dies et hora" -#: models.py:37 +#: models.py:47 msgid "Decimal number" msgstr "" -#: models.py:38 models.py:146 +#: models.py:48 models.py:157 msgid "E-mail address" msgstr "Inscriptio electronica" -#: models.py:39 +#: models.py:49 msgid "Integer" msgstr "" -#: models.py:40 +#: models.py:50 #, fuzzy #| msgid "E-mail address" msgid "IP address" msgstr "Inscriptio electronica" -#: models.py:41 +#: models.py:51 msgid "Boolean or empty (Yes/No/Neither)" msgstr "" -#: models.py:42 +#: models.py:52 msgid "Text (multi-line)" msgstr "" -#: models.py:44 +#: models.py:54 msgid "URL / Link" msgstr "" -#: models.py:56 models.py:700 +#: models.py:66 models.py:785 msgid "Name" msgstr "Nomen" -#: models.py:58 +#: models.py:68 msgid "Start date" msgstr "" -#: models.py:59 +#: models.py:69 msgid "End date" msgstr "" -#: models.py:77 +#: models.py:88 msgid "The start date must be earlier than the end date." msgstr "" -#: models.py:84 +#: models.py:95 msgid "There is already a school term for this time or a part of this time." msgstr "" -#: models.py:104 templates/core/person/accounts.html:36 +#: models.py:115 models.py:744 templates/core/person/accounts.html:41 msgid "Person" msgstr "Persona" -#: models.py:107 +#: models.py:118 #, fuzzy #| msgid "E-mail address" msgid "Can view address" msgstr "Inscriptio electronica" -#: models.py:108 +#: models.py:119 #, fuzzy #| msgid "E-mail address" msgid "Can view contact details" msgstr "Inscriptio electronica" -#: models.py:109 +#: models.py:120 #, fuzzy #| msgid "E-mail address" msgid "Can view photo" msgstr "Inscriptio electronica" -#: models.py:110 +#: models.py:121 #, fuzzy #| msgid "Persons and accounts" msgid "Can view persons groups" msgstr "Personae et computi" -#: models.py:111 +#: models.py:122 #, fuzzy #| msgid "Stop impersonation" msgid "Can view personal details" msgstr "Simulandum aliquem finire" -#: models.py:116 +#: models.py:127 msgid "female" msgstr "femininum" -#: models.py:116 +#: models.py:127 msgid "male" msgstr "maskulinum" -#: models.py:124 +#: models.py:135 msgid "Linked user" msgstr "" -#: models.py:126 +#: models.py:137 #, fuzzy #| msgid "Impersonation" msgid "Is person active?" msgstr "Simulare aliquem" -#: models.py:128 +#: models.py:139 msgid "First name" msgstr "Primus nomen" -#: models.py:129 +#: models.py:140 msgid "Last name" msgstr "Secondus nomen" -#: models.py:131 +#: models.py:142 msgid "Additional name(s)" msgstr "addita nomines" -#: models.py:135 models.py:332 +#: models.py:146 models.py:354 msgid "Short name" msgstr "Breve nomen" -#: models.py:138 +#: models.py:149 msgid "Street" msgstr "Via" -#: models.py:139 +#: models.py:150 msgid "Street number" msgstr "Numerus domini" -#: models.py:140 +#: models.py:151 msgid "Postal code" msgstr "Numerus directorius" -#: models.py:141 +#: models.py:152 msgid "Place" msgstr "Urbs" -#: models.py:143 +#: models.py:154 msgid "Home phone" msgstr "Numerus telephoni domi" -#: models.py:144 +#: models.py:155 msgid "Mobile phone" msgstr "Numerus telephoni mobilis" -#: models.py:148 +#: models.py:159 msgid "Date of birth" msgstr "Dies natalis" -#: models.py:149 +#: models.py:160 msgid "Sex" msgstr "Genus" -#: models.py:151 +#: models.py:162 msgid "Photo" msgstr "Photographia" -#: models.py:155 templates/core/person/full.html:129 +#: models.py:166 templates/core/person/full.html:137 msgid "Guardians / Parents" msgstr "Parentes" -#: models.py:162 +#: models.py:173 msgid "Primary group" msgstr "" -#: models.py:165 models.py:423 models.py:447 models.py:532 models.py:720 -#: templates/core/person/full.html:112 +#: models.py:176 models.py:461 models.py:485 models.py:570 models.py:805 +#: templates/core/person/full.html:120 msgid "Description" msgstr "Descriptio" -#: models.py:296 +#: models.py:313 msgid "Title of field" msgstr "" -#: models.py:298 +#: models.py:315 msgid "Type of field" msgstr "" -#: models.py:305 +#: models.py:322 #, fuzzy #| msgid "Additional name(s)" msgid "Addtitional field for groups" msgstr "addita nomines" -#: models.py:306 +#: models.py:323 #, fuzzy #| msgid "Additional name(s)" msgid "Addtitional fields for groups" msgstr "addita nomines" -#: models.py:318 +#: models.py:337 msgid "Group" msgstr "Grex" -#: models.py:320 +#: models.py:340 msgid "Can assign child groups to groups" msgstr "" -#: models.py:330 +#: models.py:341 +#, fuzzy +#| msgid "Persons and accounts" +msgid "Can view statistics about group." +msgstr "Personae et computi" + +#: models.py:352 #, fuzzy #| msgid "Last name" msgid "Long name" msgstr "Secondus nomen" -#: models.py:340 templates/core/group/full.html:65 +#: models.py:362 templates/core/group/full.html:85 msgid "Members" msgstr "" -#: models.py:343 templates/core/group/full.html:62 +#: models.py:365 templates/core/group/full.html:82 msgid "Owners" msgstr "" -#: models.py:350 templates/core/group/full.html:54 +#: models.py:372 templates/core/group/full.html:55 msgid "Parent groups" msgstr "" -#: models.py:358 +#: models.py:380 msgid "Type of group" msgstr "" -#: models.py:419 +#: models.py:457 msgid "User" msgstr "" -#: models.py:422 models.py:446 models.py:531 +#: models.py:460 models.py:484 models.py:569 #: templates/core/announcement/list.html:18 msgid "Title" msgstr "Titulus" -#: models.py:425 +#: models.py:463 msgid "Application" msgstr "" -#: models.py:431 +#: models.py:469 msgid "Activity" msgstr "" -#: models.py:432 +#: models.py:470 msgid "Activities" msgstr "" -#: models.py:438 +#: models.py:476 msgid "Sender" msgstr "Mittens" -#: models.py:443 +#: models.py:481 msgid "Recipient" msgstr "" -#: models.py:448 models.py:701 +#: models.py:486 models.py:786 msgid "Link" msgstr "" -#: models.py:450 +#: models.py:488 msgid "Read" msgstr "" -#: models.py:451 +#: models.py:489 msgid "Sent" msgstr "" -#: models.py:464 +#: models.py:502 #, fuzzy #| msgid "Notifications" msgid "Notification" msgstr "Nuntii" -#: models.py:465 +#: models.py:503 msgid "Notifications" msgstr "Nuntii" -#: models.py:533 +#: models.py:571 msgid "Link to detailed view" msgstr "" -#: models.py:536 +#: models.py:574 msgid "Date and time from when to show" msgstr "" -#: models.py:539 +#: models.py:577 msgid "Date and time until when to show" msgstr "" -#: models.py:563 +#: models.py:601 #, fuzzy #| msgid "Announcements" msgid "Announcement" msgstr "Nuntii" -#: models.py:601 +#: models.py:639 #, fuzzy #| msgid "Announcements" msgid "Announcement recipient" msgstr "Nuntii" -#: models.py:602 +#: models.py:640 #, fuzzy #| msgid "Announcements" msgid "Announcement recipients" msgstr "Nuntii" -#: models.py:652 +#: models.py:690 #, fuzzy #| msgid "Site title" msgid "Widget Title" msgstr "Titulus paginae" -#: models.py:653 +#: models.py:691 msgid "Activate Widget" msgstr "" -#: models.py:671 +#: models.py:694 +msgid "Size on mobile devices" +msgstr "" + +#: models.py:695 +msgid "<= 600 px, 12 columns" +msgstr "" + +#: models.py:700 +msgid "Size on tablet devices" +msgstr "" + +#: models.py:701 +msgid "> 600 px, 12 columns" +msgstr "" + +#: models.py:706 +msgid "Size on desktop devices" +msgstr "" + +#: models.py:707 +msgid "> 992 px, 12 columns" +msgstr "" + +#: models.py:712 +msgid "Size on large desktop devices" +msgstr "" + +#: models.py:713 +msgid "> 1200 px>, 12 columns" +msgstr "" + +#: models.py:734 +#, fuzzy +#| msgid "Dashboard" +msgid "Can edit default dashboard" +msgstr "Forum" + +#: models.py:735 #, fuzzy #| msgid "Dashboard" msgid "Dashboard Widget" msgstr "Forum" -#: models.py:672 +#: models.py:736 #, fuzzy #| msgid "Dashboard" msgid "Dashboard Widgets" msgstr "Forum" -#: models.py:678 +#: models.py:741 +#, fuzzy +#| msgid "Dashboard" +msgid "Dashboard widget" +msgstr "Forum" + +#: models.py:746 +msgid "Order" +msgstr "" + +#: models.py:747 +msgid "Part of the default dashboard" +msgstr "" + +#: models.py:755 +#, fuzzy +#| msgid "Dashboard" +msgid "Dashboard widget order" +msgstr "Forum" + +#: models.py:756 +#, fuzzy +#| msgid "Dashboard" +msgid "Dashboard widget orders" +msgstr "Forum" + +#: models.py:762 msgid "Menu ID" msgstr "" -#: models.py:690 +#: models.py:775 msgid "Custom menu" msgstr "" -#: models.py:691 +#: models.py:776 msgid "Custom menus" msgstr "" -#: models.py:698 +#: models.py:783 msgid "Menu" msgstr "" -#: models.py:702 +#: models.py:787 msgid "Icon" msgstr "Nota" -#: models.py:708 +#: models.py:793 msgid "Custom menu item" msgstr "" -#: models.py:709 +#: models.py:794 msgid "Custom menu items" msgstr "" -#: models.py:719 +#: models.py:804 msgid "Title of type" msgstr "" -#: models.py:726 templates/core/group/full.html:46 +#: models.py:811 templates/core/group/full.html:47 #, fuzzy #| msgid "Group" msgid "Group type" msgstr "Grex" -#: models.py:736 +#: models.py:821 #, fuzzy #| msgid "System status" msgid "Can view system status" msgstr "Status systemae" -#: models.py:737 +#: models.py:822 #, fuzzy #| msgid "Persons and accounts" msgid "Can link persons to accounts" msgstr "Personae et computi" -#: models.py:738 +#: models.py:823 #, fuzzy #| msgid "Data management" msgid "Can manage data" msgstr "Adminstratio datarum" -#: models.py:739 +#: models.py:824 #, fuzzy #| msgid "Stop impersonation" msgid "Can impersonate" msgstr "Simulandum aliquem finire" -#: models.py:740 +#: models.py:825 msgid "Can use search" msgstr "" -#: models.py:741 +#: models.py:826 msgid "Can change site preferences" msgstr "" -#: models.py:742 +#: models.py:827 msgid "Can change person preferences" msgstr "" -#: models.py:743 +#: models.py:828 msgid "Can change group preferences" msgstr "" -#: preferences.py:24 +#: models.py:864 +msgid "Related data check task" +msgstr "" + +#: models.py:872 +msgid "Issue solved" +msgstr "" + +#: models.py:873 +#, fuzzy +#| msgid "Notifications" +msgid "Notification sent" +msgstr "Nuntii" + +#: models.py:886 +msgid "Data check result" +msgstr "" + +#: models.py:887 +msgid "Data check results" +msgstr "" + +#: models.py:889 +msgid "Can run data checks" +msgstr "" + +#: models.py:890 +msgid "Can solve data check problems" +msgstr "" + +#: preferences.py:27 #, fuzzy #| msgid "Notifications" msgid "Authentication" msgstr "Nuntii" -#: preferences.py:33 +#: preferences.py:28 +#, fuzzy +#| msgid "Impersonation" +msgid "Internationalisation" +msgstr "Simulare aliquem" + +#: preferences.py:37 msgid "Site title" msgstr "Titulus paginae" -#: preferences.py:42 +#: preferences.py:46 msgid "Site description" msgstr "Descriptio paginae" -#: preferences.py:51 +#: preferences.py:55 msgid "Primary colour" msgstr "" -#: preferences.py:60 +#: preferences.py:64 msgid "Secondary colour" msgstr "" -#: preferences.py:68 +#: preferences.py:72 #, fuzzy #| msgid "Logout" msgid "Logo" msgstr "nomen retractare" -#: preferences.py:76 +#: preferences.py:80 msgid "Favicon" msgstr "" -#: preferences.py:84 +#: preferences.py:88 #, fuzzy #| msgid "Icon" msgid "PWA-Icon" msgstr "Nota" -#: preferences.py:93 +#: preferences.py:97 #, fuzzy #| msgid "Last name" msgid "Mail out name" msgstr "Secondus nomen" -#: preferences.py:102 +#: preferences.py:106 #, fuzzy #| msgid "E-mail address" msgid "Mail out address" msgstr "Inscriptio electronica" -#: preferences.py:112 +#: preferences.py:116 msgid "Link to privacy policy" msgstr "" -#: preferences.py:122 +#: preferences.py:126 msgid "Link to imprint" msgstr "" -#: preferences.py:132 +#: preferences.py:136 msgid "Name format for addressing" msgstr "" -#: preferences.py:146 +#: preferences.py:150 msgid "Channels to use for notifications" msgstr "" -#: preferences.py:156 +#: preferences.py:160 msgid "Regular expression to match primary group, e.g. '^Class .*'" msgstr "" -#: preferences.py:165 +#: preferences.py:169 msgid "Field on person to match primary group against" msgstr "" -#: preferences.py:177 +#: preferences.py:181 msgid "Display name of the school" msgstr "" -#: preferences.py:186 +#: preferences.py:190 msgid "Official name of the school, e.g. as given by supervisory authority" msgstr "Officialis nomen scolae, e. g." -#: preferences.py:194 +#: preferences.py:198 msgid "Enabled custom authentication backends" msgstr "" -#: settings.py:300 +#: preferences.py:211 +msgid "Available languages" +msgstr "" + +#: preferences.py:223 +msgid "Send emails if data checks detect problems" +msgstr "" + +#: preferences.py:234 +msgid "Email recipients for data checks problem emails" +msgstr "" + +#: preferences.py:245 +msgid "Email recipient groups for data checks problem emails" +msgstr "" + +#: settings.py:322 msgid "English" msgstr "Britannicus" -#: settings.py:301 +#: settings.py:323 msgid "German" msgstr "Germanus" -#: settings.py:302 +#: settings.py:324 msgid "French" msgstr "" -#: settings.py:303 +#: settings.py:325 msgid "Norwegian (bokmål)" msgstr "" #: tables.py:19 templates/core/announcement/list.html:36 -#: templates/core/group/full.html:23 templates/core/person/full.html:22 +#: templates/core/group/full.html:24 templates/core/person/full.html:23 msgid "Edit" msgstr "" -#: tables.py:21 templates/core/announcement/list.html:22 +#: tables.py:21 tables.py:89 templates/core/announcement/list.html:22 #, fuzzy #| msgid "Notifications" msgid "Actions" msgstr "Nuntii" -#: tables.py:56 tables.py:57 tables.py:71 -#: templates/core/announcement/list.html:42 templates/core/group/full.html:30 -#: templates/core/pages/delete.html:22 templates/core/person/full.html:29 +#: tables.py:56 tables.py:57 tables.py:71 tables.py:87 +#: templates/core/announcement/list.html:42 templates/core/group/full.html:31 +#: templates/core/pages/delete.html:22 templates/core/person/full.html:30 msgid "Delete" msgstr "" @@ -839,27 +989,27 @@ msgstr "addita nomines" msgid "Create additional field" msgstr "addita nomines" -#: templates/core/announcement/form.html:10 -#: templates/core/announcement/form.html:17 +#: templates/core/announcement/form.html:14 +#: templates/core/announcement/form.html:21 #, fuzzy #| msgid "Announcements" msgid "Edit announcement" msgstr "Nuntii" -#: templates/core/announcement/form.html:12 +#: templates/core/announcement/form.html:16 #, fuzzy #| msgid "Announcements" msgid "Publish announcement" msgstr "Nuntii" -#: templates/core/announcement/form.html:19 +#: templates/core/announcement/form.html:23 #: templates/core/announcement/list.html:13 #, fuzzy #| msgid "Who should see the announcement?" msgid "Publish new announcement" msgstr "Quis nuntium videatne?" -#: templates/core/announcement/form.html:30 +#: templates/core/announcement/form.html:34 #, fuzzy #| msgid "Who should see the announcement?" msgid "Save und publish announcement" @@ -903,6 +1053,132 @@ msgstr "" msgid "Powered by AlekSIS" msgstr "" +#: templates/core/dashboard_widget/create.html:8 +#: templates/core/dashboard_widget/create.html:12 +#, fuzzy, python-format +#| msgid "Stop impersonation" +msgid "Create %(widget)s" +msgstr "Simulandum aliquem finire" + +#: templates/core/dashboard_widget/edit.html:8 +#: templates/core/dashboard_widget/edit.html:12 +#, python-format +msgid "Edit %(widget)s" +msgstr "" + +#: templates/core/dashboard_widget/list.html:17 +#, fuzzy, python-format +#| msgid "Stop impersonation" +msgid "Create %(name)s" +msgstr "Simulandum aliquem finire" + +#: templates/core/dashboard_widget/list.html:25 +#: templates/core/edit_dashboard.html:8 templates/core/edit_dashboard.html:15 +#, fuzzy +#| msgid "Dashboard" +msgid "Edit default dashboard" +msgstr "Forum" + +#: templates/core/data_check/list.html:15 +msgid "Check data again" +msgstr "" + +#: templates/core/data_check/list.html:22 +msgid "The system detected some problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:23 +msgid "" +"Please go through all data and check whether some extra action is\n" +" needed." +msgstr "" + +#: templates/core/data_check/list.html:31 +msgid "Everything is fine." +msgstr "" + +#: templates/core/data_check/list.html:32 +msgid "The system hasn't detected any problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:40 +msgid "Detected problems" +msgstr "" + +#: templates/core/data_check/list.html:45 +msgid "Affected object" +msgstr "" + +#: templates/core/data_check/list.html:46 +msgid "Detected problem" +msgstr "" + +#: templates/core/data_check/list.html:47 +msgid "Show details" +msgstr "" + +#: templates/core/data_check/list.html:48 +msgid "Options to solve the problem" +msgstr "" + +#: templates/core/data_check/list.html:62 +msgid "Show object" +msgstr "" + +#: templates/core/data_check/list.html:84 +#, fuzzy +#| msgid "System status" +msgid "Registered checks" +msgstr "Status systemae" + +#: templates/core/data_check/list.html:88 +msgid "" +"\n" +" The system will check for the following problems:\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:6 templates/core/edit_dashboard.html:13 +#: templates/core/index.html:14 +#, fuzzy +#| msgid "Dashboard" +msgid "Edit dashboard" +msgstr "Forum" + +#: templates/core/edit_dashboard.html:24 +msgid "" +"\n" +" On this page you can arrange your personal dashboard. You can drag any items from \"Available widgets\" to \"Your\n" +" Dashboard\" or change the order by moving the widgets. After you have finished, please don't forget to click on\n" +" \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:30 +msgid "" +"\n" +" On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own\n" +" dashboard. You can drag any items from \"Available widgets\" to \"Default Dashboard\" or change the order\n" +" by moving the widgets. After you have finished, please don't forget to click on \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:48 +msgid "Available widgets" +msgstr "" + +#: templates/core/edit_dashboard.html:57 +#, fuzzy +#| msgid "Dashboard" +msgid "Your dashboard" +msgstr "Forum" + +#: templates/core/edit_dashboard.html:59 +#, fuzzy +#| msgid "Dashboard" +msgid "Default dashboard" +msgstr "Forum" + #: templates/core/group/child_groups.html:18 msgid "" "\n" @@ -975,14 +1251,40 @@ msgstr "" msgid "Save and next" msgstr "" -#: templates/core/group/edit.html:6 templates/core/group/edit.html:7 +#: templates/core/group/edit.html:11 templates/core/group/edit.html:12 msgid "Edit group" msgstr "" -#: templates/core/group/full.html:37 templates/core/person/full.html:36 +#: templates/core/group/full.html:38 templates/core/person/full.html:37 msgid "Change preferences" msgstr "" +#: templates/core/group/full.html:64 +#, fuzzy +#| msgid "System status" +msgid "Statistics" +msgstr "Status systemae" + +#: templates/core/group/full.html:67 +msgid "Count of members" +msgstr "" + +#: templates/core/group/full.html:71 +msgid "Average age" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "Age range" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years to" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years " +msgstr "" + #: templates/core/group/list.html:14 msgid "Create group" msgstr "" @@ -1015,27 +1317,35 @@ msgstr "Grex" msgid "Home" msgstr "" -#: templates/core/index.html:42 +#: templates/core/index.html:50 +msgid "" +"\n" +" You didn't customise your dashboard so that you see the system default. Please click on \"Edit dashboard\" to\n" +" customise your personal dashboard.\n" +" " +msgstr "" + +#: templates/core/index.html:59 msgid "Last activities" msgstr "" -#: templates/core/index.html:60 +#: templates/core/index.html:77 msgid "No activities available yet." msgstr "" -#: templates/core/index.html:65 +#: templates/core/index.html:82 #, fuzzy #| msgid "Notifications" msgid "Recent notifications" msgstr "Nuntii" -#: templates/core/index.html:81 +#: templates/core/index.html:98 #, fuzzy #| msgid "Edit school information" msgid "More information →" msgstr "Muta informationes scolae" -#: templates/core/index.html:88 +#: templates/core/index.html:105 msgid "No notifications available yet." msgstr "" @@ -1118,21 +1428,6 @@ msgid "" " " msgstr "" -#: templates/core/pages/offline.html:6 -msgid "" -"No internet\n" -" connection." -msgstr "" - -#: templates/core/pages/offline.html:10 -msgid "" -"\n" -" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" -" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" -" administrators:\n" -" " -msgstr "" - #: templates/core/pages/progress.html:27 msgid "" "\n" @@ -1260,37 +1555,19 @@ msgid "" " " msgstr "" -#: templates/core/partials/crud_events.html:10 -#, python-format -msgid "" -"\n" -" Created by %(person)s\n" -" " -msgstr "" - -#: templates/core/partials/crud_events.html:14 -#: templates/core/partials/crud_events.html:22 -#: templates/core/partials/crud_events.html:26 -#, python-format -msgid "" -"\n" -" Updated by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Changed by" msgstr "" -#: templates/core/partials/crud_events.html:18 -#, python-format -msgid "" -"\n" -" Deleted by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Unknown" msgstr "" -#: templates/core/partials/language_form.html:16 +#: templates/core/partials/language_form.html:15 msgid "Language" msgstr "" -#: templates/core/partials/language_form.html:28 +#: templates/core/partials/language_form.html:27 msgid "Select language" msgstr "" @@ -1311,13 +1588,14 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:7 templates/core/person/accounts.html:9 +#: templates/core/person/accounts.html:12 +#: templates/core/person/accounts.html:14 #, fuzzy #| msgid "Persons and accounts" msgid "Link persons to accounts" msgstr "Personae et computi" -#: templates/core/person/accounts.html:16 +#: templates/core/person/accounts.html:21 msgid "" "\n" " You can use this form to assign user accounts to persons. Use the\n" @@ -1327,28 +1605,34 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:31 -#: templates/core/person/accounts.html:55 +#: templates/core/person/accounts.html:36 +#: templates/core/person/accounts.html:60 msgid "Update" msgstr "" -#: templates/core/person/accounts.html:37 +#: templates/core/person/accounts.html:42 msgid "Existing account" msgstr "" -#: templates/core/person/accounts.html:38 +#: templates/core/person/accounts.html:43 msgid "New account" msgstr "" -#: templates/core/person/edit.html:11 templates/core/person/edit.html:12 +#: templates/core/person/edit.html:12 templates/core/person/edit.html:13 msgid "Edit person" msgstr "" -#: templates/core/person/full.html:42 +#: templates/core/person/full.html:44 +#, fuzzy +#| msgid "Impersonation" +msgid "Impersonate" +msgstr "Simulare aliquem" + +#: templates/core/person/full.html:50 msgid "Contact details" msgstr "" -#: templates/core/person/full.html:122 +#: templates/core/person/full.html:130 msgid "Children" msgstr "" @@ -1396,16 +1680,31 @@ msgstr "" msgid "Save preferences" msgstr "" -#: templates/dynamic_preferences/sections.html:7 -msgid "All" -msgstr "" - #: templates/impersonate/list_users.html:8 #, fuzzy #| msgid "Impersonation" msgid "Impersonate user" msgstr "Simulare aliquem" +#: templates/offline.html:5 +msgid "Network error" +msgstr "" + +#: templates/offline.html:8 +msgid "" +"No internet\n" +" connection." +msgstr "" + +#: templates/offline.html:12 +msgid "" +"\n" +" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" +" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" +" administrators:\n" +" " +msgstr "" + #: templates/search/search.html:8 msgid "Global Search" msgstr "" @@ -1426,6 +1725,41 @@ msgstr "" msgid "Please enter a search term above." msgstr "" +#: templates/templated_email/data_checks.email:4 +msgid "The system detected some new problems with your data." +msgstr "" + +#: templates/templated_email/data_checks.email:8 +#: templates/templated_email/data_checks.email:24 +msgid "Hello," +msgstr "" + +#: templates/templated_email/data_checks.email:10 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:26 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:34 +#, fuzzy +#| msgid "Site description" +msgid "Problem description" +msgstr "Descriptio paginae" + +#: templates/templated_email/data_checks.email:35 +msgid "Count of objects with new problems" +msgstr "" + #: templates/templated_email/notification.email:3 #, fuzzy #| msgid "Notification" @@ -1815,62 +2149,95 @@ msgstr "" msgid "SMS" msgstr "" -#: views.py:122 +#: views.py:141 msgid "The school term has been created." msgstr "" -#: views.py:133 +#: views.py:153 msgid "The school term has been saved." msgstr "" -#: views.py:273 +#: views.py:298 msgid "The child groups were successfully saved." msgstr "" -#: views.py:309 +#: views.py:336 msgid "The person has been saved." msgstr "" -#: views.py:346 +#: views.py:375 msgid "The group has been saved." msgstr "" -#: views.py:434 +#: views.py:467 msgid "The announcement has been saved." msgstr "" -#: views.py:450 +#: views.py:483 msgid "The announcement has been deleted." msgstr "" -#: views.py:521 +#: views.py:562 msgid "The preferences have been saved successfully." msgstr "" -#: views.py:544 +#: views.py:586 msgid "The person has been deleted." msgstr "" -#: views.py:557 +#: views.py:600 msgid "The group has been deleted." msgstr "" -#: views.py:588 +#: views.py:632 msgid "The additional_field has been saved." msgstr "" -#: views.py:622 +#: views.py:666 msgid "The additional field has been deleted." msgstr "" -#: views.py:646 +#: views.py:691 msgid "The group type has been saved." msgstr "" -#: views.py:676 +#: views.py:721 msgid "The group type has been deleted." msgstr "" +#: views.py:749 +msgid "The data check has been started. Please note that it may take a while before you are able to fetch the data on this page." +msgstr "" + +#: views.py:754 +msgid "The data check has finished." +msgstr "" + +#: views.py:769 +#, python-brace-format +msgid "The solve option '{solve_option_obj.verbose_name}' " +msgstr "" + +#: views.py:811 +msgid "The dashboard widget has been saved." +msgstr "" + +#: views.py:841 +msgid "The dashboard widget has been created." +msgstr "" + +#: views.py:851 +msgid "The dashboard widget has been deleted." +msgstr "" + +#: views.py:914 +msgid "Your dashboard configuration has been saved successfully." +msgstr "" + +#: views.py:916 +msgid "The configuration of the default dashboard has been saved successfully." +msgstr "" + #~ msgid "School logo" #~ msgstr "Imago scolae" diff --git a/aleksis/core/locale/la/LC_MESSAGES/djangojs.po b/aleksis/core/locale/la/LC_MESSAGES/djangojs.po index 9dbef6e6f74f842b0e8f25e389edf1a852cd8d99..ea24fc3ff92afc211898fb8b426dccb97fce56ed 100644 --- a/aleksis/core/locale/la/LC_MESSAGES/djangojs.po +++ b/aleksis/core/locale/la/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,14 +17,18 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: static/js/main.js:21 +#: static/js/main.js:15 msgid "Today" msgstr "" -#: static/js/main.js:22 +#: static/js/main.js:16 msgid "Cancel" msgstr "" -#: static/js/main.js:23 +#: static/js/main.js:17 msgid "OK" msgstr "" + +#: static/js/main.js:118 +msgid "This page may contain outdated information since there is no internet connection." +msgstr "" diff --git a/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po b/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po index 5be24e272186dd1985d749b948562bb13f222bc7..6b4be05da6b0a53f60a6ffa060739c419ea4af3a 100644 --- a/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po +++ b/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: AlekSIS (School Information System) 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,6 +17,15 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: data_checks.py:53 +msgid "Ignore problem" +msgstr "" + +#: data_checks.py:174 +#, python-brace-format +msgid "Solve option '{solve_option_obj.verbose_name}' " +msgstr "" + #: filters.py:37 templates/core/base.html:77 templates/core/group/list.html:20 #: templates/core/person/list.html:24 templates/search/search.html:7 #: templates/search/search.html:22 @@ -31,92 +40,96 @@ msgstr "" msgid "Search by contact details" msgstr "" -#: forms.py:46 +#: forms.py:54 msgid "You cannot set a new username when also selecting an existing user." msgstr "" -#: forms.py:50 +#: forms.py:58 msgid "This username is already in use." msgstr "" -#: forms.py:74 +#: forms.py:82 msgid "Base data" msgstr "" -#: forms.py:80 +#: forms.py:88 msgid "Address" msgstr "" -#: forms.py:81 +#: forms.py:89 msgid "Contact data" msgstr "" -#: forms.py:83 +#: forms.py:91 msgid "Advanced personal data" msgstr "" -#: forms.py:114 +#: forms.py:134 msgid "New user" msgstr "" -#: forms.py:114 +#: forms.py:134 msgid "Create a new account" msgstr "" -#: forms.py:126 models.py:91 +#: forms.py:146 models.py:102 msgid "School term" msgstr "" -#: forms.py:127 +#: forms.py:147 msgid "Common data" msgstr "" -#: forms.py:128 forms.py:170 menus.py:152 models.py:105 +#: forms.py:148 forms.py:197 menus.py:169 models.py:116 #: templates/core/person/list.html:8 templates/core/person/list.html:9 msgid "Persons" msgstr "" -#: forms.py:129 +#: forms.py:149 msgid "Additional data" msgstr "" -#: forms.py:163 forms.py:166 models.py:35 +#: forms.py:189 forms.py:192 models.py:45 msgid "Date" msgstr "" -#: forms.py:164 forms.py:167 models.py:43 +#: forms.py:190 forms.py:193 models.py:53 msgid "Time" msgstr "" -#: forms.py:172 menus.py:160 models.py:319 templates/core/group/list.html:8 -#: templates/core/group/list.html:9 templates/core/person/full.html:136 +#: forms.py:210 menus.py:177 models.py:338 templates/core/group/list.html:8 +#: templates/core/group/list.html:9 templates/core/person/full.html:144 msgid "Groups" msgstr "" -#: forms.py:176 +#: forms.py:220 msgid "From when until when should the announcement be displayed?" msgstr "" -#: forms.py:179 +#: forms.py:223 msgid "Who should see the announcement?" msgstr "" -#: forms.py:180 +#: forms.py:224 msgid "Write your announcement:" msgstr "" -#: forms.py:219 +#: forms.py:263 msgid "You are not allowed to create announcements which are only valid in the past." msgstr "" -#: forms.py:223 +#: forms.py:267 msgid "The from date and time must be earlier then the until date and time." msgstr "" -#: forms.py:232 +#: forms.py:276 msgid "You need at least one recipient." msgstr "" +#: health_checks.py:15 +msgid "There are unresolved data problems." +msgstr "" + #: menus.py:7 templates/two_factor/core/login.html:6 #: templates/two_factor/core/login.html:10 #: templates/two_factor/core/login.html:86 @@ -155,539 +168,657 @@ msgstr "" msgid "Admin" msgstr "" -#: menus.py:75 models.py:564 templates/core/announcement/list.html:7 +#: menus.py:75 models.py:602 templates/core/announcement/list.html:7 #: templates/core/announcement/list.html:8 msgid "Announcements" msgstr "" -#: menus.py:86 models.py:92 templates/core/school_term/list.html:8 +#: menus.py:86 models.py:103 templates/core/school_term/list.html:8 #: templates/core/school_term/list.html:9 msgid "School terms" msgstr "" -#: menus.py:97 templates/core/management/data_management.html:6 +#: menus.py:97 templates/core/dashboard_widget/list.html:8 +#: templates/core/dashboard_widget/list.html:9 +msgid "Dashboard widgets" +msgstr "" + +#: menus.py:108 templates/core/management/data_management.html:6 #: templates/core/management/data_management.html:7 msgid "Data management" msgstr "" -#: menus.py:105 templates/core/pages/system_status.html:5 +#: menus.py:116 templates/core/pages/system_status.html:5 #: templates/core/pages/system_status.html:7 msgid "System status" msgstr "" -#: menus.py:116 +#: menus.py:127 msgid "Impersonation" msgstr "" -#: menus.py:124 +#: menus.py:135 msgid "Configuration" msgstr "" -#: menus.py:135 +#: menus.py:146 templates/core/data_check/list.html:9 +#: templates/core/data_check/list.html:10 +msgid "Data checks" +msgstr "" + +#: menus.py:152 msgid "Backend Admin" msgstr "" -#: menus.py:143 +#: menus.py:160 msgid "People" msgstr "" -#: menus.py:168 models.py:727 templates/core/group_type/list.html:8 +#: menus.py:185 models.py:812 templates/core/group_type/list.html:8 #: templates/core/group_type/list.html:9 msgid "Group types" msgstr "" -#: menus.py:179 +#: menus.py:196 msgid "Persons and accounts" msgstr "" -#: menus.py:190 +#: menus.py:207 msgid "Groups and child groups" msgstr "" -#: menus.py:201 models.py:363 templates/core/additional_field/list.html:8 +#: menus.py:218 models.py:385 templates/core/additional_field/list.html:8 #: templates/core/additional_field/list.html:9 msgid "Additional fields" msgstr "" -#: menus.py:216 templates/core/group/child_groups.html:7 +#: menus.py:233 templates/core/group/child_groups.html:7 #: templates/core/group/child_groups.html:9 msgid "Assign child groups to groups" msgstr "" -#: mixins.py:406 +#: mixins.py:384 msgid "Linked school term" msgstr "" -#: models.py:33 +#: models.py:43 msgid "Boolean (Yes/No)" msgstr "" -#: models.py:34 +#: models.py:44 msgid "Text (one line)" msgstr "" -#: models.py:36 +#: models.py:46 msgid "Date and time" msgstr "" -#: models.py:37 +#: models.py:47 msgid "Decimal number" msgstr "" -#: models.py:38 models.py:146 +#: models.py:48 models.py:157 msgid "E-mail address" msgstr "" -#: models.py:39 +#: models.py:49 msgid "Integer" msgstr "" -#: models.py:40 +#: models.py:50 msgid "IP address" msgstr "" -#: models.py:41 +#: models.py:51 msgid "Boolean or empty (Yes/No/Neither)" msgstr "" -#: models.py:42 +#: models.py:52 msgid "Text (multi-line)" msgstr "" -#: models.py:44 +#: models.py:54 msgid "URL / Link" msgstr "" -#: models.py:56 models.py:700 +#: models.py:66 models.py:785 msgid "Name" msgstr "" -#: models.py:58 +#: models.py:68 msgid "Start date" msgstr "" -#: models.py:59 +#: models.py:69 msgid "End date" msgstr "" -#: models.py:77 +#: models.py:88 msgid "The start date must be earlier than the end date." msgstr "" -#: models.py:84 +#: models.py:95 msgid "There is already a school term for this time or a part of this time." msgstr "" -#: models.py:104 templates/core/person/accounts.html:36 +#: models.py:115 models.py:744 templates/core/person/accounts.html:41 msgid "Person" msgstr "" -#: models.py:107 +#: models.py:118 msgid "Can view address" msgstr "" -#: models.py:108 +#: models.py:119 msgid "Can view contact details" msgstr "" -#: models.py:109 +#: models.py:120 msgid "Can view photo" msgstr "" -#: models.py:110 +#: models.py:121 msgid "Can view persons groups" msgstr "" -#: models.py:111 +#: models.py:122 msgid "Can view personal details" msgstr "" -#: models.py:116 +#: models.py:127 msgid "female" msgstr "" -#: models.py:116 +#: models.py:127 msgid "male" msgstr "" -#: models.py:124 +#: models.py:135 msgid "Linked user" msgstr "" -#: models.py:126 +#: models.py:137 msgid "Is person active?" msgstr "" -#: models.py:128 +#: models.py:139 msgid "First name" msgstr "" -#: models.py:129 +#: models.py:140 msgid "Last name" msgstr "" -#: models.py:131 +#: models.py:142 msgid "Additional name(s)" msgstr "" -#: models.py:135 models.py:332 +#: models.py:146 models.py:354 msgid "Short name" msgstr "" -#: models.py:138 +#: models.py:149 msgid "Street" msgstr "" -#: models.py:139 +#: models.py:150 msgid "Street number" msgstr "" -#: models.py:140 +#: models.py:151 msgid "Postal code" msgstr "" -#: models.py:141 +#: models.py:152 msgid "Place" msgstr "" -#: models.py:143 +#: models.py:154 msgid "Home phone" msgstr "" -#: models.py:144 +#: models.py:155 msgid "Mobile phone" msgstr "" -#: models.py:148 +#: models.py:159 msgid "Date of birth" msgstr "" -#: models.py:149 +#: models.py:160 msgid "Sex" msgstr "" -#: models.py:151 +#: models.py:162 msgid "Photo" msgstr "" -#: models.py:155 templates/core/person/full.html:129 +#: models.py:166 templates/core/person/full.html:137 msgid "Guardians / Parents" msgstr "" -#: models.py:162 +#: models.py:173 msgid "Primary group" msgstr "" -#: models.py:165 models.py:423 models.py:447 models.py:532 models.py:720 -#: templates/core/person/full.html:112 +#: models.py:176 models.py:461 models.py:485 models.py:570 models.py:805 +#: templates/core/person/full.html:120 msgid "Description" msgstr "" -#: models.py:296 +#: models.py:313 msgid "Title of field" msgstr "" -#: models.py:298 +#: models.py:315 msgid "Type of field" msgstr "" -#: models.py:305 +#: models.py:322 msgid "Addtitional field for groups" msgstr "" -#: models.py:306 +#: models.py:323 msgid "Addtitional fields for groups" msgstr "" -#: models.py:318 +#: models.py:337 msgid "Group" msgstr "" -#: models.py:320 +#: models.py:340 msgid "Can assign child groups to groups" msgstr "" -#: models.py:330 +#: models.py:341 +msgid "Can view statistics about group." +msgstr "" + +#: models.py:352 msgid "Long name" msgstr "" -#: models.py:340 templates/core/group/full.html:65 +#: models.py:362 templates/core/group/full.html:85 msgid "Members" msgstr "" -#: models.py:343 templates/core/group/full.html:62 +#: models.py:365 templates/core/group/full.html:82 msgid "Owners" msgstr "" -#: models.py:350 templates/core/group/full.html:54 +#: models.py:372 templates/core/group/full.html:55 msgid "Parent groups" msgstr "" -#: models.py:358 +#: models.py:380 msgid "Type of group" msgstr "" -#: models.py:419 +#: models.py:457 msgid "User" msgstr "" -#: models.py:422 models.py:446 models.py:531 +#: models.py:460 models.py:484 models.py:569 #: templates/core/announcement/list.html:18 msgid "Title" msgstr "" -#: models.py:425 +#: models.py:463 msgid "Application" msgstr "" -#: models.py:431 +#: models.py:469 msgid "Activity" msgstr "" -#: models.py:432 +#: models.py:470 msgid "Activities" msgstr "" -#: models.py:438 +#: models.py:476 msgid "Sender" msgstr "" -#: models.py:443 +#: models.py:481 msgid "Recipient" msgstr "" -#: models.py:448 models.py:701 +#: models.py:486 models.py:786 msgid "Link" msgstr "" -#: models.py:450 +#: models.py:488 msgid "Read" msgstr "" -#: models.py:451 +#: models.py:489 msgid "Sent" msgstr "" -#: models.py:464 +#: models.py:502 msgid "Notification" msgstr "" -#: models.py:465 +#: models.py:503 msgid "Notifications" msgstr "" -#: models.py:533 +#: models.py:571 msgid "Link to detailed view" msgstr "" -#: models.py:536 +#: models.py:574 msgid "Date and time from when to show" msgstr "" -#: models.py:539 +#: models.py:577 msgid "Date and time until when to show" msgstr "" -#: models.py:563 +#: models.py:601 msgid "Announcement" msgstr "" -#: models.py:601 +#: models.py:639 msgid "Announcement recipient" msgstr "" -#: models.py:602 +#: models.py:640 msgid "Announcement recipients" msgstr "" -#: models.py:652 +#: models.py:690 msgid "Widget Title" msgstr "" -#: models.py:653 +#: models.py:691 msgid "Activate Widget" msgstr "" -#: models.py:671 +#: models.py:694 +msgid "Size on mobile devices" +msgstr "" + +#: models.py:695 +msgid "<= 600 px, 12 columns" +msgstr "" + +#: models.py:700 +msgid "Size on tablet devices" +msgstr "" + +#: models.py:701 +msgid "> 600 px, 12 columns" +msgstr "" + +#: models.py:706 +msgid "Size on desktop devices" +msgstr "" + +#: models.py:707 +msgid "> 992 px, 12 columns" +msgstr "" + +#: models.py:712 +msgid "Size on large desktop devices" +msgstr "" + +#: models.py:713 +msgid "> 1200 px>, 12 columns" +msgstr "" + +#: models.py:734 +msgid "Can edit default dashboard" +msgstr "" + +#: models.py:735 msgid "Dashboard Widget" msgstr "" -#: models.py:672 +#: models.py:736 msgid "Dashboard Widgets" msgstr "" -#: models.py:678 +#: models.py:741 +msgid "Dashboard widget" +msgstr "" + +#: models.py:746 +msgid "Order" +msgstr "" + +#: models.py:747 +msgid "Part of the default dashboard" +msgstr "" + +#: models.py:755 +msgid "Dashboard widget order" +msgstr "" + +#: models.py:756 +msgid "Dashboard widget orders" +msgstr "" + +#: models.py:762 msgid "Menu ID" msgstr "" -#: models.py:690 +#: models.py:775 msgid "Custom menu" msgstr "" -#: models.py:691 +#: models.py:776 msgid "Custom menus" msgstr "" -#: models.py:698 +#: models.py:783 msgid "Menu" msgstr "" -#: models.py:702 +#: models.py:787 msgid "Icon" msgstr "" -#: models.py:708 +#: models.py:793 msgid "Custom menu item" msgstr "" -#: models.py:709 +#: models.py:794 msgid "Custom menu items" msgstr "" -#: models.py:719 +#: models.py:804 msgid "Title of type" msgstr "" -#: models.py:726 templates/core/group/full.html:46 +#: models.py:811 templates/core/group/full.html:47 msgid "Group type" msgstr "" -#: models.py:736 +#: models.py:821 msgid "Can view system status" msgstr "" -#: models.py:737 +#: models.py:822 msgid "Can link persons to accounts" msgstr "" -#: models.py:738 +#: models.py:823 msgid "Can manage data" msgstr "" -#: models.py:739 +#: models.py:824 msgid "Can impersonate" msgstr "" -#: models.py:740 +#: models.py:825 msgid "Can use search" msgstr "" -#: models.py:741 +#: models.py:826 msgid "Can change site preferences" msgstr "" -#: models.py:742 +#: models.py:827 msgid "Can change person preferences" msgstr "" -#: models.py:743 +#: models.py:828 msgid "Can change group preferences" msgstr "" -#: preferences.py:24 +#: models.py:864 +msgid "Related data check task" +msgstr "" + +#: models.py:872 +msgid "Issue solved" +msgstr "" + +#: models.py:873 +msgid "Notification sent" +msgstr "" + +#: models.py:886 +msgid "Data check result" +msgstr "" + +#: models.py:887 +msgid "Data check results" +msgstr "" + +#: models.py:889 +msgid "Can run data checks" +msgstr "" + +#: models.py:890 +msgid "Can solve data check problems" +msgstr "" + +#: preferences.py:27 msgid "Authentication" msgstr "" -#: preferences.py:33 +#: preferences.py:28 +msgid "Internationalisation" +msgstr "" + +#: preferences.py:37 msgid "Site title" msgstr "" -#: preferences.py:42 +#: preferences.py:46 msgid "Site description" msgstr "" -#: preferences.py:51 +#: preferences.py:55 msgid "Primary colour" msgstr "" -#: preferences.py:60 +#: preferences.py:64 msgid "Secondary colour" msgstr "" -#: preferences.py:68 +#: preferences.py:72 msgid "Logo" msgstr "" -#: preferences.py:76 +#: preferences.py:80 msgid "Favicon" msgstr "" -#: preferences.py:84 +#: preferences.py:88 msgid "PWA-Icon" msgstr "" -#: preferences.py:93 +#: preferences.py:97 msgid "Mail out name" msgstr "" -#: preferences.py:102 +#: preferences.py:106 msgid "Mail out address" msgstr "" -#: preferences.py:112 +#: preferences.py:116 msgid "Link to privacy policy" msgstr "" -#: preferences.py:122 +#: preferences.py:126 msgid "Link to imprint" msgstr "" -#: preferences.py:132 +#: preferences.py:136 msgid "Name format for addressing" msgstr "" -#: preferences.py:146 +#: preferences.py:150 msgid "Channels to use for notifications" msgstr "" -#: preferences.py:156 +#: preferences.py:160 msgid "Regular expression to match primary group, e.g. '^Class .*'" msgstr "" -#: preferences.py:165 +#: preferences.py:169 msgid "Field on person to match primary group against" msgstr "" -#: preferences.py:177 +#: preferences.py:181 msgid "Display name of the school" msgstr "" -#: preferences.py:186 +#: preferences.py:190 msgid "Official name of the school, e.g. as given by supervisory authority" msgstr "" -#: preferences.py:194 +#: preferences.py:198 msgid "Enabled custom authentication backends" msgstr "" -#: settings.py:300 +#: preferences.py:211 +msgid "Available languages" +msgstr "" + +#: preferences.py:223 +msgid "Send emails if data checks detect problems" +msgstr "" + +#: preferences.py:234 +msgid "Email recipients for data checks problem emails" +msgstr "" + +#: preferences.py:245 +msgid "Email recipient groups for data checks problem emails" +msgstr "" + +#: settings.py:322 msgid "English" msgstr "" -#: settings.py:301 +#: settings.py:323 msgid "German" msgstr "" -#: settings.py:302 +#: settings.py:324 msgid "French" msgstr "" -#: settings.py:303 +#: settings.py:325 msgid "Norwegian (bokmål)" msgstr "Norsk (bokmål)" #: tables.py:19 templates/core/announcement/list.html:36 -#: templates/core/group/full.html:23 templates/core/person/full.html:22 +#: templates/core/group/full.html:24 templates/core/person/full.html:23 msgid "Edit" msgstr "" -#: tables.py:21 templates/core/announcement/list.html:22 +#: tables.py:21 tables.py:89 templates/core/announcement/list.html:22 msgid "Actions" msgstr "" -#: tables.py:56 tables.py:57 tables.py:71 -#: templates/core/announcement/list.html:42 templates/core/group/full.html:30 -#: templates/core/pages/delete.html:22 templates/core/person/full.html:29 +#: tables.py:56 tables.py:57 tables.py:71 tables.py:87 +#: templates/core/announcement/list.html:42 templates/core/group/full.html:31 +#: templates/core/pages/delete.html:22 templates/core/person/full.html:30 msgid "Delete" msgstr "" @@ -759,21 +890,21 @@ msgstr "" msgid "Create additional field" msgstr "" -#: templates/core/announcement/form.html:10 -#: templates/core/announcement/form.html:17 +#: templates/core/announcement/form.html:14 +#: templates/core/announcement/form.html:21 msgid "Edit announcement" msgstr "" -#: templates/core/announcement/form.html:12 +#: templates/core/announcement/form.html:16 msgid "Publish announcement" msgstr "" -#: templates/core/announcement/form.html:19 +#: templates/core/announcement/form.html:23 #: templates/core/announcement/list.html:13 msgid "Publish new announcement" msgstr "" -#: templates/core/announcement/form.html:30 +#: templates/core/announcement/form.html:34 msgid "Save und publish announcement" msgstr "" @@ -813,6 +944,120 @@ msgstr "" msgid "Powered by AlekSIS" msgstr "" +#: templates/core/dashboard_widget/create.html:8 +#: templates/core/dashboard_widget/create.html:12 +#, python-format +msgid "Create %(widget)s" +msgstr "" + +#: templates/core/dashboard_widget/edit.html:8 +#: templates/core/dashboard_widget/edit.html:12 +#, python-format +msgid "Edit %(widget)s" +msgstr "" + +#: templates/core/dashboard_widget/list.html:17 +#, python-format +msgid "Create %(name)s" +msgstr "" + +#: templates/core/dashboard_widget/list.html:25 +#: templates/core/edit_dashboard.html:8 templates/core/edit_dashboard.html:15 +msgid "Edit default dashboard" +msgstr "" + +#: templates/core/data_check/list.html:15 +msgid "Check data again" +msgstr "" + +#: templates/core/data_check/list.html:22 +msgid "The system detected some problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:23 +msgid "" +"Please go through all data and check whether some extra action is\n" +" needed." +msgstr "" + +#: templates/core/data_check/list.html:31 +msgid "Everything is fine." +msgstr "" + +#: templates/core/data_check/list.html:32 +msgid "The system hasn't detected any problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:40 +msgid "Detected problems" +msgstr "" + +#: templates/core/data_check/list.html:45 +msgid "Affected object" +msgstr "" + +#: templates/core/data_check/list.html:46 +msgid "Detected problem" +msgstr "" + +#: templates/core/data_check/list.html:47 +msgid "Show details" +msgstr "" + +#: templates/core/data_check/list.html:48 +msgid "Options to solve the problem" +msgstr "" + +#: templates/core/data_check/list.html:62 +msgid "Show object" +msgstr "" + +#: templates/core/data_check/list.html:84 +msgid "Registered checks" +msgstr "" + +#: templates/core/data_check/list.html:88 +msgid "" +"\n" +" The system will check for the following problems:\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:6 templates/core/edit_dashboard.html:13 +#: templates/core/index.html:14 +msgid "Edit dashboard" +msgstr "" + +#: templates/core/edit_dashboard.html:24 +msgid "" +"\n" +" On this page you can arrange your personal dashboard. You can drag any items from \"Available widgets\" to \"Your\n" +" Dashboard\" or change the order by moving the widgets. After you have finished, please don't forget to click on\n" +" \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:30 +msgid "" +"\n" +" On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own\n" +" dashboard. You can drag any items from \"Available widgets\" to \"Default Dashboard\" or change the order\n" +" by moving the widgets. After you have finished, please don't forget to click on \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:48 +msgid "Available widgets" +msgstr "" + +#: templates/core/edit_dashboard.html:57 +msgid "Your dashboard" +msgstr "" + +#: templates/core/edit_dashboard.html:59 +msgid "Default dashboard" +msgstr "" + #: templates/core/group/child_groups.html:18 msgid "" "\n" @@ -885,14 +1130,38 @@ msgstr "" msgid "Save and next" msgstr "" -#: templates/core/group/edit.html:6 templates/core/group/edit.html:7 +#: templates/core/group/edit.html:11 templates/core/group/edit.html:12 msgid "Edit group" msgstr "" -#: templates/core/group/full.html:37 templates/core/person/full.html:36 +#: templates/core/group/full.html:38 templates/core/person/full.html:37 msgid "Change preferences" msgstr "" +#: templates/core/group/full.html:64 +msgid "Statistics" +msgstr "" + +#: templates/core/group/full.html:67 +msgid "Count of members" +msgstr "" + +#: templates/core/group/full.html:71 +msgid "Average age" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "Age range" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years to" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years " +msgstr "" + #: templates/core/group/list.html:14 msgid "Create group" msgstr "" @@ -921,23 +1190,31 @@ msgstr "" msgid "Home" msgstr "" -#: templates/core/index.html:42 +#: templates/core/index.html:50 +msgid "" +"\n" +" You didn't customise your dashboard so that you see the system default. Please click on \"Edit dashboard\" to\n" +" customise your personal dashboard.\n" +" " +msgstr "" + +#: templates/core/index.html:59 msgid "Last activities" msgstr "" -#: templates/core/index.html:60 +#: templates/core/index.html:77 msgid "No activities available yet." msgstr "" -#: templates/core/index.html:65 +#: templates/core/index.html:82 msgid "Recent notifications" msgstr "" -#: templates/core/index.html:81 +#: templates/core/index.html:98 msgid "More information →" msgstr "" -#: templates/core/index.html:88 +#: templates/core/index.html:105 msgid "No notifications available yet." msgstr "" @@ -1016,21 +1293,6 @@ msgid "" " " msgstr "" -#: templates/core/pages/offline.html:6 -msgid "" -"No internet\n" -" connection." -msgstr "" - -#: templates/core/pages/offline.html:10 -msgid "" -"\n" -" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" -" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" -" administrators:\n" -" " -msgstr "" - #: templates/core/pages/progress.html:27 msgid "" "\n" @@ -1150,37 +1412,19 @@ msgid "" " " msgstr "" -#: templates/core/partials/crud_events.html:10 -#, python-format -msgid "" -"\n" -" Created by %(person)s\n" -" " -msgstr "" - -#: templates/core/partials/crud_events.html:14 -#: templates/core/partials/crud_events.html:22 -#: templates/core/partials/crud_events.html:26 -#, python-format -msgid "" -"\n" -" Updated by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Changed by" msgstr "" -#: templates/core/partials/crud_events.html:18 -#, python-format -msgid "" -"\n" -" Deleted by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Unknown" msgstr "" -#: templates/core/partials/language_form.html:16 +#: templates/core/partials/language_form.html:15 msgid "Language" msgstr "" -#: templates/core/partials/language_form.html:28 +#: templates/core/partials/language_form.html:27 msgid "Select language" msgstr "" @@ -1201,11 +1445,12 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:7 templates/core/person/accounts.html:9 +#: templates/core/person/accounts.html:12 +#: templates/core/person/accounts.html:14 msgid "Link persons to accounts" msgstr "" -#: templates/core/person/accounts.html:16 +#: templates/core/person/accounts.html:21 msgid "" "\n" " You can use this form to assign user accounts to persons. Use the\n" @@ -1215,28 +1460,32 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:31 -#: templates/core/person/accounts.html:55 +#: templates/core/person/accounts.html:36 +#: templates/core/person/accounts.html:60 msgid "Update" msgstr "" -#: templates/core/person/accounts.html:37 +#: templates/core/person/accounts.html:42 msgid "Existing account" msgstr "" -#: templates/core/person/accounts.html:38 +#: templates/core/person/accounts.html:43 msgid "New account" msgstr "" -#: templates/core/person/edit.html:11 templates/core/person/edit.html:12 +#: templates/core/person/edit.html:12 templates/core/person/edit.html:13 msgid "Edit person" msgstr "" -#: templates/core/person/full.html:42 +#: templates/core/person/full.html:44 +msgid "Impersonate" +msgstr "" + +#: templates/core/person/full.html:50 msgid "Contact details" msgstr "" -#: templates/core/person/full.html:122 +#: templates/core/person/full.html:130 msgid "Children" msgstr "" @@ -1280,14 +1529,29 @@ msgstr "" msgid "Save preferences" msgstr "" -#: templates/dynamic_preferences/sections.html:7 -msgid "All" -msgstr "" - #: templates/impersonate/list_users.html:8 msgid "Impersonate user" msgstr "" +#: templates/offline.html:5 +msgid "Network error" +msgstr "" + +#: templates/offline.html:8 +msgid "" +"No internet\n" +" connection." +msgstr "" + +#: templates/offline.html:12 +msgid "" +"\n" +" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" +" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" +" administrators:\n" +" " +msgstr "" + #: templates/search/search.html:8 msgid "Global Search" msgstr "" @@ -1308,6 +1572,39 @@ msgstr "" msgid "Please enter a search term above." msgstr "" +#: templates/templated_email/data_checks.email:4 +msgid "The system detected some new problems with your data." +msgstr "" + +#: templates/templated_email/data_checks.email:8 +#: templates/templated_email/data_checks.email:24 +msgid "Hello," +msgstr "" + +#: templates/templated_email/data_checks.email:10 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:26 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:34 +msgid "Problem description" +msgstr "" + +#: templates/templated_email/data_checks.email:35 +msgid "Count of objects with new problems" +msgstr "" + #: templates/templated_email/notification.email:3 msgid "New notification for" msgstr "" @@ -1690,58 +1987,91 @@ msgstr "" msgid "SMS" msgstr "" -#: views.py:122 +#: views.py:141 msgid "The school term has been created." msgstr "" -#: views.py:133 +#: views.py:153 msgid "The school term has been saved." msgstr "" -#: views.py:273 +#: views.py:298 msgid "The child groups were successfully saved." msgstr "" -#: views.py:309 +#: views.py:336 msgid "The person has been saved." msgstr "" -#: views.py:346 +#: views.py:375 msgid "The group has been saved." msgstr "" -#: views.py:434 +#: views.py:467 msgid "The announcement has been saved." msgstr "" -#: views.py:450 +#: views.py:483 msgid "The announcement has been deleted." msgstr "" -#: views.py:521 +#: views.py:562 msgid "The preferences have been saved successfully." msgstr "" -#: views.py:544 +#: views.py:586 msgid "The person has been deleted." msgstr "" -#: views.py:557 +#: views.py:600 msgid "The group has been deleted." msgstr "" -#: views.py:588 +#: views.py:632 msgid "The additional_field has been saved." msgstr "" -#: views.py:622 +#: views.py:666 msgid "The additional field has been deleted." msgstr "" -#: views.py:646 +#: views.py:691 msgid "The group type has been saved." msgstr "" -#: views.py:676 +#: views.py:721 msgid "The group type has been deleted." msgstr "" + +#: views.py:749 +msgid "The data check has been started. Please note that it may take a while before you are able to fetch the data on this page." +msgstr "" + +#: views.py:754 +msgid "The data check has finished." +msgstr "" + +#: views.py:769 +#, python-brace-format +msgid "The solve option '{solve_option_obj.verbose_name}' " +msgstr "" + +#: views.py:811 +msgid "The dashboard widget has been saved." +msgstr "" + +#: views.py:841 +msgid "The dashboard widget has been created." +msgstr "" + +#: views.py:851 +msgid "The dashboard widget has been deleted." +msgstr "" + +#: views.py:914 +msgid "Your dashboard configuration has been saved successfully." +msgstr "" + +#: views.py:916 +msgid "The configuration of the default dashboard has been saved successfully." +msgstr "" diff --git a/aleksis/core/locale/nb_NO/LC_MESSAGES/djangojs.po b/aleksis/core/locale/nb_NO/LC_MESSAGES/djangojs.po index 9dbef6e6f74f842b0e8f25e389edf1a852cd8d99..ea24fc3ff92afc211898fb8b426dccb97fce56ed 100644 --- a/aleksis/core/locale/nb_NO/LC_MESSAGES/djangojs.po +++ b/aleksis/core/locale/nb_NO/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,14 +17,18 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: static/js/main.js:21 +#: static/js/main.js:15 msgid "Today" msgstr "" -#: static/js/main.js:22 +#: static/js/main.js:16 msgid "Cancel" msgstr "" -#: static/js/main.js:23 +#: static/js/main.js:17 msgid "OK" msgstr "" + +#: static/js/main.js:118 +msgid "This page may contain outdated information since there is no internet connection." +msgstr "" diff --git a/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po b/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po index d0417b32bd8aa0df398290eacf5a23491641abd2..7ecf671cf7dd018f3d159265a13605f8f050e518 100644 --- a/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po +++ b/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: AlekSIS (School Information System) 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,6 +17,15 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: data_checks.py:53 +msgid "Ignore problem" +msgstr "" + +#: data_checks.py:174 +#, python-brace-format +msgid "Solve option '{solve_option_obj.verbose_name}' " +msgstr "" + #: filters.py:37 templates/core/base.html:77 templates/core/group/list.html:20 #: templates/core/person/list.html:24 templates/search/search.html:7 #: templates/search/search.html:22 @@ -31,92 +40,96 @@ msgstr "" msgid "Search by contact details" msgstr "" -#: forms.py:46 +#: forms.py:54 msgid "You cannot set a new username when also selecting an existing user." msgstr "" -#: forms.py:50 +#: forms.py:58 msgid "This username is already in use." msgstr "" -#: forms.py:74 +#: forms.py:82 msgid "Base data" msgstr "" -#: forms.py:80 +#: forms.py:88 msgid "Address" msgstr "" -#: forms.py:81 +#: forms.py:89 msgid "Contact data" msgstr "" -#: forms.py:83 +#: forms.py:91 msgid "Advanced personal data" msgstr "" -#: forms.py:114 +#: forms.py:134 msgid "New user" msgstr "" -#: forms.py:114 +#: forms.py:134 msgid "Create a new account" msgstr "" -#: forms.py:126 models.py:91 +#: forms.py:146 models.py:102 msgid "School term" msgstr "" -#: forms.py:127 +#: forms.py:147 msgid "Common data" msgstr "" -#: forms.py:128 forms.py:170 menus.py:152 models.py:105 +#: forms.py:148 forms.py:197 menus.py:169 models.py:116 #: templates/core/person/list.html:8 templates/core/person/list.html:9 msgid "Persons" msgstr "" -#: forms.py:129 +#: forms.py:149 msgid "Additional data" msgstr "" -#: forms.py:163 forms.py:166 models.py:35 +#: forms.py:189 forms.py:192 models.py:45 msgid "Date" msgstr "" -#: forms.py:164 forms.py:167 models.py:43 +#: forms.py:190 forms.py:193 models.py:53 msgid "Time" msgstr "" -#: forms.py:172 menus.py:160 models.py:319 templates/core/group/list.html:8 -#: templates/core/group/list.html:9 templates/core/person/full.html:136 +#: forms.py:210 menus.py:177 models.py:338 templates/core/group/list.html:8 +#: templates/core/group/list.html:9 templates/core/person/full.html:144 msgid "Groups" msgstr "" -#: forms.py:176 +#: forms.py:220 msgid "From when until when should the announcement be displayed?" msgstr "" -#: forms.py:179 +#: forms.py:223 msgid "Who should see the announcement?" msgstr "" -#: forms.py:180 +#: forms.py:224 msgid "Write your announcement:" msgstr "" -#: forms.py:219 +#: forms.py:263 msgid "You are not allowed to create announcements which are only valid in the past." msgstr "" -#: forms.py:223 +#: forms.py:267 msgid "The from date and time must be earlier then the until date and time." msgstr "" -#: forms.py:232 +#: forms.py:276 msgid "You need at least one recipient." msgstr "" +#: health_checks.py:15 +msgid "There are unresolved data problems." +msgstr "" + #: menus.py:7 templates/two_factor/core/login.html:6 #: templates/two_factor/core/login.html:10 #: templates/two_factor/core/login.html:86 @@ -155,539 +168,657 @@ msgstr "" msgid "Admin" msgstr "" -#: menus.py:75 models.py:564 templates/core/announcement/list.html:7 +#: menus.py:75 models.py:602 templates/core/announcement/list.html:7 #: templates/core/announcement/list.html:8 msgid "Announcements" msgstr "" -#: menus.py:86 models.py:92 templates/core/school_term/list.html:8 +#: menus.py:86 models.py:103 templates/core/school_term/list.html:8 #: templates/core/school_term/list.html:9 msgid "School terms" msgstr "" -#: menus.py:97 templates/core/management/data_management.html:6 +#: menus.py:97 templates/core/dashboard_widget/list.html:8 +#: templates/core/dashboard_widget/list.html:9 +msgid "Dashboard widgets" +msgstr "" + +#: menus.py:108 templates/core/management/data_management.html:6 #: templates/core/management/data_management.html:7 msgid "Data management" msgstr "" -#: menus.py:105 templates/core/pages/system_status.html:5 +#: menus.py:116 templates/core/pages/system_status.html:5 #: templates/core/pages/system_status.html:7 msgid "System status" msgstr "" -#: menus.py:116 +#: menus.py:127 msgid "Impersonation" msgstr "" -#: menus.py:124 +#: menus.py:135 msgid "Configuration" msgstr "" -#: menus.py:135 +#: menus.py:146 templates/core/data_check/list.html:9 +#: templates/core/data_check/list.html:10 +msgid "Data checks" +msgstr "" + +#: menus.py:152 msgid "Backend Admin" msgstr "" -#: menus.py:143 +#: menus.py:160 msgid "People" msgstr "" -#: menus.py:168 models.py:727 templates/core/group_type/list.html:8 +#: menus.py:185 models.py:812 templates/core/group_type/list.html:8 #: templates/core/group_type/list.html:9 msgid "Group types" msgstr "" -#: menus.py:179 +#: menus.py:196 msgid "Persons and accounts" msgstr "" -#: menus.py:190 +#: menus.py:207 msgid "Groups and child groups" msgstr "" -#: menus.py:201 models.py:363 templates/core/additional_field/list.html:8 +#: menus.py:218 models.py:385 templates/core/additional_field/list.html:8 #: templates/core/additional_field/list.html:9 msgid "Additional fields" msgstr "" -#: menus.py:216 templates/core/group/child_groups.html:7 +#: menus.py:233 templates/core/group/child_groups.html:7 #: templates/core/group/child_groups.html:9 msgid "Assign child groups to groups" msgstr "" -#: mixins.py:406 +#: mixins.py:384 msgid "Linked school term" msgstr "" -#: models.py:33 +#: models.py:43 msgid "Boolean (Yes/No)" msgstr "" -#: models.py:34 +#: models.py:44 msgid "Text (one line)" msgstr "" -#: models.py:36 +#: models.py:46 msgid "Date and time" msgstr "" -#: models.py:37 +#: models.py:47 msgid "Decimal number" msgstr "" -#: models.py:38 models.py:146 +#: models.py:48 models.py:157 msgid "E-mail address" msgstr "" -#: models.py:39 +#: models.py:49 msgid "Integer" msgstr "" -#: models.py:40 +#: models.py:50 msgid "IP address" msgstr "" -#: models.py:41 +#: models.py:51 msgid "Boolean or empty (Yes/No/Neither)" msgstr "" -#: models.py:42 +#: models.py:52 msgid "Text (multi-line)" msgstr "" -#: models.py:44 +#: models.py:54 msgid "URL / Link" msgstr "" -#: models.py:56 models.py:700 +#: models.py:66 models.py:785 msgid "Name" msgstr "" -#: models.py:58 +#: models.py:68 msgid "Start date" msgstr "" -#: models.py:59 +#: models.py:69 msgid "End date" msgstr "" -#: models.py:77 +#: models.py:88 msgid "The start date must be earlier than the end date." msgstr "" -#: models.py:84 +#: models.py:95 msgid "There is already a school term for this time or a part of this time." msgstr "" -#: models.py:104 templates/core/person/accounts.html:36 +#: models.py:115 models.py:744 templates/core/person/accounts.html:41 msgid "Person" msgstr "" -#: models.py:107 +#: models.py:118 msgid "Can view address" msgstr "" -#: models.py:108 +#: models.py:119 msgid "Can view contact details" msgstr "" -#: models.py:109 +#: models.py:120 msgid "Can view photo" msgstr "" -#: models.py:110 +#: models.py:121 msgid "Can view persons groups" msgstr "" -#: models.py:111 +#: models.py:122 msgid "Can view personal details" msgstr "" -#: models.py:116 +#: models.py:127 msgid "female" msgstr "" -#: models.py:116 +#: models.py:127 msgid "male" msgstr "" -#: models.py:124 +#: models.py:135 msgid "Linked user" msgstr "" -#: models.py:126 +#: models.py:137 msgid "Is person active?" msgstr "" -#: models.py:128 +#: models.py:139 msgid "First name" msgstr "" -#: models.py:129 +#: models.py:140 msgid "Last name" msgstr "" -#: models.py:131 +#: models.py:142 msgid "Additional name(s)" msgstr "" -#: models.py:135 models.py:332 +#: models.py:146 models.py:354 msgid "Short name" msgstr "" -#: models.py:138 +#: models.py:149 msgid "Street" msgstr "" -#: models.py:139 +#: models.py:150 msgid "Street number" msgstr "" -#: models.py:140 +#: models.py:151 msgid "Postal code" msgstr "" -#: models.py:141 +#: models.py:152 msgid "Place" msgstr "" -#: models.py:143 +#: models.py:154 msgid "Home phone" msgstr "" -#: models.py:144 +#: models.py:155 msgid "Mobile phone" msgstr "" -#: models.py:148 +#: models.py:159 msgid "Date of birth" msgstr "" -#: models.py:149 +#: models.py:160 msgid "Sex" msgstr "" -#: models.py:151 +#: models.py:162 msgid "Photo" msgstr "" -#: models.py:155 templates/core/person/full.html:129 +#: models.py:166 templates/core/person/full.html:137 msgid "Guardians / Parents" msgstr "" -#: models.py:162 +#: models.py:173 msgid "Primary group" msgstr "" -#: models.py:165 models.py:423 models.py:447 models.py:532 models.py:720 -#: templates/core/person/full.html:112 +#: models.py:176 models.py:461 models.py:485 models.py:570 models.py:805 +#: templates/core/person/full.html:120 msgid "Description" msgstr "" -#: models.py:296 +#: models.py:313 msgid "Title of field" msgstr "" -#: models.py:298 +#: models.py:315 msgid "Type of field" msgstr "" -#: models.py:305 +#: models.py:322 msgid "Addtitional field for groups" msgstr "" -#: models.py:306 +#: models.py:323 msgid "Addtitional fields for groups" msgstr "" -#: models.py:318 +#: models.py:337 msgid "Group" msgstr "" -#: models.py:320 +#: models.py:340 msgid "Can assign child groups to groups" msgstr "" -#: models.py:330 +#: models.py:341 +msgid "Can view statistics about group." +msgstr "" + +#: models.py:352 msgid "Long name" msgstr "" -#: models.py:340 templates/core/group/full.html:65 +#: models.py:362 templates/core/group/full.html:85 msgid "Members" msgstr "" -#: models.py:343 templates/core/group/full.html:62 +#: models.py:365 templates/core/group/full.html:82 msgid "Owners" msgstr "" -#: models.py:350 templates/core/group/full.html:54 +#: models.py:372 templates/core/group/full.html:55 msgid "Parent groups" msgstr "" -#: models.py:358 +#: models.py:380 msgid "Type of group" msgstr "" -#: models.py:419 +#: models.py:457 msgid "User" msgstr "" -#: models.py:422 models.py:446 models.py:531 +#: models.py:460 models.py:484 models.py:569 #: templates/core/announcement/list.html:18 msgid "Title" msgstr "" -#: models.py:425 +#: models.py:463 msgid "Application" msgstr "" -#: models.py:431 +#: models.py:469 msgid "Activity" msgstr "" -#: models.py:432 +#: models.py:470 msgid "Activities" msgstr "" -#: models.py:438 +#: models.py:476 msgid "Sender" msgstr "" -#: models.py:443 +#: models.py:481 msgid "Recipient" msgstr "" -#: models.py:448 models.py:701 +#: models.py:486 models.py:786 msgid "Link" msgstr "" -#: models.py:450 +#: models.py:488 msgid "Read" msgstr "" -#: models.py:451 +#: models.py:489 msgid "Sent" msgstr "" -#: models.py:464 +#: models.py:502 msgid "Notification" msgstr "" -#: models.py:465 +#: models.py:503 msgid "Notifications" msgstr "" -#: models.py:533 +#: models.py:571 msgid "Link to detailed view" msgstr "" -#: models.py:536 +#: models.py:574 msgid "Date and time from when to show" msgstr "" -#: models.py:539 +#: models.py:577 msgid "Date and time until when to show" msgstr "" -#: models.py:563 +#: models.py:601 msgid "Announcement" msgstr "" -#: models.py:601 +#: models.py:639 msgid "Announcement recipient" msgstr "" -#: models.py:602 +#: models.py:640 msgid "Announcement recipients" msgstr "" -#: models.py:652 +#: models.py:690 msgid "Widget Title" msgstr "" -#: models.py:653 +#: models.py:691 msgid "Activate Widget" msgstr "" -#: models.py:671 +#: models.py:694 +msgid "Size on mobile devices" +msgstr "" + +#: models.py:695 +msgid "<= 600 px, 12 columns" +msgstr "" + +#: models.py:700 +msgid "Size on tablet devices" +msgstr "" + +#: models.py:701 +msgid "> 600 px, 12 columns" +msgstr "" + +#: models.py:706 +msgid "Size on desktop devices" +msgstr "" + +#: models.py:707 +msgid "> 992 px, 12 columns" +msgstr "" + +#: models.py:712 +msgid "Size on large desktop devices" +msgstr "" + +#: models.py:713 +msgid "> 1200 px>, 12 columns" +msgstr "" + +#: models.py:734 +msgid "Can edit default dashboard" +msgstr "" + +#: models.py:735 msgid "Dashboard Widget" msgstr "" -#: models.py:672 +#: models.py:736 msgid "Dashboard Widgets" msgstr "" -#: models.py:678 +#: models.py:741 +msgid "Dashboard widget" +msgstr "" + +#: models.py:746 +msgid "Order" +msgstr "" + +#: models.py:747 +msgid "Part of the default dashboard" +msgstr "" + +#: models.py:755 +msgid "Dashboard widget order" +msgstr "" + +#: models.py:756 +msgid "Dashboard widget orders" +msgstr "" + +#: models.py:762 msgid "Menu ID" msgstr "" -#: models.py:690 +#: models.py:775 msgid "Custom menu" msgstr "" -#: models.py:691 +#: models.py:776 msgid "Custom menus" msgstr "" -#: models.py:698 +#: models.py:783 msgid "Menu" msgstr "" -#: models.py:702 +#: models.py:787 msgid "Icon" msgstr "" -#: models.py:708 +#: models.py:793 msgid "Custom menu item" msgstr "" -#: models.py:709 +#: models.py:794 msgid "Custom menu items" msgstr "" -#: models.py:719 +#: models.py:804 msgid "Title of type" msgstr "" -#: models.py:726 templates/core/group/full.html:46 +#: models.py:811 templates/core/group/full.html:47 msgid "Group type" msgstr "" -#: models.py:736 +#: models.py:821 msgid "Can view system status" msgstr "" -#: models.py:737 +#: models.py:822 msgid "Can link persons to accounts" msgstr "" -#: models.py:738 +#: models.py:823 msgid "Can manage data" msgstr "" -#: models.py:739 +#: models.py:824 msgid "Can impersonate" msgstr "" -#: models.py:740 +#: models.py:825 msgid "Can use search" msgstr "" -#: models.py:741 +#: models.py:826 msgid "Can change site preferences" msgstr "" -#: models.py:742 +#: models.py:827 msgid "Can change person preferences" msgstr "" -#: models.py:743 +#: models.py:828 msgid "Can change group preferences" msgstr "" -#: preferences.py:24 +#: models.py:864 +msgid "Related data check task" +msgstr "" + +#: models.py:872 +msgid "Issue solved" +msgstr "" + +#: models.py:873 +msgid "Notification sent" +msgstr "" + +#: models.py:886 +msgid "Data check result" +msgstr "" + +#: models.py:887 +msgid "Data check results" +msgstr "" + +#: models.py:889 +msgid "Can run data checks" +msgstr "" + +#: models.py:890 +msgid "Can solve data check problems" +msgstr "" + +#: preferences.py:27 msgid "Authentication" msgstr "" -#: preferences.py:33 +#: preferences.py:28 +msgid "Internationalisation" +msgstr "" + +#: preferences.py:37 msgid "Site title" msgstr "" -#: preferences.py:42 +#: preferences.py:46 msgid "Site description" msgstr "" -#: preferences.py:51 +#: preferences.py:55 msgid "Primary colour" msgstr "" -#: preferences.py:60 +#: preferences.py:64 msgid "Secondary colour" msgstr "" -#: preferences.py:68 +#: preferences.py:72 msgid "Logo" msgstr "" -#: preferences.py:76 +#: preferences.py:80 msgid "Favicon" msgstr "" -#: preferences.py:84 +#: preferences.py:88 msgid "PWA-Icon" msgstr "" -#: preferences.py:93 +#: preferences.py:97 msgid "Mail out name" msgstr "" -#: preferences.py:102 +#: preferences.py:106 msgid "Mail out address" msgstr "" -#: preferences.py:112 +#: preferences.py:116 msgid "Link to privacy policy" msgstr "" -#: preferences.py:122 +#: preferences.py:126 msgid "Link to imprint" msgstr "" -#: preferences.py:132 +#: preferences.py:136 msgid "Name format for addressing" msgstr "" -#: preferences.py:146 +#: preferences.py:150 msgid "Channels to use for notifications" msgstr "" -#: preferences.py:156 +#: preferences.py:160 msgid "Regular expression to match primary group, e.g. '^Class .*'" msgstr "" -#: preferences.py:165 +#: preferences.py:169 msgid "Field on person to match primary group against" msgstr "" -#: preferences.py:177 +#: preferences.py:181 msgid "Display name of the school" msgstr "" -#: preferences.py:186 +#: preferences.py:190 msgid "Official name of the school, e.g. as given by supervisory authority" msgstr "" -#: preferences.py:194 +#: preferences.py:198 msgid "Enabled custom authentication backends" msgstr "" -#: settings.py:300 +#: preferences.py:211 +msgid "Available languages" +msgstr "" + +#: preferences.py:223 +msgid "Send emails if data checks detect problems" +msgstr "" + +#: preferences.py:234 +msgid "Email recipients for data checks problem emails" +msgstr "" + +#: preferences.py:245 +msgid "Email recipient groups for data checks problem emails" +msgstr "" + +#: settings.py:322 msgid "English" msgstr "" -#: settings.py:301 +#: settings.py:323 msgid "German" msgstr "" -#: settings.py:302 +#: settings.py:324 msgid "French" msgstr "" -#: settings.py:303 +#: settings.py:325 msgid "Norwegian (bokmål)" msgstr "" #: tables.py:19 templates/core/announcement/list.html:36 -#: templates/core/group/full.html:23 templates/core/person/full.html:22 +#: templates/core/group/full.html:24 templates/core/person/full.html:23 msgid "Edit" msgstr "" -#: tables.py:21 templates/core/announcement/list.html:22 +#: tables.py:21 tables.py:89 templates/core/announcement/list.html:22 msgid "Actions" msgstr "" -#: tables.py:56 tables.py:57 tables.py:71 -#: templates/core/announcement/list.html:42 templates/core/group/full.html:30 -#: templates/core/pages/delete.html:22 templates/core/person/full.html:29 +#: tables.py:56 tables.py:57 tables.py:71 tables.py:87 +#: templates/core/announcement/list.html:42 templates/core/group/full.html:31 +#: templates/core/pages/delete.html:22 templates/core/person/full.html:30 msgid "Delete" msgstr "" @@ -759,21 +890,21 @@ msgstr "" msgid "Create additional field" msgstr "" -#: templates/core/announcement/form.html:10 -#: templates/core/announcement/form.html:17 +#: templates/core/announcement/form.html:14 +#: templates/core/announcement/form.html:21 msgid "Edit announcement" msgstr "" -#: templates/core/announcement/form.html:12 +#: templates/core/announcement/form.html:16 msgid "Publish announcement" msgstr "" -#: templates/core/announcement/form.html:19 +#: templates/core/announcement/form.html:23 #: templates/core/announcement/list.html:13 msgid "Publish new announcement" msgstr "" -#: templates/core/announcement/form.html:30 +#: templates/core/announcement/form.html:34 msgid "Save und publish announcement" msgstr "" @@ -813,6 +944,120 @@ msgstr "" msgid "Powered by AlekSIS" msgstr "" +#: templates/core/dashboard_widget/create.html:8 +#: templates/core/dashboard_widget/create.html:12 +#, python-format +msgid "Create %(widget)s" +msgstr "" + +#: templates/core/dashboard_widget/edit.html:8 +#: templates/core/dashboard_widget/edit.html:12 +#, python-format +msgid "Edit %(widget)s" +msgstr "" + +#: templates/core/dashboard_widget/list.html:17 +#, python-format +msgid "Create %(name)s" +msgstr "" + +#: templates/core/dashboard_widget/list.html:25 +#: templates/core/edit_dashboard.html:8 templates/core/edit_dashboard.html:15 +msgid "Edit default dashboard" +msgstr "" + +#: templates/core/data_check/list.html:15 +msgid "Check data again" +msgstr "" + +#: templates/core/data_check/list.html:22 +msgid "The system detected some problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:23 +msgid "" +"Please go through all data and check whether some extra action is\n" +" needed." +msgstr "" + +#: templates/core/data_check/list.html:31 +msgid "Everything is fine." +msgstr "" + +#: templates/core/data_check/list.html:32 +msgid "The system hasn't detected any problems with your data." +msgstr "" + +#: templates/core/data_check/list.html:40 +msgid "Detected problems" +msgstr "" + +#: templates/core/data_check/list.html:45 +msgid "Affected object" +msgstr "" + +#: templates/core/data_check/list.html:46 +msgid "Detected problem" +msgstr "" + +#: templates/core/data_check/list.html:47 +msgid "Show details" +msgstr "" + +#: templates/core/data_check/list.html:48 +msgid "Options to solve the problem" +msgstr "" + +#: templates/core/data_check/list.html:62 +msgid "Show object" +msgstr "" + +#: templates/core/data_check/list.html:84 +msgid "Registered checks" +msgstr "" + +#: templates/core/data_check/list.html:88 +msgid "" +"\n" +" The system will check for the following problems:\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:6 templates/core/edit_dashboard.html:13 +#: templates/core/index.html:14 +msgid "Edit dashboard" +msgstr "" + +#: templates/core/edit_dashboard.html:24 +msgid "" +"\n" +" On this page you can arrange your personal dashboard. You can drag any items from \"Available widgets\" to \"Your\n" +" Dashboard\" or change the order by moving the widgets. After you have finished, please don't forget to click on\n" +" \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:30 +msgid "" +"\n" +" On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own\n" +" dashboard. You can drag any items from \"Available widgets\" to \"Default Dashboard\" or change the order\n" +" by moving the widgets. After you have finished, please don't forget to click on \"Save\".\n" +" " +msgstr "" + +#: templates/core/edit_dashboard.html:48 +msgid "Available widgets" +msgstr "" + +#: templates/core/edit_dashboard.html:57 +msgid "Your dashboard" +msgstr "" + +#: templates/core/edit_dashboard.html:59 +msgid "Default dashboard" +msgstr "" + #: templates/core/group/child_groups.html:18 msgid "" "\n" @@ -885,14 +1130,38 @@ msgstr "" msgid "Save and next" msgstr "" -#: templates/core/group/edit.html:6 templates/core/group/edit.html:7 +#: templates/core/group/edit.html:11 templates/core/group/edit.html:12 msgid "Edit group" msgstr "" -#: templates/core/group/full.html:37 templates/core/person/full.html:36 +#: templates/core/group/full.html:38 templates/core/person/full.html:37 msgid "Change preferences" msgstr "" +#: templates/core/group/full.html:64 +msgid "Statistics" +msgstr "" + +#: templates/core/group/full.html:67 +msgid "Count of members" +msgstr "" + +#: templates/core/group/full.html:71 +msgid "Average age" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "Age range" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years to" +msgstr "" + +#: templates/core/group/full.html:76 +msgid "years " +msgstr "" + #: templates/core/group/list.html:14 msgid "Create group" msgstr "" @@ -921,23 +1190,31 @@ msgstr "" msgid "Home" msgstr "" -#: templates/core/index.html:42 +#: templates/core/index.html:50 +msgid "" +"\n" +" You didn't customise your dashboard so that you see the system default. Please click on \"Edit dashboard\" to\n" +" customise your personal dashboard.\n" +" " +msgstr "" + +#: templates/core/index.html:59 msgid "Last activities" msgstr "" -#: templates/core/index.html:60 +#: templates/core/index.html:77 msgid "No activities available yet." msgstr "" -#: templates/core/index.html:65 +#: templates/core/index.html:82 msgid "Recent notifications" msgstr "" -#: templates/core/index.html:81 +#: templates/core/index.html:98 msgid "More information →" msgstr "" -#: templates/core/index.html:88 +#: templates/core/index.html:105 msgid "No notifications available yet." msgstr "" @@ -1016,21 +1293,6 @@ msgid "" " " msgstr "" -#: templates/core/pages/offline.html:6 -msgid "" -"No internet\n" -" connection." -msgstr "" - -#: templates/core/pages/offline.html:10 -msgid "" -"\n" -" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" -" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" -" administrators:\n" -" " -msgstr "" - #: templates/core/pages/progress.html:27 msgid "" "\n" @@ -1150,37 +1412,19 @@ msgid "" " " msgstr "" -#: templates/core/partials/crud_events.html:10 -#, python-format -msgid "" -"\n" -" Created by %(person)s\n" -" " -msgstr "" - -#: templates/core/partials/crud_events.html:14 -#: templates/core/partials/crud_events.html:22 -#: templates/core/partials/crud_events.html:26 -#, python-format -msgid "" -"\n" -" Updated by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Changed by" msgstr "" -#: templates/core/partials/crud_events.html:18 -#, python-format -msgid "" -"\n" -" Deleted by %(person)s\n" -" " +#: templates/core/partials/crud_events.html:15 +msgid "Unknown" msgstr "" -#: templates/core/partials/language_form.html:16 +#: templates/core/partials/language_form.html:15 msgid "Language" msgstr "" -#: templates/core/partials/language_form.html:28 +#: templates/core/partials/language_form.html:27 msgid "Select language" msgstr "" @@ -1201,11 +1445,12 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:7 templates/core/person/accounts.html:9 +#: templates/core/person/accounts.html:12 +#: templates/core/person/accounts.html:14 msgid "Link persons to accounts" msgstr "" -#: templates/core/person/accounts.html:16 +#: templates/core/person/accounts.html:21 msgid "" "\n" " You can use this form to assign user accounts to persons. Use the\n" @@ -1215,28 +1460,32 @@ msgid "" " " msgstr "" -#: templates/core/person/accounts.html:31 -#: templates/core/person/accounts.html:55 +#: templates/core/person/accounts.html:36 +#: templates/core/person/accounts.html:60 msgid "Update" msgstr "" -#: templates/core/person/accounts.html:37 +#: templates/core/person/accounts.html:42 msgid "Existing account" msgstr "" -#: templates/core/person/accounts.html:38 +#: templates/core/person/accounts.html:43 msgid "New account" msgstr "" -#: templates/core/person/edit.html:11 templates/core/person/edit.html:12 +#: templates/core/person/edit.html:12 templates/core/person/edit.html:13 msgid "Edit person" msgstr "" -#: templates/core/person/full.html:42 +#: templates/core/person/full.html:44 +msgid "Impersonate" +msgstr "" + +#: templates/core/person/full.html:50 msgid "Contact details" msgstr "" -#: templates/core/person/full.html:122 +#: templates/core/person/full.html:130 msgid "Children" msgstr "" @@ -1280,14 +1529,29 @@ msgstr "" msgid "Save preferences" msgstr "" -#: templates/dynamic_preferences/sections.html:7 -msgid "All" -msgstr "" - #: templates/impersonate/list_users.html:8 msgid "Impersonate user" msgstr "" +#: templates/offline.html:5 +msgid "Network error" +msgstr "" + +#: templates/offline.html:8 +msgid "" +"No internet\n" +" connection." +msgstr "" + +#: templates/offline.html:12 +msgid "" +"\n" +" There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n" +" or mobile data is turned on and try again. If you think you are connected, please contact the system\n" +" administrators:\n" +" " +msgstr "" + #: templates/search/search.html:8 msgid "Global Search" msgstr "" @@ -1308,6 +1572,39 @@ msgstr "" msgid "Please enter a search term above." msgstr "" +#: templates/templated_email/data_checks.email:4 +msgid "The system detected some new problems with your data." +msgstr "" + +#: templates/templated_email/data_checks.email:8 +#: templates/templated_email/data_checks.email:24 +msgid "Hello," +msgstr "" + +#: templates/templated_email/data_checks.email:10 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:26 +msgid "" +"\n" +" the system detected some new problems with your data.\n" +" Please take some time to inspect them and solve the issues or mark them as ignored.\n" +" " +msgstr "" + +#: templates/templated_email/data_checks.email:34 +msgid "Problem description" +msgstr "" + +#: templates/templated_email/data_checks.email:35 +msgid "Count of objects with new problems" +msgstr "" + #: templates/templated_email/notification.email:3 msgid "New notification for" msgstr "" @@ -1690,58 +1987,91 @@ msgstr "" msgid "SMS" msgstr "" -#: views.py:122 +#: views.py:141 msgid "The school term has been created." msgstr "" -#: views.py:133 +#: views.py:153 msgid "The school term has been saved." msgstr "" -#: views.py:273 +#: views.py:298 msgid "The child groups were successfully saved." msgstr "" -#: views.py:309 +#: views.py:336 msgid "The person has been saved." msgstr "" -#: views.py:346 +#: views.py:375 msgid "The group has been saved." msgstr "" -#: views.py:434 +#: views.py:467 msgid "The announcement has been saved." msgstr "" -#: views.py:450 +#: views.py:483 msgid "The announcement has been deleted." msgstr "" -#: views.py:521 +#: views.py:562 msgid "The preferences have been saved successfully." msgstr "" -#: views.py:544 +#: views.py:586 msgid "The person has been deleted." msgstr "" -#: views.py:557 +#: views.py:600 msgid "The group has been deleted." msgstr "" -#: views.py:588 +#: views.py:632 msgid "The additional_field has been saved." msgstr "" -#: views.py:622 +#: views.py:666 msgid "The additional field has been deleted." msgstr "" -#: views.py:646 +#: views.py:691 msgid "The group type has been saved." msgstr "" -#: views.py:676 +#: views.py:721 msgid "The group type has been deleted." msgstr "" + +#: views.py:749 +msgid "The data check has been started. Please note that it may take a while before you are able to fetch the data on this page." +msgstr "" + +#: views.py:754 +msgid "The data check has finished." +msgstr "" + +#: views.py:769 +#, python-brace-format +msgid "The solve option '{solve_option_obj.verbose_name}' " +msgstr "" + +#: views.py:811 +msgid "The dashboard widget has been saved." +msgstr "" + +#: views.py:841 +msgid "The dashboard widget has been created." +msgstr "" + +#: views.py:851 +msgid "The dashboard widget has been deleted." +msgstr "" + +#: views.py:914 +msgid "Your dashboard configuration has been saved successfully." +msgstr "" + +#: views.py:916 +msgid "The configuration of the default dashboard has been saved successfully." +msgstr "" diff --git a/aleksis/core/locale/tr_TR/LC_MESSAGES/djangojs.po b/aleksis/core/locale/tr_TR/LC_MESSAGES/djangojs.po index 9dbef6e6f74f842b0e8f25e389edf1a852cd8d99..ea24fc3ff92afc211898fb8b426dccb97fce56ed 100644 --- a/aleksis/core/locale/tr_TR/LC_MESSAGES/djangojs.po +++ b/aleksis/core/locale/tr_TR/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-02 16:29+0200\n" +"POT-Creation-Date: 2021-01-11 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,14 +17,18 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: static/js/main.js:21 +#: static/js/main.js:15 msgid "Today" msgstr "" -#: static/js/main.js:22 +#: static/js/main.js:16 msgid "Cancel" msgstr "" -#: static/js/main.js:23 +#: static/js/main.js:17 msgid "OK" msgstr "" + +#: static/js/main.js:118 +msgid "This page may contain outdated information since there is no internet connection." +msgstr "" diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py index 9b1c42e931235333fb8d4a9dca55cd7f977b47de..fc9ba63604dabf57e1c4101fc8d7f5700548cc6c 100644 --- a/aleksis/core/menus.py +++ b/aleksis/core/menus.py @@ -142,6 +142,12 @@ MENUS = { ), ], }, + { + "name": _("Data checks"), + "url": "check_data", + "icon": "done_all", + "validators": ["menu_generator.validators.is_superuser"], + }, { "name": _("Backend Admin"), "url": "admin:index", diff --git a/aleksis/core/migrations/0008_data_check_result.py b/aleksis/core/migrations/0008_data_check_result.py new file mode 100644 index 0000000000000000000000000000000000000000..3f8285e22a6af4109a67d6baddccd05e69b1bd6e --- /dev/null +++ b/aleksis/core/migrations/0008_data_check_result.py @@ -0,0 +1,63 @@ +# Generated by Django 3.1.3 on 2020-11-14 16:11 + +import django.contrib.sites.managers +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ("sites", "0002_alter_domain_unique"), + ("contenttypes", "0002_remove_content_type_name"), + ("core", "0007_dashboard_widget_order"), + ] + + operations = [ + migrations.CreateModel( + name="DataCheckResult", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("extended_data", models.JSONField(default=dict, editable=False)), + ( + "check", + models.CharField( + choices=[], max_length=255, verbose_name="Related data check task" + ), + ), + ("object_id", models.CharField(max_length=255)), + ("solved", models.BooleanField(default=False, verbose_name="Issue solved")), + ("sent", models.BooleanField(default=False, verbose_name="Notification sent")), + ( + "content_type", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="contenttypes.contenttype" + ), + ), + ( + "site", + models.ForeignKey( + default=1, + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to="sites.site", + ), + ), + ], + options={ + "permissions": ( + ("run_data_checks", "Can run data checks"), + ("solve_data_problem", "Can solve data check problems"), + ), + "verbose_name": "Data check result", + "verbose_name_plural": "Data check results", + }, + managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),], + ), + ] diff --git a/aleksis/core/migrations/0009_default_dashboard.py b/aleksis/core/migrations/0009_default_dashboard.py new file mode 100644 index 0000000000000000000000000000000000000000..401396bb064139d5c9bdda37f1c9a5dece65dc22 --- /dev/null +++ b/aleksis/core/migrations/0009_default_dashboard.py @@ -0,0 +1,30 @@ +# Generated by Django 3.1.4 on 2021-01-04 13:39 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_data_check_result'), + ] + + operations = [ + migrations.AddField( + model_name='dashboardwidgetorder', + name='default', + field=models.BooleanField(default=False, verbose_name='Part of the default dashboard'), + ), + migrations.AlterField( + model_name='dashboardwidgetorder', + name='person', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.person', verbose_name='Person'), + ), + migrations.AlterModelOptions( + name='dashboardwidget', + options={'permissions': (('edit_default_dashboard', 'Can edit default dashboard'),), + 'verbose_name': 'Dashboard Widget', 'verbose_name_plural': 'Dashboard Widgets'}, + ), + ] diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py index e44caa020bdb6fb1814a3b7e10de95d72915b26b..3cad91f2c80301d8d73268e32f58d7773c5a45c8 100644 --- a/aleksis/core/mixins.py +++ b/aleksis/core/mixins.py @@ -47,6 +47,28 @@ class _ExtensibleModelBase(models.base.ModelBase): return mcls +def _generate_one_to_one_proxy_property(field, subfield): + def getter(self): + if hasattr(self, field.name): + related = getattr(self, field.name) + return getattr(related, subfield.name) + # Related instane does not exist + return None + + def setter(self, val): + if hasattr(self, field.name): + related = getattr(self, field.name) + else: + # Auto-create related instance (but do not save) + related = field.related_model() + setattr(related, field.remote_field.name, self) + # Ensure the related model is saved later + self._save_reverse = getattr(self, "_save_reverse", []) + [related] + setattr(related, subfield.name, val) + + return property(getter, setter) + + class ExtensibleModel(models.Model, metaclass=_ExtensibleModelBase): """Base model for all objects in AlekSIS apps. @@ -248,13 +270,51 @@ class ExtensibleModel(models.Model, metaclass=_ExtensibleModelBase): to.property_(_virtual_related, related_name) @classmethod - def syncable_fields(cls) -> List[models.Field]: - """Collect all fields that can be synced on a model.""" - return [ - field - for field in cls._meta.fields - if (field.editable and not field.auto_created and not field.is_relation) - ] + def syncable_fields( + cls, recursive: bool = True, exclude_remotes: List = [] + ) -> List[models.Field]: + """Collect all fields that can be synced on a model. + + If recursive is True, it recurses into related models and generates virtual + proxy fields to access fields in related models.""" + fields = [] + for field in cls._meta.get_fields(): + if field.is_relation and field.one_to_one and recursive: + if ExtensibleModel not in field.related_model.__mro__: + # Related model is not extensible and thus has no syncable fields + continue + if field.related_model in exclude_remotes: + # Remote is excluded, probably to avoid recursion + continue + + # Recurse into related model to get its fields as well + for subfield in field.related_model.syncable_fields( + recursive, exclude_remotes + [cls] + ): + # generate virtual field names for proxy access + name = f"_{field.name}__{subfield.name}" + verbose_name = f"{field.name} ({field.related_model._meta.verbose_name}) → {subfield.verbose_name}" + + if not hasattr(cls, name): + # Add proxy properties to handle access to related model + setattr(cls, name, _generate_one_to_one_proxy_property(field, subfield)) + + # Generate a fake field class with enough API to detect attribute names + fields.append( + type( + "FakeRelatedProxyField", + (), + { + "name": name, + "verbose_name": verbose_name, + "to_python": lambda v: subfield.to_python(v), + }, + ) + ) + elif field.editable and not field.auto_created: + fields.append(field) + + return fields @classmethod def syncable_fields_choices(cls) -> Tuple[Tuple[str, str]]: @@ -273,6 +333,16 @@ class ExtensibleModel(models.Model, metaclass=_ExtensibleModelBase): """Dynamically add a new permission to a model.""" cls.extra_permissions.append((name, verbose_name)) + def save(self, *args, **kwargs): + """Ensure all functionality of our extensions that needs saving gets it.""" + # For auto-created remote syncable fields + if hasattr(self, "_save_reverse"): + for related in self._save_reverse: + related.save() + del self._save_reverse + + super().save(*args, **kwargs) + class Meta: abstract = True diff --git a/aleksis/core/models.py b/aleksis/core/models.py index 0f7a80d416cfe1dec30b57f2bf1e070e664a8966..8927844a47da2f30e030a9b0815e82539940d8ba 100644 --- a/aleksis/core/models.py +++ b/aleksis/core/models.py @@ -26,6 +26,8 @@ from model_utils.models import TimeStampedModel from phonenumber_field.modelfields import PhoneNumberField from polymorphic.models import PolymorphicModel +from aleksis.core.data_checks import DataCheck, DataCheckRegistry + from .managers import ( CurrentSiteManagerWithoutMigrations, GroupManager, @@ -237,7 +239,10 @@ class Person(ExtensibleModel): @property def dashboard_widgets(self): return [ - w.widget for w in DashboardWidgetOrder.objects.filter(person=self).order_by("order") + w.widget + for w in DashboardWidgetOrder.objects.filter(person=self, widget__active=True).order_by( + "order" + ) ] def save(self, *args, **kwargs): @@ -492,7 +497,7 @@ class Notification(ExtensibleModel, TimeStampedModel): def save(self, **kwargs): super().save(**kwargs) if not self.sent: - transaction.on_commit(lambda: send_notification(self.pk, resend=True)) + send_notification(self.pk, resend=True) self.sent = True super().save(**kwargs) @@ -729,6 +734,7 @@ class DashboardWidget(PolymorphicModel, PureDjangoModel): return self.title class Meta: + permissions = (("edit_default_dashboard", _("Can edit default dashboard")),) verbose_name = _("Dashboard Widget") verbose_name_plural = _("Dashboard Widgets") @@ -737,8 +743,21 @@ class DashboardWidgetOrder(ExtensibleModel): widget = models.ForeignKey( DashboardWidget, on_delete=models.CASCADE, verbose_name=_("Dashboard widget") ) - person = models.ForeignKey(Person, on_delete=models.CASCADE, verbose_name=_("Person")) + person = models.ForeignKey( + Person, on_delete=models.CASCADE, verbose_name=_("Person"), null=True, blank=True + ) order = models.PositiveIntegerField(verbose_name=_("Order")) + default = models.BooleanField(default=False, verbose_name=_("Part of the default dashboard")) + + @classproperty + def default_dashboard_widgets(cls): + """Get default order for dashboard widgets.""" + return [ + w.widget + for w in cls.objects.filter(person=None, default=True, widget__active=True).order_by( + "order" + ) + ] class Meta: verbose_name = _("Dashboard widget order") @@ -843,3 +862,38 @@ class GroupPreferenceModel(PerInstancePreferenceModel, PureDjangoModel): class Meta: app_label = "core" + + +class DataCheckResult(ExtensibleModel): + """Save the result of a data check for a specific object.""" + + check = models.CharField( + max_length=255, + verbose_name=_("Related data check task"), + choices=DataCheckRegistry.data_checks_choices, + ) + + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.CharField(max_length=255) + related_object = GenericForeignKey("content_type", "object_id") + + solved = models.BooleanField(default=False, verbose_name=_("Issue solved")) + sent = models.BooleanField(default=False, verbose_name=_("Notification sent")) + + @property + def related_check(self) -> DataCheck: + return DataCheckRegistry.data_checks_by_name[self.check] + + def solve(self, solve_option: str = "default"): + self.related_check.solve(self, solve_option) + + def __str__(self): + return f"{self.related_object}: {self.related_check.problem_name}" + + class Meta: + verbose_name = _("Data check result") + verbose_name_plural = _("Data check results") + permissions = ( + ("run_data_checks", _("Can run data checks")), + ("solve_data_problem", _("Can solve data check problems")), + ) diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py index b962f27bba83374a2f331af43a913362cd6d71ad..6594e3b1ddf8c48e038de228c0b9e22c13a70ec0 100644 --- a/aleksis/core/preferences.py +++ b/aleksis/core/preferences.py @@ -8,11 +8,12 @@ from dynamic_preferences.types import ( BooleanPreference, ChoicePreference, FilePreference, + ModelMultipleChoicePreference, MultipleChoicePreference, StringPreference, ) -from .models import Person +from .models import Group, Person from .registries import person_preferences_registry, site_preferences_registry from .util.notifications import get_notification_choices_lazy @@ -228,3 +229,35 @@ class AvailableLanguages(MultipleChoicePreference): verbose_name = _("Available languages") field_attribute = {"initial": []} choices = settings.LANGUAGES + + +@site_preferences_registry.register +class DataChecksSendEmails(BooleanPreference): + """Enable email sending if data checks detect problems.""" + + section = general + name = "data_checks_send_emails" + default = False + verbose_name = _("Send emails if data checks detect problems") + + +@site_preferences_registry.register +class DataChecksEmailsRecipients(ModelMultipleChoicePreference): + """Email recipients for data check problem emails.""" + + section = general + name = "data_checks_recipients" + default = [] + model = Person + verbose_name = _("Email recipients for data checks problem emails") + + +@site_preferences_registry.register +class DataChecksEmailsRecipientGroups(ModelMultipleChoicePreference): + """Email recipient groups for data check problem emails.""" + + section = general + name = "data_checks_recipient_groups" + default = [] + model = Group + verbose_name = _("Email recipient groups for data checks problem emails") diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py index 2e36bc08bf3a307c0b8a116a5731f0f4793f2475..ac713bbe54a937d529da6c4e0266209dae593fd5 100644 --- a/aleksis/core/rules.py +++ b/aleksis/core/rules.py @@ -279,6 +279,21 @@ view_group_stats_predicate = has_person & ( ) rules.add_perm("core.view_group_stats", view_group_stats_predicate) +# View data check results +view_data_check_results_predicate = has_person & has_global_perm("core.view_datacheckresult") +rules.add_perm("core.view_datacheckresults", view_data_check_results_predicate) + +# Run data checks +run_data_checks_predicate = ( + has_person & view_data_check_results_predicate & has_global_perm("core.run_data_checks") +) +rules.add_perm("core.run_data_checks", run_data_checks_predicate) + +# Solve data problems +solve_data_problem_predicate = ( + has_person & view_data_check_results_predicate & has_global_perm("core.solve_data_problem") +) +rules.add_perm("core.solve_data_problem", solve_data_problem_predicate) view_dashboard_widget_predicate = has_person & has_global_perm("core.view_dashboardwidget") rules.add_perm("core.view_dashboardwidget", view_dashboard_widget_predicate) @@ -291,3 +306,6 @@ rules.add_perm("core.edit_dashboardwidget", edit_dashboard_widget_predicate) delete_dashboard_widget_predicate = has_person & has_global_perm("core.delete_dashboardwidget") rules.add_perm("core.delete_dashboardwidget", delete_dashboard_widget_predicate) + +edit_default_dashboard_predicate = has_person & has_global_perm("core.edit_default_dashboard") +rules.add_perm("core.edit_default_dashboard", edit_default_dashboard_predicate) diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py index e8870dc656e05c566ab1e83131d7fc4c70e0b9d5..f6f8f538345f76bbfd3a340ae748c9bbc77e3f09 100644 --- a/aleksis/core/settings.py +++ b/aleksis/core/settings.py @@ -377,7 +377,7 @@ ANY_JS = { "css_url": JS_URL + "/select2-materialize/select2-materialize.css", "js_url": JS_URL + "/select2-materialize/index.js", }, - "sortablejs": {"js_url": JS_URL + "/sortablejs/dist/sortable.umd.js"}, + "sortablejs": {"js_url": JS_URL + "/sortablejs/Sortable.min.js"}, "jquery-sortablejs": {"js_url": JS_URL + "/jquery-sortablejs/jquery-sortable.js"}, } diff --git a/aleksis/core/static/js/main.js b/aleksis/core/static/js/main.js index 615f8b287df18ed38c57d1f44358073bb74bcf61..45fee5c5bde0eb54655b2fe556c05f9aee573c4f 100644 --- a/aleksis/core/static/js/main.js +++ b/aleksis/core/static/js/main.js @@ -58,6 +58,9 @@ $(document).ready(function () { // Initialize select [MAT] $('select').formSelect(); + // Initialize dropdown [MAT] + $('.dropdown-trigger').dropdown(); + // If JS is activated, the language form will be auto-submitted $('.language-field select').change(function () { @@ -110,3 +113,11 @@ $(document).ready(function () { el.addClass("closed").removeClass("opened"); }); }); + +// Show notice if serviceworker broadcasts that the current page comes from its cache +const channel = new BroadcastChannel("cache-or-not"); +channel.addEventListener("message", event => { + if ((event.data) && !($("#cache-alert").length)) { + $("main").prepend('<div id="cache-alert" class="alert warning"><p><i class="material-icons left">warning</i>' + gettext("This page may contain outdated information since there is no internet connection.") + '</p> </div>') + } +}); diff --git a/aleksis/core/static/js/serviceworker.js b/aleksis/core/static/js/serviceworker.js index 818e27cb7d28e820431a4ac28ba6fdf0c422deb7..16382ec00c5dadb22c63060a227451377202d363 100644 --- a/aleksis/core/static/js/serviceworker.js +++ b/aleksis/core/static/js/serviceworker.js @@ -5,6 +5,10 @@ const CACHE = 'aleksis-cache'; const offlineFallbackPage = 'offline/'; +const channel = new BroadcastChannel('cache-or-not'); + +var comesFromCache = false; + self.addEventListener("install", function (event) { console.log("[AlekSIS PWA] Install Event processing."); @@ -29,6 +33,7 @@ self.addEventListener("activate", function (event) { self.addEventListener("fetch", function (event) { if (event.request.method !== "GET") return; networkFirstFetch(event); + if (comesFromCache) channel.postMessage(true); }); function networkFirstFetch(event) { @@ -38,6 +43,7 @@ function networkFirstFetch(event) { // If request was successful, add or update it in the cache console.log("[AlekSIS PWA] Network request successful."); event.waitUntil(updateCache(event.request, response.clone())); + comesFromCache = false; return response; }) .catch(function (error) { @@ -56,10 +62,11 @@ function fromCache(event) { .then(function (matching) { if (!matching || matching.status === 404) { console.log("[AlekSIS PWA] Cache request failed. Serving offline fallback page."); + comesFromCache = false; // Use the precached offline page as fallback - return caches.match(offlineFallbackPage) + return caches.match(offlineFallbackPage); } - + comesFromCache = true; return matching; }); }); diff --git a/aleksis/core/static/style.scss b/aleksis/core/static/style.scss index 81f443f75ab79c34121390bf03da9ab1e611dd5b..a7b29299653c8e40d7b98304dcf2ea6f68ee4de2 100644 --- a/aleksis/core/static/style.scss +++ b/aleksis/core/static/style.scss @@ -543,6 +543,10 @@ main .alert p:first-child, main .alert div:first-child { height: 100%; } +.btn-margin { + margin-bottom: 5px; +} + /* Dashboard */ diff --git a/aleksis/core/templates/components/materialize-chips.html b/aleksis/core/templates/components/materialize-chips.html new file mode 100644 index 0000000000000000000000000000000000000000..e8a37bbb456dd47eb1231ecbc7ec541a7944be74 --- /dev/null +++ b/aleksis/core/templates/components/materialize-chips.html @@ -0,0 +1,9 @@ +<div class="chip {{ classes }}"> + {% if img %} + <img class="{{ img_classes }}" src="{{ img }}" alt="{{ alt }}"> + {% endif %} + {{ content }} + {% if close %} + <i class="close material-icons"></i> + {% endif %} +</div> diff --git a/aleksis/core/templates/core/dashboard_widget/list.html b/aleksis/core/templates/core/dashboard_widget/list.html index ab384e8620f7c6499adb3e663fdb98e4eb598b2c..6715e3df96e2faed4a1afc78333529039000de32 100644 --- a/aleksis/core/templates/core/dashboard_widget/list.html +++ b/aleksis/core/templates/core/dashboard_widget/list.html @@ -2,7 +2,7 @@ {% extends "core/base.html" %} -{% load i18n data_helpers %} +{% load i18n data_helpers rules %} {% load render_table from django_tables2 %} {% block browser_title %}{% blocktrans %}Dashboard widgets{% endblocktrans %}{% endblock %} @@ -10,13 +10,28 @@ {% block content %} - {% for ct, model in widget_types %} - <a class="btn green waves-effect waves-light" href="{% url 'create_dashboard_widget' ct.app_label ct.model %}"> - <i class="material-icons left">add</i> - {% verbose_name_object model as widget_name %} - {% blocktrans with name=widget_name %}Create {{ name }}{% endblocktrans %} + <a class="btn green waves-effect waves-light dropdown-trigger" href="#" data-target="widget-dropdown"> + <i class="material-icons left">add</i> + {% trans "Create dashboard widget" %} + </a> + <ul id="widget-dropdown" class="dropdown-content"> + {% for ct, model in widget_types %} + <li> + <a href="{% url 'create_dashboard_widget' ct.app_label ct.model %}"> + {% verbose_name_object model as widget_name %} + {% blocktrans with name=widget_name %}Create {{ name }}{% endblocktrans %} + </a> + </li> + {% endfor %} + </ul> + + {% has_perm "core.edit_default_dashboard" user as can_edit_default_dashboard %} + {% if can_edit_default_dashboard %} + <a class="btn orange waves-effect waves-light" href="{% url "edit_default_dashboard" %}"> + <i class="material-icons left">edit</i> + {% trans "Edit default dashboard" %} </a> - {% endfor %} + {% endif %} {% render_table table %} {% endblock %} diff --git a/aleksis/core/templates/core/data_check/list.html b/aleksis/core/templates/core/data_check/list.html new file mode 100644 index 0000000000000000000000000000000000000000..5a510fdd92f550ad1b7ff6b0b81983ea225420ba --- /dev/null +++ b/aleksis/core/templates/core/data_check/list.html @@ -0,0 +1,103 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} +{% load data_helpers %} + +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block browser_title %}{% blocktrans %}Data checks{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Data checks{% endblocktrans %}{% endblock %} + +{% block content %} + <a class="btn green waves-effect waves-light" href="{% url "data_check_run" %}"> + <i class="material-icons left">refresh</i> + {% trans "Check data again" %} + </a> + + {% if results %} + <div class="card"> + <div class="card-content"> + <i class="material-icons left medium red-text">warning</i> + <span class="card-title">{% trans "The system detected some problems with your data." %}</span> + <p>{% blocktrans %}Please go through all data and check whether some extra action is + needed.{% endblocktrans %}</p> + </div> + </div> + {% else %} + <div class="card"> + <div class="card-content"> + <i class="material-icons left medium green-text">check_circle</i> + <span class="card-title">{% trans "Everything is fine." %}</span> + <p>{% blocktrans %}The system hasn't detected any problems with your data.{% endblocktrans %}</p> + </div> + </div> + {% endif %} + + {% if results %} + <div class="card"> + <div class="card-content"> + <div class="card-title">{% trans "Detected problems" %}</div> + <table> + <thead> + <tr> + <th></th> + <th colspan="2">{% trans "Affected object" %}</th> + <th>{% trans "Detected problem" %}</th> + <th>{% trans "Show details" %}</th> + <th>{% trans "Options to solve the problem" %}</th> + </tr> + </thead> + <tbody> + {% for result in results %} + <tr> + <td> + <code>{{ result.id }}</code> + </td> + <td>{% verbose_name_object result.related_object %}</td> + <td>{{ result.related_object }}</td> + <td>{{ result.related_check.problem_name }}</td> + <td> + <a class="btn-flat waves-effect waves-light" href="{{ result.related_object.get_absolute_url }}"> + {% trans "Show object" %} + </a> + </td> + <td> + {% for option_name, option in result.related_check.solve_options.items %} + <a class="btn btn-margin waves-effect waves-light" + href="{% url "data_check_solve" result.pk option_name %}"> + {{ option.verbose_name }} + </a> + {% endfor %} + </td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + </div> + {% endif %} + </div> + + <div class="card hundred-percent"> + <div class="card-content"> + <div class="card-title">{% trans "Registered checks" %}</div> + <div class="alert primary"> + <div> + <i class="material-icons left">info</i> + {% blocktrans %} + The system will check for the following problems: + {% endblocktrans %} + </div> + </div> + <ul class="collection"> + {% for check in registered_checks %} + <li class="collection-item"> + <i class="material-icons left">check</i> + {{ check.verbose_name }} + </li> + {% endfor %} + </ul> + </div> + </div> +{% endblock %} diff --git a/aleksis/core/templates/core/edit_dashboard.html b/aleksis/core/templates/core/edit_dashboard.html index a15f24bff8ebcca6a7f9b4ea081d9d8c008f3b0a..09d9afd97ee8b983ed4577267641eae77fd2f14e 100644 --- a/aleksis/core/templates/core/edit_dashboard.html +++ b/aleksis/core/templates/core/edit_dashboard.html @@ -1,16 +1,38 @@ {% extends 'core/base.html' %} {% load i18n static dashboard any_js %} -{% block browser_title %}{% blocktrans %}Edit dashboard{% endblocktrans %}{% endblock %} -{% block page_title %}{% blocktrans %}Edit dashboard{% endblocktrans %}{% endblock %} +{% block browser_title %} + {% if not default_dashboard %} + {% trans "Edit dashboard" %} + {% else %} + {% trans "Edit default dashboard" %} + {% endif %} +{% endblock %} +{% block page_title %} + {% if not default_dashboard %} + {% trans "Edit dashboard" %} + {% else %} + {% trans "Edit default dashboard" %} + {% endif %} +{% endblock %} {% block content %} <div class="alert primary"> <p> <i class="material-icons left">info</i> - On this page you can arrange your personal dashboard. You can drag any items from "Available widgets" to "Your - Dashboard" or change the order by moving the widgets. After you have finished, please don't forget to click on - "Save". + {% if not default_dashboard %} + {% blocktrans %} + On this page you can arrange your personal dashboard. You can drag any items from "Available widgets" to "Your + Dashboard" or change the order by moving the widgets. After you have finished, please don't forget to click on + "Save". + {% endblocktrans %} + {% else %} + {% blocktrans %} + On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own + dashboard. You can drag any items from "Available widgets" to "Default Dashboard" or change the order + by moving the widgets. After you have finished, please don't forget to click on "Save". + {% endblocktrans %} + {% endif %} </p> </div> @@ -30,7 +52,14 @@ {% endfor %} </div> - <h5>{% trans "Your dashboard" %}</h5> + <h5> + {% if not default_dashboard %} + {% trans "Your dashboard" %} + {% else %} + {% trans "Default dashboard" %} + {% endif %} + </h5> + <div class="row card-panel grey lighten-3" id="widgets"> {% for widget in widgets %} {% include "core/partials/edit_dashboard_widget.html" %} diff --git a/aleksis/core/templates/core/index.html b/aleksis/core/templates/core/index.html index 93b80ddefffaa8cf72db56a7ef503a694f0514f5..419bb59f11b5084eee7ac379605c3cefe00d530b 100644 --- a/aleksis/core/templates/core/index.html +++ b/aleksis/core/templates/core/index.html @@ -42,19 +42,18 @@ <div class="col s{{ widget.size_s }} m{{ widget.size_m }} l{{ widget.size_l }} xl{{ widget.size_xl }}"> {% include_widget widget %} </div> - {% empty %} - <div class="col s12 grey-text center"> - <i class="material-icons medium ">widgets</i> - <p class="flow-text"> - {% blocktrans %} - You haven't selected any dashboard widgets. Please click on "Edit dashboard" to add widgets to your - personal dashboard. - {% endblocktrans %} - </p> - </div> {% endfor %} </div> + {% if default_dashboard and widgets %} + <div class="grey-text right"> + {% blocktrans %} + You didn't customise your dashboard so that you see the system default. Please click on "Edit dashboard" to + customise your personal dashboard. + {% endblocktrans %} + </div> + {% endif %} + <div class="row"> <div class="col s12 m6"> <h5>{% blocktrans %}Last activities{% endblocktrans %}</h5> diff --git a/aleksis/core/templates/dynamic_preferences/sections.html b/aleksis/core/templates/dynamic_preferences/sections.html index 39b97496fd4542cfbad440f3571eb2b6f8df86d2..344962dfdfd6ed21ff25aef9056a823904260234 100644 --- a/aleksis/core/templates/dynamic_preferences/sections.html +++ b/aleksis/core/templates/dynamic_preferences/sections.html @@ -1,11 +1,6 @@ {% load i18n %} <ul class="tabs"> <li class="tab "> - <a href="{% url registry_url %}" - class="{% if not active_section %}active{% endif %}" - target="_self"> - {% trans "All" %} - </a> {% for section in registry.section_objects.values %} <li class="tab"> <a class="{% if active_section == section.name %}active{% endif %}" diff --git a/aleksis/core/templates/templated_email/data_checks.css b/aleksis/core/templates/templated_email/data_checks.css new file mode 100644 index 0000000000000000000000000000000000000000..b385b185ecfe66dcca070e8eb024bbb091917f04 --- /dev/null +++ b/aleksis/core/templates/templated_email/data_checks.css @@ -0,0 +1,16 @@ +body { + line-height: 1.5; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-weight: normal; + color: rgba(0, 0, 0, 0.87); +} + +.count { + text-align: right; + font-family: monospace; + font-size: 14pt; +} + +td, th { + padding: 10px; +} diff --git a/aleksis/core/templates/templated_email/data_checks.email b/aleksis/core/templates/templated_email/data_checks.email new file mode 100644 index 0000000000000000000000000000000000000000..0f9b5a5307fba9152683d4e03b0c41a2105629c7 --- /dev/null +++ b/aleksis/core/templates/templated_email/data_checks.email @@ -0,0 +1,44 @@ +{% load i18n %} + +{% block subject %} + {% trans "The system detected some new problems with your data." %} +{% endblock %} + +{% block plain %} + {% trans "Hello," %} + + {% blocktrans %} + the system detected some new problems with your data. + Please take some time to inspect them and solve the issues or mark them as ignored. + {% endblocktrans %} + + {% for result in results %} + {{ result.0.problem_name }}: {{ result.1 }} + {% endfor %} +{% endblock %} + +{% block html %} + <style> + {% include "templated_email/data_checks.css" %} + </style> + <p>{% trans "Hello," %}</p> + <p> + {% blocktrans %} + the system detected some new problems with your data. + Please take some time to inspect them and solve the issues or mark them as ignored. + {% endblocktrans %} + </p> + + <table> + <tr> + <th>{% trans "Problem description" %}</th> + <th>{% trans "Count of objects with new problems" %}</th> + </tr> + {% for result in results %} + <tr> + <td>{{ result.0.problem_name }}</td> + <td class="count">{{ result.1 }}</td> + </tr> + {% endfor %} + </table> +{% endblock %} diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py index 7f0f81b0960174c8df67fb862a38e4d2bdd38164..30c38b11c9c1e884f62f88a0f62b369f55fe2969 100644 --- a/aleksis/core/urls.py +++ b/aleksis/core/urls.py @@ -154,6 +154,13 @@ urlpatterns = [ name="preferences_group", ), path("health/", include(health_urls)), + path("data_check/", views.DataCheckView.as_view(), name="check_data",), + path("data_check/run/", views.RunDataChecks.as_view(), name="data_check_run",), + path( + "data_check/<int:pk>/<str:solve_option>/", + views.SolveDataCheckView.as_view(), + name="data_check_solve", + ), path("dashboard_widgets/", views.DashboardWidgetListView.as_view(), name="dashboard_widgets"), path( "dashboard_widgets/<int:pk>/edit/", @@ -170,6 +177,12 @@ urlpatterns = [ views.DashboardWidgetCreateView.as_view(), name="create_dashboard_widget", ), + path( + "dashboard_widgets/default/", + views.EditDashboardView.as_view(), + {"default": True}, + name="edit_default_dashboard", + ), ] # Serve static files from STATIC_ROOT to make it work with runserver diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py index bef457feee2b0dd53f72591523387d912c4038b1..18fcfc241ea76d96312e1339fbde6fd526218307 100644 --- a/aleksis/core/util/core_helpers.py +++ b/aleksis/core/util/core_helpers.py @@ -14,6 +14,7 @@ else: import importlib_metadata as metadata from django.conf import settings +from django.db import transaction from django.db.models import Model, QuerySet from django.http import HttpRequest from django.shortcuts import get_object_or_404 @@ -195,6 +196,12 @@ def celery_optional(orig: Callable) -> Callable: If Celery is configured and available, it wraps the function in a Task and calls its delay method when invoked; if not, it leaves it untouched and it is executed synchronously. + + The wrapped function returns a tuple with either + the return value of the task's delay method and False + if the method has been executed asynchronously + or the return value of the executed method and True + if the method has been executed synchronously. """ if is_celery_enabled(): from ..celery import app # noqa @@ -203,9 +210,9 @@ def celery_optional(orig: Callable) -> Callable: def wrapped(*args, **kwargs): if is_celery_enabled(): - task.delay(*args, **kwargs) + return transaction.on_commit(lambda: task.delay(*args, **kwargs)), False else: - orig(*args, **kwargs) + return orig(*args, **kwargs), True return wrapped diff --git a/aleksis/core/util/forms.py b/aleksis/core/util/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..9d7279892f89f47bccebef95786df79ce1f737f1 --- /dev/null +++ b/aleksis/core/util/forms.py @@ -0,0 +1,34 @@ +from collections import OrderedDict + +from material import Layout, Row + + +class PreferenceLayout(Layout): + """django-material Layout object for managing preferences.""" + + def __init__(self, form_base_class, section=None): + """ + Create Layout object for the given form_base_class. + + :param form_base_class: A Form class used as the base. Must have a ``registry` attribute + :param section: A section where the layout builder will load preferences + """ + registry = form_base_class.registry + if section: + # Try to use section param + preferences_obj = registry.preferences(section=section) + else: + # display all preferences in the form + preferences_obj = registry.preferences() + + rows = OrderedDict() + + for preference in preferences_obj: + row_name = preference.get("row", preference.identifier()) + rows.setdefault(row_name, []) + rows[row_name].append(preference.identifier()) + + rows_material = [] + for fields in rows.values(): + rows_material.append(Row(*fields)) + super().__init__(*rows_material) diff --git a/aleksis/core/views.py b/aleksis/core/views.py index 14cbf2dbeafc7dd84edfadcd5375e97560159011..41b36acfbcff7573366a2328d2d0f9f2a92fe1df 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -5,6 +5,7 @@ from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.core.paginator import Paginator +from django.db.models import QuerySet from django.forms.models import BaseModelForm, modelform_factory from django.http import HttpRequest, HttpResponse, HttpResponseNotFound from django.shortcuts import get_object_or_404, redirect, render @@ -13,6 +14,8 @@ from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache from django.views.generic.base import View +from django.views.generic.detail import DetailView +from django.views.generic.list import ListView import reversion from django_tables2 import RequestConfig, SingleTableView @@ -23,8 +26,11 @@ from haystack.query import SearchQuerySet from haystack.views import SearchView from health_check.views import MainView from reversion import set_user +from reversion.views import RevisionMixin from rules.contrib.views import PermissionRequiredMixin, permission_required +from aleksis.core.data_checks import DataCheckRegistry, check_data + from .filters import GroupFilter, PersonFilter from .forms import ( AnnouncementForm, @@ -46,6 +52,7 @@ from .models import ( Announcement, DashboardWidget, DashboardWidgetOrder, + DataCheckResult, Group, GroupType, Notification, @@ -68,6 +75,7 @@ from .tables import ( from .util import messages from .util.apps import AppConfig from .util.core_helpers import objectgetter_optional +from .util.forms import PreferenceLayout @permission_required("core.view_dashboard") @@ -87,6 +95,12 @@ def index(request: HttpRequest) -> HttpResponse: context["announcements"] = announcements widgets = request.user.person.dashboard_widgets + + if len(widgets) == 0: + # Use default dashboard if there are no widgets + widgets = DashboardWidgetOrder.default_dashboard_widgets + context["default_dashboard"] = True + media = DashboardWidget.get_media(widgets) context["widgets"] = widgets @@ -106,7 +120,7 @@ def about(request: HttpRequest) -> HttpResponse: return render(request, "core/pages/about.html", context) -class SchoolTermListView(SingleTableView, PermissionRequiredMixin): +class SchoolTermListView(PermissionRequiredMixin, SingleTableView): """Table of all school terms.""" model = SchoolTerm @@ -116,7 +130,7 @@ class SchoolTermListView(SingleTableView, PermissionRequiredMixin): @method_decorator(never_cache, name="dispatch") -class SchoolTermCreateView(AdvancedCreateView, PermissionRequiredMixin): +class SchoolTermCreateView(PermissionRequiredMixin, AdvancedCreateView): """Create view for school terms.""" model = SchoolTerm @@ -128,7 +142,7 @@ class SchoolTermCreateView(AdvancedCreateView, PermissionRequiredMixin): @method_decorator(never_cache, name="dispatch") -class SchoolTermEditView(AdvancedEditView, PermissionRequiredMixin): +class SchoolTermEditView(PermissionRequiredMixin, AdvancedEditView): """Edit view for school terms.""" model = SchoolTerm @@ -374,7 +388,7 @@ def data_management(request: HttpRequest) -> HttpResponse: return render(request, "core/management/data_management.html", context) -class SystemStatus(MainView, PermissionRequiredMixin): +class SystemStatus(PermissionRequiredMixin, MainView): """View giving information about the system status.""" template_name = "core/pages/system_status.html" @@ -531,9 +545,16 @@ def preferences( # Invalid registry name passed from URL return HttpResponseNotFound() + if not section and len(registry.sections()) > 0: + default_section = list(registry.sections())[0] + return redirect(f"preferences_{registry_name}", default_section) + # Build final form from dynamic-preferences form_class = preference_form_builder(form_class, instance=instance, section=section) + # Get layout + form_class.layout = PreferenceLayout(form_class, section=section) + if request.method == "POST": form = form_class(request.POST, request.FILES or None) if form.is_valid(): @@ -702,7 +723,63 @@ def delete_group_type(request: HttpRequest, id_: int) -> HttpResponse: return redirect("group_types") -class DashboardWidgetListView(SingleTableView, PermissionRequiredMixin): +class DataCheckView(PermissionRequiredMixin, ListView): + permission_required = "core.view_datacheckresults" + model = DataCheckResult + template_name = "core/data_check/list.html" + context_object_name = "results" + + def get_queryset(self) -> QuerySet: + return DataCheckResult.objects.filter(solved=False).order_by("check") + + def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: + context = super().get_context_data(**kwargs) + context["registered_checks"] = DataCheckRegistry.data_checks + return context + + +class RunDataChecks(PermissionRequiredMixin, View): + permission_required = "core.run_data_checks" + + def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: + if not check_data()[1]: + messages.success( + request, + _( + "The data check has been started. Please note that it may take " + "a while before you are able to fetch the data on this page." + ), + ) + else: + messages.success(request, _("The data check has finished.")) + return redirect("check_data") + + +class SolveDataCheckView(PermissionRequiredMixin, RevisionMixin, DetailView): + queryset = DataCheckResult.objects.all() + permission_required = "core.solve_data_problem" + + def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: + solve_option = self.kwargs["solve_option"] + result = self.get_object() + if solve_option in result.related_check.solve_options: + solve_option_obj = result.related_check.solve_options[solve_option] + + msg = _( + f"The solve option '{solve_option_obj.verbose_name}' " + f"has been executed on the object '{result.related_object}' " + f"(type: {result.related_object._meta.verbose_name})." + ) + + result.solve(solve_option) + + messages.success(request, msg) + return redirect("check_data") + else: + return HttpResponseNotFound() + + +class DashboardWidgetListView(PermissionRequiredMixin, SingleTableView): """Table of all dashboard widgets.""" model = DashboardWidget @@ -720,7 +797,7 @@ class DashboardWidgetListView(SingleTableView, PermissionRequiredMixin): @method_decorator(never_cache, name="dispatch") -class DashboardWidgetEditView(AdvancedEditView, PermissionRequiredMixin): +class DashboardWidgetEditView(PermissionRequiredMixin, AdvancedEditView): """Edit view for dashboard widgets.""" def get_form_class(self) -> Type[BaseModelForm]: @@ -735,7 +812,7 @@ class DashboardWidgetEditView(AdvancedEditView, PermissionRequiredMixin): @method_decorator(never_cache, name="dispatch") -class DashboardWidgetCreateView(AdvancedCreateView, PermissionRequiredMixin): +class DashboardWidgetCreateView(PermissionRequiredMixin, AdvancedCreateView): """Create view for dashboard widgets.""" def get_model(self, request, *args, **kwargs): @@ -777,11 +854,23 @@ class DashboardWidgetDeleteView(PermissionRequiredMixin, AdvancedDeleteView): class EditDashboardView(View): """View for editing dashboard widget order.""" - def get_context_data(self, request): + def get_context_data(self, request, **kwargs): context = {} + self.default_dashboard = kwargs.get("default", False) + + if self.default_dashboard and not request.user.has_perm("core.edit_default_dashboard"): + raise PermissionDenied() + + context["default_dashboard"] = self.default_dashboard - widgets = request.user.person.dashboard_widgets - not_used_widgets = DashboardWidget.objects.exclude(pk__in=[w.pk for w in widgets]) + widgets = ( + request.user.person.dashboard_widgets + if not self.default_dashboard + else DashboardWidgetOrder.default_dashboard_widgets + ) + not_used_widgets = DashboardWidget.objects.exclude(pk__in=[w.pk for w in widgets]).filter( + active=True + ) context["widgets"] = widgets context["not_used_widgets"] = not_used_widgets @@ -800,8 +889,8 @@ class EditDashboardView(View): return context - def post(self, request): - context = self.get_context_data(request) + def post(self, request, **kwargs): + context = self.get_context_data(request, **kwargs) if context["formset"].is_valid(): added_objects = [] @@ -811,22 +900,26 @@ class EditDashboardView(View): obj, created = DashboardWidgetOrder.objects.update_or_create( widget=form.cleaned_data["pk"], - person=request.user.person, + person=request.user.person if not self.default_dashboard else None, + default=self.default_dashboard, defaults={"order": form.cleaned_data["order"]}, ) added_objects.append(obj.pk) - DashboardWidgetOrder.objects.filter(person=request.user.person).exclude( - pk__in=added_objects - ).delete() + DashboardWidgetOrder.objects.filter( + person=request.user.person if not self.default_dashboard else None, + default=self.default_dashboard, + ).exclude(pk__in=added_objects).delete() - messages.success( - request, _("Your dashboard configuration has been saved successfully.") - ) - return redirect("index") + if not self.default_dashboard: + msg = _("Your dashboard configuration has been saved successfully.") + else: + msg = _("The configuration of the default dashboard has been saved successfully.") + messages.success(request, msg) + return redirect("index" if not self.default_dashboard else "dashboard_widgets") - def get(self, request): - context = self.get_context_data(request) + def get(self, request, **kwargs): + context = self.get_context_data(request, **kwargs) return render(request, "core/edit_dashboard.html", context=context) diff --git a/poetry.lock b/poetry.lock index be0e61cd822675c59e9c9fc318fd2f104c7f900a..050abe9881f5ea2dd89b14b83932f3954791bf80 100644 --- a/poetry.lock +++ b/poetry.lock @@ -46,7 +46,7 @@ reference = "gitlab" [[package]] name = "amqp" -version = "5.0.2" +version = "5.0.3" description = "Low-level AMQP client for Python (fork of amqplib)." category = "main" optional = true @@ -122,6 +122,21 @@ PyYAML = ">=5.3.1" six = ">=1.10.0" stevedore = ">=1.20.0" +[[package]] +name = "beautifulsoup4" +version = "4.9.3" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""} + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "billiard" version = "3.6.3.0" @@ -152,7 +167,7 @@ d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] name = "bleach" -version = "3.2.1" +version = "3.2.2" description = "An easy safelist-based HTML-sanitizing tool." category = "main" optional = false @@ -171,6 +186,17 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "bs4" +version = "0.0.1" +description = "Dummy package for Beautiful Soup" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +beautifulsoup4 = "*" + [[package]] name = "calendarweek" version = "0.4.7" @@ -354,7 +380,7 @@ six = "*" [[package]] name = "coverage" -version = "5.3" +version = "5.3.1" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -373,7 +399,7 @@ python-versions = "*" [[package]] name = "django" -version = "3.1.4" +version = "3.1.5" description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." category = "main" optional = false @@ -469,7 +495,7 @@ dev = ["flake8", "tox", "twine", "therapist", "black"] [[package]] name = "django-celery-beat" -version = "2.1.0" +version = "2.2.0" description = "Database-backed Periodic Tasks." category = "main" optional = true @@ -477,8 +503,8 @@ python-versions = "*" [package.dependencies] celery = ">=4.4,<6.0" -Django = ">=2.2" -django-timezone-field = ">=4.0,<5.0" +Django = ">=2.2,<4.0" +django-timezone-field = ">=4.1.0,<5.0" python-crontab = ">=2.3.4" [[package]] @@ -496,7 +522,7 @@ django-appconf = "*" [[package]] name = "django-celery-results" -version = "2.0.0" +version = "2.0.1" description = "Celery result backends for Django." category = "main" optional = true @@ -635,7 +661,7 @@ Django = ">=2.2" [[package]] name = "django-health-check" -version = "3.16.1" +version = "3.16.2" description = "Run checks on services like databases, queue servers, celery processes, etc." category = "main" optional = false @@ -646,7 +672,7 @@ django = ">=1.11" [[package]] name = "django-impersonate" -version = "1.7" +version = "1.7.2" description = "Django app to allow superusers to impersonate other users." category = "main" optional = false @@ -681,7 +707,7 @@ Django = ">=1.5" [[package]] name = "django-jsonstore" -version = "0.4.1" +version = "0.5.0" description = "Expose JSONField data as a virtual django model fields." category = "main" optional = false @@ -701,7 +727,7 @@ python-versions = "*" [[package]] name = "django-material" -version = "1.7.3" +version = "1.7.4" description = "Material design for django forms and admin" category = "main" optional = false @@ -711,8 +737,8 @@ python-versions = "*" six = "*" [[package]] -name = "django-menu-generator" -version = "1.1.0" +name = "django-menu-generator-ng" +version = "1.2.0" description = "A straightforward menu generator for Django" category = "main" optional = false @@ -852,7 +878,7 @@ management-command = ["django-compressor (>=2.4)"] [[package]] name = "django-select2" -version = "7.5.0" +version = "7.6.1" description = "Select2 option fields for Django" category = "main" optional = false @@ -888,7 +914,7 @@ typing-extensions = "*" [[package]] name = "django-tables2" -version = "2.3.3" +version = "2.3.4" description = "Table/data-grid framework for Django" category = "main" optional = false @@ -1020,7 +1046,7 @@ yaml = ["ruamel.yaml"] [[package]] name = "faker" -version = "5.0.2" +version = "5.6.5" description = "Faker is a Python package that generates fake data for you." category = "main" optional = false @@ -1180,7 +1206,7 @@ smmap = ">=3.0.1,<4" [[package]] name = "gitpython" -version = "3.1.11" +version = "3.1.12" description = "Python Git Library" category = "dev" optional = false @@ -1215,7 +1241,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "3.3.0" +version = "3.4.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -1226,8 +1252,8 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -1239,7 +1265,7 @@ python-versions = "*" [[package]] name = "isort" -version = "5.6.4" +version = "5.7.0" description = "A Python utility / library to sort Python imports." category = "dev" optional = false @@ -1402,7 +1428,7 @@ scramp = "1.2.0" [[package]] name = "phonenumbers" -version = "8.12.15" +version = "8.12.16" description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." category = "main" optional = false @@ -1410,7 +1436,7 @@ python-versions = "*" [[package]] name = "pillow" -version = "8.0.1" +version = "8.1.0" description = "Python Imaging Library (Fork)" category = "main" optional = false @@ -1443,7 +1469,7 @@ twisted = ["twisted"] [[package]] name = "prompt-toolkit" -version = "3.0.8" +version = "3.0.11" description = "Library for building powerful interactive command lines in Python" category = "main" optional = true @@ -1535,7 +1561,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.7.3" +version = "2.7.4" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -1586,14 +1612,14 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm [[package]] name = "pytest-cov" -version = "2.10.1" +version = "2.11.1" description = "Pytest plugin for measuring coverage." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -coverage = ">=4.4" +coverage = ">=5.2.1" pytest = ">=4.6" [package.extras] @@ -1690,7 +1716,7 @@ six = ">=1.4.0" [[package]] name = "pytz" -version = "2020.4" +version = "2020.5" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -1698,11 +1724,11 @@ python-versions = "*" [[package]] name = "pyyaml" -version = "5.3.1" +version = "5.4.1" description = "YAML parser and emitter for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "qrcode" @@ -1803,7 +1829,7 @@ python-versions = "*" [[package]] name = "safety" -version = "1.9.0" +version = "1.10.3" description = "Checks installed dependencies for known vulnerabilities." category = "dev" optional = false @@ -1858,6 +1884,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "soupsieve" +version = "2.1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "spdx-license-list" version = "0.5.2" @@ -1868,7 +1902,7 @@ python-versions = "*" [[package]] name = "sphinx" -version = "3.3.1" +version = "3.4.3" description = "Python documentation generator" category = "dev" optional = false @@ -1894,8 +1928,8 @@ sphinxcontrib-serializinghtml = "*" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "flake8-import-order", "mypy (>=0.790)", "docutils-stubs"] -test = ["pytest", "pytest-cov", "html5lib", "typed-ast", "cython"] +lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.790)", "docutils-stubs"] +test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] [[package]] name = "sphinx-autodoc-typehints" @@ -2021,7 +2055,7 @@ python-versions = "*" [[package]] name = "testfixtures" -version = "6.17.0" +version = "6.17.1" description = "A collection of helpers and mock objects for unit tests and doc tests." category = "dev" optional = false @@ -2076,32 +2110,33 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tqdm" -version = "4.54.1" +version = "4.56.0" description = "Fast, Extensible Progress Meter" category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" [package.extras] -dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown", "wheel"] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +telegram = ["requests"] [[package]] name = "twilio" -version = "6.50.1" +version = "6.51.0" description = "Twilio API client and TwiML generator" category = "main" optional = false python-versions = "*" [package.dependencies] -PyJWT = ">=1.4.2" +PyJWT = "1.7.1" pytz = "*" requests = {version = ">=2.0.0", markers = "python_version >= \"3.0\""} six = "*" [[package]] name = "typed-ast" -version = "1.4.1" +version = "1.4.2" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false @@ -2182,7 +2217,7 @@ ldap = ["django-auth-ldap"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "65a4b7b891965a330e7fec9ed64213579550aac1ac8295aa8c3022a4978d488c" +content-hash = "b8b44ac64723c99f10cbeaa085976eb30a503e29379801c21495d362933e4aa0" [metadata.files] alabaster = [ @@ -2193,8 +2228,8 @@ aleksis-builddeps = [ {file = "AlekSIS-Builddeps-1.tar.gz", hash = "sha256:97a19597f422593cbdc438aabf17f95748126c8951df6ac7db7991fc99c108c4"}, ] amqp = [ - {file = "amqp-5.0.2-py3-none-any.whl", hash = "sha256:5b9062d5c0812335c75434bf17ce33d7a20ecfedaa0733faec7379868eb4068a"}, - {file = "amqp-5.0.2.tar.gz", hash = "sha256:fcd5b3baeeb7fc19b3486ff6d10543099d40ae1f5c9196eae695d1cde1b2f784"}, + {file = "amqp-5.0.3-py3-none-any.whl", hash = "sha256:2c58528a05dcbf2ae080f3141b6a5bf467949fad9234edd8b9085b8db2e325fe"}, + {file = "amqp-5.0.3.tar.gz", hash = "sha256:1733ebf713050504fd9d2ebc661f1fc95b3588f99ee87d2e39c84c27bfd815dc"}, ] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, @@ -2220,6 +2255,11 @@ bandit = [ {file = "bandit-1.7.0-py3-none-any.whl", hash = "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07"}, {file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"}, ] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, + {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, + {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, +] billiard = [ {file = "billiard-3.6.3.0-py3-none-any.whl", hash = "sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede"}, {file = "billiard-3.6.3.0.tar.gz", hash = "sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a"}, @@ -2229,13 +2269,16 @@ black = [ {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, ] bleach = [ - {file = "bleach-3.2.1-py2.py3-none-any.whl", hash = "sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"}, - {file = "bleach-3.2.1.tar.gz", hash = "sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080"}, + {file = "bleach-3.2.2-py2.py3-none-any.whl", hash = "sha256:a690ccc41a10d806a7c0a9130767750925e4863e332f7e4ea93da1bc12a24300"}, + {file = "bleach-3.2.2.tar.gz", hash = "sha256:ce6270dd0ae56cd810495b8d994551ae16b41f2b4043cf50064f298985afdb3c"}, ] "boolean.py" = [ {file = "boolean.py-3.8-py2.py3-none-any.whl", hash = "sha256:d75da0fd0354425fa64f6bbc6cec6ae1485d0eec3447b73187ff8cbf9b572e26"}, {file = "boolean.py-3.8.tar.gz", hash = "sha256:cc24e20f985d60cd4a3a5a1c0956dd12611159d32a75081dabd0c9ab981acaa4"}, ] +bs4 = [ + {file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"}, +] calendarweek = [ {file = "calendarweek-0.4.7-py3-none-any.whl", hash = "sha256:ee65caea113503dcdb33d96bca9f79f88b3ab4f66279d4cb568d89f1f662608a"}, {file = "calendarweek-0.4.7.tar.gz", hash = "sha256:7655d6a4c3b4f6a4e01aa7d23b49cd121db0399050e9c08cd8d1210155be25dd"}, @@ -2277,6 +2320,7 @@ click-repl = [ ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] colour = [ {file = "colour-0.1.5-py2.py3-none-any.whl", hash = "sha256:33f6db9d564fadc16e59921a56999b79571160ce09916303d35346dddc17978c"}, @@ -2286,48 +2330,63 @@ configobj = [ {file = "configobj-5.0.6.tar.gz", hash = "sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902"}, ] coverage = [ - {file = "coverage-5.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270"}, - {file = "coverage-5.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4"}, - {file = "coverage-5.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9"}, - {file = "coverage-5.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729"}, - {file = "coverage-5.3-cp27-cp27m-win32.whl", hash = "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d"}, - {file = "coverage-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418"}, - {file = "coverage-5.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9"}, - {file = "coverage-5.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5"}, - {file = "coverage-5.3-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822"}, - {file = "coverage-5.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097"}, - {file = "coverage-5.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9"}, - {file = "coverage-5.3-cp35-cp35m-win32.whl", hash = "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636"}, - {file = "coverage-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f"}, - {file = "coverage-5.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237"}, - {file = "coverage-5.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54"}, - {file = "coverage-5.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7"}, - {file = "coverage-5.3-cp36-cp36m-win32.whl", hash = "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a"}, - {file = "coverage-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d"}, - {file = "coverage-5.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"}, - {file = "coverage-5.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f"}, - {file = "coverage-5.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c"}, - {file = "coverage-5.3-cp37-cp37m-win32.whl", hash = "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751"}, - {file = "coverage-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709"}, - {file = "coverage-5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516"}, - {file = "coverage-5.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f"}, - {file = "coverage-5.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259"}, - {file = "coverage-5.3-cp38-cp38-win32.whl", hash = "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82"}, - {file = "coverage-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221"}, - {file = "coverage-5.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978"}, - {file = "coverage-5.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21"}, - {file = "coverage-5.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24"}, - {file = "coverage-5.3-cp39-cp39-win32.whl", hash = "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7"}, - {file = "coverage-5.3-cp39-cp39-win_amd64.whl", hash = "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7"}, - {file = "coverage-5.3.tar.gz", hash = "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0"}, + {file = "coverage-5.3.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b"}, + {file = "coverage-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297"}, + {file = "coverage-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7"}, + {file = "coverage-5.3.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b"}, + {file = "coverage-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7"}, + {file = "coverage-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72"}, + {file = "coverage-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277"}, + {file = "coverage-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f"}, + {file = "coverage-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c"}, + {file = "coverage-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e"}, + {file = "coverage-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2"}, + {file = "coverage-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879"}, + {file = "coverage-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830"}, + {file = "coverage-5.3.1-cp38-cp38-win32.whl", hash = "sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"}, + {file = "coverage-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606"}, + {file = "coverage-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d"}, + {file = "coverage-5.3.1-cp39-cp39-win32.whl", hash = "sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98"}, + {file = "coverage-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1"}, + {file = "coverage-5.3.1-pp36-none-any.whl", hash = "sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3"}, + {file = "coverage-5.3.1-pp37-none-any.whl", hash = "sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c"}, + {file = "coverage-5.3.1.tar.gz", hash = "sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b"}, ] dj-database-url = [ {file = "dj-database-url-0.5.0.tar.gz", hash = "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163"}, {file = "dj_database_url-0.5.0-py2.py3-none-any.whl", hash = "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9"}, ] django = [ - {file = "Django-3.1.4-py3-none-any.whl", hash = "sha256:5c866205f15e7a7123f1eec6ab939d22d5bde1416635cab259684af66d8e48a2"}, - {file = "Django-3.1.4.tar.gz", hash = "sha256:edb10b5c45e7e9c0fb1dc00b76ec7449aca258a39ffd613dbd078c51d19c9f03"}, + {file = "Django-3.1.5-py3-none-any.whl", hash = "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9"}, + {file = "Django-3.1.5.tar.gz", hash = "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7"}, ] django-any-js = [ {file = "django-any-js-1.0.3.post0.tar.gz", hash = "sha256:1da88b44b861b0f54f6b8ea0eb4c7c4fa1a5772e9a4320532cd4e0871a4e23f7"}, @@ -2357,16 +2416,16 @@ django-cache-memoize = [ {file = "django_cache_memoize-0.1.7-py2.py3-none-any.whl", hash = "sha256:bc7f53725558244af62197d0125732d7ec88ecc1281a3a2f37d77ae1a8c269d3"}, ] django-celery-beat = [ - {file = "django-celery-beat-2.1.0.tar.gz", hash = "sha256:4eb0e8412e2e05ba0029912a6f80d1054731001eecbcb4d59688c4e07cf4d9d3"}, - {file = "django_celery_beat-2.1.0-py2.py3-none-any.whl", hash = "sha256:8a169e11d96faed8b72d505ddbc70e7fe0b16cdc854df43cb209c153ed08d651"}, + {file = "django-celery-beat-2.2.0.tar.gz", hash = "sha256:b8a13afb15e7c53fc04f4f847ac71a6d32088959aba701eb7c4a59f0c28ba543"}, + {file = "django_celery_beat-2.2.0-py2.py3-none-any.whl", hash = "sha256:c4c72a9579f20eff4c4ccf1b58ebdca5ef940f4210065057db1754ea5f8dffdc"}, ] django-celery-email = [ {file = "django-celery-email-3.0.0.tar.gz", hash = "sha256:5546cbba80952cc3b8a0ffa4206ce90a4a996a7ffd1c385a2bdb65903ca18ece"}, {file = "django_celery_email-3.0.0-py2.py3-none-any.whl", hash = "sha256:0f72da39cb2ea83c69440566e87f27cd72f68f247f98ce99fb29889fcf329406"}, ] django-celery-results = [ - {file = "django_celery_results-2.0.0-py2.py3-none-any.whl", hash = "sha256:f82280a9a25c44048b9e64ae4d47ade7d522c8221304b0e25388080021b95468"}, - {file = "django_celery_results-2.0.0.tar.gz", hash = "sha256:754e01f22f70fddee5f2ca95c18f183fccee42ad98f9803577bffa717d45ac5d"}, + {file = "django_celery_results-2.0.1-py2.py3-none-any.whl", hash = "sha256:a2f7d172f7f57dd972538acc6e80a5bf50c673fb4d82fe027189c8659c60dfce"}, + {file = "django_celery_results-2.0.1.tar.gz", hash = "sha256:d625e324138e5b2ef46ffa9e89fa353c16d619420066ac8b240ef9247b293a84"}, ] django-ckeditor = [ {file = "django-ckeditor-6.0.0.tar.gz", hash = "sha256:29fd1a333cb9741ac2c3fd4e427a5c00115ed33a2389716a09af7656022dcdde"}, @@ -2412,11 +2471,11 @@ django-haystack = [ {file = "django_haystack-3.0b1-py3-none-any.whl", hash = "sha256:b83705e1cf8141cd1755fc6683ac65fea4e1281f4b4306bc9224af96495b0df3"}, ] django-health-check = [ - {file = "django-health-check-3.16.1.tar.gz", hash = "sha256:2cb3944e313e435bdf299288e109f398b6c08b610e09cc90d7f5f6a2bcf469fc"}, - {file = "django_health_check-3.16.1-py2.py3-none-any.whl", hash = "sha256:8b0835f04ebaeb0d12498a5ef47dd22196237c3987ff28bcce9ed28b5a169d5e"}, + {file = "django-health-check-3.16.2.tar.gz", hash = "sha256:d5edf773b59e4091ac6bd65280091dbb13d92e70eba5cf8573faa738a9cb481c"}, + {file = "django_health_check-3.16.2-py2.py3-none-any.whl", hash = "sha256:aec4dd5cfb8333071f535c3611ef805ec2012af9568cf984be351ff1bd4020be"}, ] django-impersonate = [ - {file = "django-impersonate-1.7.tar.gz", hash = "sha256:1dadb5239a5cb79d4327ef3000cd989f9d4e76428a5e2a080496a53b41fa785f"}, + {file = "django-impersonate-1.7.2.tar.gz", hash = "sha256:ef1f9fa3180f4d95db0abbca3403f389e901e0beb781afd0db0edface72d148d"}, ] django-ipware = [ {file = "django-ipware-3.0.2.tar.gz", hash = "sha256:c7df8e1410a8e5d6b1fbae58728402ea59950f043c3582e033e866f0f0cf5e94"}, @@ -2430,18 +2489,20 @@ django-js-reverse = [ {file = "django_js_reverse-0.9.1-py2.py3-none-any.whl", hash = "sha256:8134c2ab6307c945edfa90671ca65e85d6c1754d48566bdd6464be259cc80c30"}, ] django-jsonstore = [ - {file = "django-jsonstore-0.4.1.tar.gz", hash = "sha256:d6e42152af3f924e4657c99e80144ba9a6410799256f6134b5a4e9fa4282ec10"}, + {file = "django-jsonstore-0.5.0.tar.gz", hash = "sha256:896dc10b08f59807eda1c6cebf43cd26e50d0db29d13495c027dc31e464be3c3"}, + {file = "django_jsonstore-0.5.0-py2-none-any.whl", hash = "sha256:9630c1fb43ae9f8e32733c5cf7d4c3775ba6f08532f517c64025053352d72844"}, ] django-maintenance-mode = [ {file = "django-maintenance-mode-0.15.1.tar.gz", hash = "sha256:d07102cab88dd707a82232f0c552c287e62aa53af582a0ca4f2aa31f14f5ed27"}, {file = "django_maintenance_mode-0.15.1-py3-none-any.whl", hash = "sha256:8c45b400253076655562c99a2ffb88f8353fc1c84496c1b9de812cc8132aea6f"}, ] django-material = [ - {file = "django-material-1.7.3.tar.gz", hash = "sha256:00599cd87f19f3a66be065865b223801afa9da680744a5eac10c489a10d98eba"}, - {file = "django_material-1.7.3-py2.py3-none-any.whl", hash = "sha256:6c010aa47618ceae2617f8a476b55aaed0884b1a4a6f5bdf969448a1ba72e352"}, + {file = "django-material-1.7.4.tar.gz", hash = "sha256:93af86e740b6db15a3b9df913c343217b198d7342a083db694acb319b49cb2dd"}, + {file = "django_material-1.7.4-py2.py3-none-any.whl", hash = "sha256:70dcaa34b35dbc31fbdb7454c7a376358586d0f166abe15870e07e468d729425"}, ] -django-menu-generator = [ - {file = "django-menu-generator-1.1.0.tar.gz", hash = "sha256:e8f9b808080c4b281f9c5962f39078c76c2007a5ef8ab1f7a81c81dbbe6a9848"}, +django-menu-generator-ng = [ + {file = "django-menu-generator-ng-1.2.0.tar.gz", hash = "sha256:ed3666ffe7d669045958ff45254a8d5afcedfc1824ed27df5c63823f624bf521"}, + {file = "django_menu_generator_ng-1.2.0-py3-none-any.whl", hash = "sha256:aea14db10b81899c022074dfb0dce058effef1c899b0bd1d2b3491bea1e86ee2"}, ] django-middleware-global-request = [ {file = "django-middleware-global-request-0.1.2.tar.gz", hash = "sha256:f6490759bc9f7dbde4001709554e29ca715daf847f2222914b4e47117dca9313"}, @@ -2486,8 +2547,8 @@ django-sass-processor = [ {file = "django-sass-processor-0.8.2.tar.gz", hash = "sha256:9b46a12ca8bdcb397d46fbcc49e6a926ff9f76a93c5efeb23b495419fd01fc7a"}, ] django-select2 = [ - {file = "django-select2-7.5.0.tar.gz", hash = "sha256:df71dedba9a362041b65e3cd692cb8b4f9e1e17a19681c7b4e61f331868bae0c"}, - {file = "django_select2-7.5.0-py2.py3-none-any.whl", hash = "sha256:6662aa1c21d4839b8fff38e4c9d402ed3da81f7c5ef7f7e703c862255ba3b9ed"}, + {file = "django-select2-7.6.1.tar.gz", hash = "sha256:25362c5bafe082a19add598fb0a69e3239b94759691a0ac8e01ab7fba8e650ad"}, + {file = "django_select2-7.6.1-py2.py3-none-any.whl", hash = "sha256:dc6b6fa737b6ea0b673e27c218955dd51a3fb81b2b28af93ce87703b24f4faf8"}, ] django-settings-context-processor = [ {file = "django-settings-context-processor-0.2.tar.gz", hash = "sha256:d37c853d69a3069f5abbf94c7f4f6fc0fac38bbd0524190cd5a250ba800e496a"}, @@ -2497,8 +2558,8 @@ django-stubs = [ {file = "django_stubs-1.7.0-py3-none-any.whl", hash = "sha256:30a7d99c694acf79c5d93d69a5a8e4b54d2a8c11dd672aa869006789e2189fa6"}, ] django-tables2 = [ - {file = "django-tables2-2.3.3.tar.gz", hash = "sha256:ad38ece83157b8b9c1fb72b0316fcffc4b32d7fec53eec3f2847b83c4c0a2cb2"}, - {file = "django_tables2-2.3.3-py2.py3-none-any.whl", hash = "sha256:9c175834130ebb2b3a5644431391e09128405e4538637804d5e74a8b96fc9f68"}, + {file = "django-tables2-2.3.4.tar.gz", hash = "sha256:50ccadbd13740a996d8a4d4f144ef80134745cd0b5ec278061537e341f5ef7a2"}, + {file = "django_tables2-2.3.4-py2.py3-none-any.whl", hash = "sha256:af5f70a9845fd8690c6b44120b065e55b9771cae99c1dcd0eb4f1cfa3f0a71e4"}, ] django-templated-email = [ {file = "django-templated-email-2.3.0.tar.gz", hash = "sha256:536c4e5ae099eabfb9aab36087d4d7799948c654e73da55a744213d086d5bb33"}, @@ -2531,8 +2592,8 @@ dynaconf = [ {file = "dynaconf-3.1.2.tar.gz", hash = "sha256:9b34ab2f811a81755f5eb4beac77a69e1e0887528c7e37fc4bc83fed52dcf502"}, ] faker = [ - {file = "Faker-5.0.2-py3-none-any.whl", hash = "sha256:5b17c95cfb013a22b062b8df18286f08ce4ea880f9948ec74295e5a42dbb2e44"}, - {file = "Faker-5.0.2.tar.gz", hash = "sha256:00ce4342c221b1931b2f35d46f5027d35bc62a4ca3a34628b2c5b514b4ca958a"}, + {file = "Faker-5.6.5-py3-none-any.whl", hash = "sha256:8fe22be90c0db35d830464918f84beb50f970f4caacae4d88e92692857b78aa7"}, + {file = "Faker-5.6.5.tar.gz", hash = "sha256:0f8198d876bc65edd3699e2542c6ae13cc93fbc4a478c17db190d5d9b6ce790e"}, ] flake8 = [ {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, @@ -2580,8 +2641,8 @@ gitdb = [ {file = "gitdb-4.0.5.tar.gz", hash = "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9"}, ] gitpython = [ - {file = "GitPython-3.1.11-py3-none-any.whl", hash = "sha256:6eea89b655917b500437e9668e4a12eabdcf00229a0df1762aabd692ef9b746b"}, - {file = "GitPython-3.1.11.tar.gz", hash = "sha256:befa4d101f91bad1b632df4308ec64555db684c360bd7d2130b4807d49ce86b8"}, + {file = "GitPython-3.1.12-py3-none-any.whl", hash = "sha256:867ec3dfb126aac0f8296b19fb63b8c4a399f32b4b6fafe84c4b10af5fa9f7b5"}, + {file = "GitPython-3.1.12.tar.gz", hash = "sha256:42dbefd8d9e2576c496ed0059f3103dcef7125b9ce16f9d5f9c834aed44a1dac"}, ] html2text = [ {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"}, @@ -2596,15 +2657,16 @@ imagesize = [ {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"}, - {file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"}, + {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"}, + {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"}, ] iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] isort = [ - {file = "isort-5.6.4-py3-none-any.whl", hash = "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7"}, - {file = "isort-5.6.4.tar.gz", hash = "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"}, + {file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"}, + {file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"}, ] jinja2 = [ {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, @@ -2712,38 +2774,42 @@ pg8000 = [ {file = "pg8000-1.16.6.tar.gz", hash = "sha256:8fc1e6a62ccb7c9830f1e7e9288e2d20eaf373cc8875b5c55b7d5d9b7717be91"}, ] phonenumbers = [ - {file = "phonenumbers-8.12.15-py2.py3-none-any.whl", hash = "sha256:13d499f7114c4b37c54ee844b188d5e7441951a7da41de5fc1a25ff8fdceef80"}, - {file = "phonenumbers-8.12.15.tar.gz", hash = "sha256:b734bfcf33e87ddae72196a40b3d1af35abd0beb263816ae18e1bff612926406"}, + {file = "phonenumbers-8.12.16-py2.py3-none-any.whl", hash = "sha256:56ad29025b8f885945506350b06d77afbc506c5463141d77a5df767280a7ee0b"}, + {file = "phonenumbers-8.12.16.tar.gz", hash = "sha256:a820ab08c980ef24a2d2a1ead4f8d7016fdf008e484d1aecf7ff0b32cc475e16"}, ] pillow = [ - {file = "Pillow-8.0.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3"}, - {file = "Pillow-8.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302"}, - {file = "Pillow-8.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c"}, - {file = "Pillow-8.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11"}, - {file = "Pillow-8.0.1-cp36-cp36m-win32.whl", hash = "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e"}, - {file = "Pillow-8.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3"}, - {file = "Pillow-8.0.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09"}, - {file = "Pillow-8.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae"}, - {file = "Pillow-8.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a"}, - {file = "Pillow-8.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8"}, - {file = "Pillow-8.0.1-cp37-cp37m-win32.whl", hash = "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0"}, - {file = "Pillow-8.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039"}, - {file = "Pillow-8.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11"}, - {file = "Pillow-8.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72"}, - {file = "Pillow-8.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792"}, - {file = "Pillow-8.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015"}, - {file = "Pillow-8.0.1-cp38-cp38-win32.whl", hash = "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271"}, - {file = "Pillow-8.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7"}, - {file = "Pillow-8.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5"}, - {file = "Pillow-8.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce"}, - {file = "Pillow-8.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3"}, - {file = "Pillow-8.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544"}, - {file = "Pillow-8.0.1-cp39-cp39-win32.whl", hash = "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140"}, - {file = "Pillow-8.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021"}, - {file = "Pillow-8.0.1-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6"}, - {file = "Pillow-8.0.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb"}, - {file = "Pillow-8.0.1-pp37-pypy37_pp73-win32.whl", hash = "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8"}, - {file = "Pillow-8.0.1.tar.gz", hash = "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e"}, + {file = "Pillow-8.1.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a"}, + {file = "Pillow-8.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2"}, + {file = "Pillow-8.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174"}, + {file = "Pillow-8.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded"}, + {file = "Pillow-8.1.0-cp36-cp36m-win32.whl", hash = "sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d"}, + {file = "Pillow-8.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d"}, + {file = "Pillow-8.1.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234"}, + {file = "Pillow-8.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8"}, + {file = "Pillow-8.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17"}, + {file = "Pillow-8.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7"}, + {file = "Pillow-8.1.0-cp37-cp37m-win32.whl", hash = "sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e"}, + {file = "Pillow-8.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b"}, + {file = "Pillow-8.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0"}, + {file = "Pillow-8.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a"}, + {file = "Pillow-8.1.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d"}, + {file = "Pillow-8.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae"}, + {file = "Pillow-8.1.0-cp38-cp38-win32.whl", hash = "sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59"}, + {file = "Pillow-8.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c"}, + {file = "Pillow-8.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6"}, + {file = "Pillow-8.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378"}, + {file = "Pillow-8.1.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7"}, + {file = "Pillow-8.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0"}, + {file = "Pillow-8.1.0-cp39-cp39-win32.whl", hash = "sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b"}, + {file = "Pillow-8.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865"}, + {file = "Pillow-8.1.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9"}, + {file = "Pillow-8.1.0-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913"}, + {file = "Pillow-8.1.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206"}, + {file = "Pillow-8.1.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9"}, + {file = "Pillow-8.1.0-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032"}, + {file = "Pillow-8.1.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820"}, + {file = "Pillow-8.1.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5"}, + {file = "Pillow-8.1.0.tar.gz", hash = "sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba"}, ] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, @@ -2754,8 +2820,8 @@ prometheus-client = [ {file = "prometheus_client-0.9.0.tar.gz", hash = "sha256:9da7b32f02439d8c04f7777021c304ed51d9ec180604700c1ba72a4d44dceb03"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.8-py3-none-any.whl", hash = "sha256:7debb9a521e0b1ee7d2fe96ee4bd60ef03c6492784de0547337ca4433e46aa63"}, - {file = "prompt_toolkit-3.0.8.tar.gz", hash = "sha256:25c95d2ac813909f813c93fde734b6e44406d1477a9faef7c915ff37d39c0a8c"}, + {file = "prompt_toolkit-3.0.11-py3-none-any.whl", hash = "sha256:0bdd2585e5afd00c5f91dd28eb2090ea67e94c385878921939bb4ccfa3904723"}, + {file = "prompt_toolkit-3.0.11.tar.gz", hash = "sha256:dc83e6368b0edd9ceabe17a055f2e22f6ed95b9aa39dbd59d0b4f3585bdfe9ed"}, ] psutil = [ {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"}, @@ -2800,6 +2866,8 @@ psycopg2 = [ {file = "psycopg2-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:56fee7f818d032f802b8eed81ef0c1232b8b42390df189cab9cfa87573fe52c5"}, {file = "psycopg2-2.8.6-cp38-cp38-win32.whl", hash = "sha256:ad2fe8a37be669082e61fb001c185ffb58867fdbb3e7a6b0b0d2ffe232353a3e"}, {file = "psycopg2-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:56007a226b8e95aa980ada7abdea6b40b75ce62a433bd27cec7a8178d57f4051"}, + {file = "psycopg2-2.8.6-cp39-cp39-win32.whl", hash = "sha256:2c93d4d16933fea5bbacbe1aaf8fa8c1348740b2e50b3735d1b0bf8154cbf0f3"}, + {file = "psycopg2-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:d5062ae50b222da28253059880a871dc87e099c25cb68acf613d9d227413d6f7"}, {file = "psycopg2-2.8.6.tar.gz", hash = "sha256:fb23f6c71107c37fd667cb4ea363ddeb936b348bbd6449278eb92c189699f543"}, ] py = [ @@ -2844,6 +2912,8 @@ pycryptodome = [ {file = "pycryptodome-3.9.9-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:5598dc6c9dbfe882904e54584322893eff185b98960bbe2cdaaa20e8a437b6e5"}, {file = "pycryptodome-3.9.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1cfdb92dca388e27e732caa72a1cc624520fe93752a665c3b6cd8f1a91b34916"}, {file = "pycryptodome-3.9.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5f19e6ef750f677d924d9c7141f54bade3cd56695bbfd8a9ef15d0378557dfe4"}, + {file = "pycryptodome-3.9.9-cp27-cp27m-win32.whl", hash = "sha256:a3d8a9efa213be8232c59cdc6b65600276508e375e0a119d710826248fd18d37"}, + {file = "pycryptodome-3.9.9-cp27-cp27m-win_amd64.whl", hash = "sha256:50826b49fbca348a61529693b0031cdb782c39060fb9dca5ac5dff858159dc5a"}, {file = "pycryptodome-3.9.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:19cb674df6c74a14b8b408aa30ba8a89bd1c01e23505100fb45f930fbf0ed0d9"}, {file = "pycryptodome-3.9.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:28f75e58d02019a7edc7d4135203d2501dfc47256d175c72c9798f9a129a49a7"}, {file = "pycryptodome-3.9.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:6d3baaf82681cfb1a842f1c8f77beac791ceedd99af911e4f5fabec32bae2259"}, @@ -2854,17 +2924,26 @@ pycryptodome = [ {file = "pycryptodome-3.9.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:7798e73225a699651888489fbb1dbc565e03a509942a8ce6194bbe6fb582a41f"}, {file = "pycryptodome-3.9.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:46e96aeb8a9ca8b1edf9b1fd0af4bf6afcf3f1ca7fa35529f5d60b98f3e4e959"}, {file = "pycryptodome-3.9.9-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:843e5f10ecdf9d307032b8b91afe9da1d6ed5bb89d0bbec5c8dcb4ba44008e11"}, + {file = "pycryptodome-3.9.9-cp36-cp36m-win32.whl", hash = "sha256:b68794fba45bdb367eeb71249c26d23e61167510a1d0c3d6cf0f2f14636e62ee"}, + {file = "pycryptodome-3.9.9-cp36-cp36m-win_amd64.whl", hash = "sha256:60febcf5baf70c566d9d9351c47fbd8321da9a4edf2eff45c4c31c86164ca794"}, {file = "pycryptodome-3.9.9-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:4ed27951b0a17afd287299e2206a339b5b6d12de9321e1a1575261ef9c4a851b"}, {file = "pycryptodome-3.9.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9000877383e2189dafd1b2fc68c6c726eca9a3cfb6d68148fbb72ccf651959b6"}, {file = "pycryptodome-3.9.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:faa682c404c218e8788c3126c9a4b8fbcc54dc245b5b6e8ea5b46f3b63bd0c84"}, {file = "pycryptodome-3.9.9-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:62c488a21c253dadc9f731a32f0ac61e4e436d81a1ea6f7d1d9146ed4d20d6bd"}, + {file = "pycryptodome-3.9.9-cp37-cp37m-win32.whl", hash = "sha256:834b790bbb6bd18956f625af4004d9c15eed12d5186d8e57851454ae76d52215"}, + {file = "pycryptodome-3.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:70d807d11d508433daf96244ec1c64e55039e8a35931fc5ea9eee94dbe3cb6b5"}, {file = "pycryptodome-3.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:27397aee992af69d07502126561d851ba3845aa808f0e55c71ad0efa264dd7d4"}, {file = "pycryptodome-3.9.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:d7ec2bd8f57c559dd24e71891c51c25266a8deb66fc5f02cc97c7fb593d1780a"}, {file = "pycryptodome-3.9.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:e15bde67ccb7d4417f627dd16ffe2f5a4c2941ce5278444e884cb26d73ecbc61"}, {file = "pycryptodome-3.9.9-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:5c3c4865730dfb0263f822b966d6d58429d8b1e560d1ddae37685fd9e7c63161"}, + {file = "pycryptodome-3.9.9-cp38-cp38-win32.whl", hash = "sha256:76b1a34d74bb2c91bce460cdc74d1347592045627a955e9a252554481c17c52f"}, + {file = "pycryptodome-3.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:6e4227849e4231a3f5b35ea5bdedf9a82b3883500e5624f00a19156e9a9ef861"}, {file = "pycryptodome-3.9.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2a68df525b387201a43b27b879ce8c08948a430e883a756d6c9e3acdaa7d7bd8"}, {file = "pycryptodome-3.9.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:a4599c0ca0fc027c780c1c45ed996d5bef03e571470b7b1c7171ec1e1a90914c"}, {file = "pycryptodome-3.9.9-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b4e6b269a8ddaede774e5c3adbef6bf452ee144e6db8a716d23694953348cd86"}, + {file = "pycryptodome-3.9.9-cp39-cp39-win32.whl", hash = "sha256:a199e9ca46fc6e999e5f47fce342af4b56c7de85fae893c69ab6aa17531fb1e1"}, + {file = "pycryptodome-3.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:6e89bb3826e6f84501e8e3b205c22595d0c5492c2f271cbb9ee1c48eb1866645"}, + {file = "pycryptodome-3.9.9.tar.gz", hash = "sha256:910e202a557e1131b1c1b3f17a63914d57aac55cf9fb9b51644962841c3995c4"}, ] pydocstyle = [ {file = "pydocstyle-5.1.1-py3-none-any.whl", hash = "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"}, @@ -2875,8 +2954,8 @@ pyflakes = [ {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, ] pygments = [ - {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"}, - {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"}, + {file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"}, + {file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"}, ] pyjwt = [ {file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"}, @@ -2891,8 +2970,8 @@ pytest = [ {file = "pytest-6.2.1.tar.gz", hash = "sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"}, ] pytest-cov = [ - {file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"}, - {file = "pytest_cov-2.10.1-py2.py3-none-any.whl", hash = "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191"}, + {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, + {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, ] pytest-django = [ {file = "pytest-django-3.10.0.tar.gz", hash = "sha256:4de6dbd077ed8606616958f77655fed0d5e3ee45159475671c7fa67596c6dba6"}, @@ -2920,21 +2999,31 @@ python-memcached = [ {file = "python_memcached-1.59-py2.py3-none-any.whl", hash = "sha256:4dac64916871bd3550263323fc2ce18e1e439080a2d5670c594cf3118d99b594"}, ] pytz = [ - {file = "pytz-2020.4-py2.py3-none-any.whl", hash = "sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd"}, - {file = "pytz-2020.4.tar.gz", hash = "sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268"}, + {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, + {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, ] pyyaml = [ - {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, - {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, - {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, - {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, - {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] qrcode = [ {file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"}, @@ -3006,28 +3095,37 @@ restructuredtext-lint = [ {file = "ruamel.yaml.clib-0.2.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2"}, {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026"}, {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f"}, {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-win32.whl", hash = "sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f"}, {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62"}, {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c"}, {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3"}, {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-win32.whl", hash = "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2"}, {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91"}, {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6"}, {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4"}, {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-win32.whl", hash = "sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6"}, {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5"}, {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0"}, {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923"}, {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-win32.whl", hash = "sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1"}, {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-win32.whl", hash = "sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb"}, {file = "ruamel.yaml.clib-0.2.2.tar.gz", hash = "sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7"}, ] rules = [ {file = "rules-2.2.tar.gz", hash = "sha256:9bae429f9d4f91a375402990da1541f9e093b0ac077221d57124d06eeeca4405"}, ] safety = [ - {file = "safety-1.9.0-py2.py3-none-any.whl", hash = "sha256:86c1c4a031fe35bd624fce143fbe642a0234d29f7cbf7a9aa269f244a955b087"}, - {file = "safety-1.9.0.tar.gz", hash = "sha256:23bf20690d4400edc795836b0c983c2b4cbbb922233108ff925b7dd7750f00c9"}, + {file = "safety-1.10.3-py2.py3-none-any.whl", hash = "sha256:5f802ad5df5614f9622d8d71fedec2757099705c2356f862847c58c6dfe13e84"}, + {file = "safety-1.10.3.tar.gz", hash = "sha256:30e394d02a20ac49b7f65292d19d38fa927a8f9582cdfd3ad1adbbc66c641ad5"}, ] scramp = [ {file = "scramp-1.2.0-py3-none-any.whl", hash = "sha256:74815c25aad1fe0b5fb994e96c3de63e8695164358a80138352aaadfa4760350"}, @@ -3049,13 +3147,17 @@ snowballstemmer = [ {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"}, {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, ] +soupsieve = [ + {file = "soupsieve-2.1-py3-none-any.whl", hash = "sha256:4bb21a6ee4707bf43b61230e80740e71bfe56e55d1f1f50924b087bb2975c851"}, + {file = "soupsieve-2.1.tar.gz", hash = "sha256:6dc52924dc0bc710a5d16794e6b3480b2c7c08b07729505feab2b2c16661ff6e"}, +] spdx-license-list = [ {file = "spdx_license_list-0.5.2-py3-none-any.whl", hash = "sha256:1b338470c7b403dbecceca563a316382c7977516128ca6c1e8f7078e3ed6e7b0"}, {file = "spdx_license_list-0.5.2.tar.gz", hash = "sha256:952996f72ab807972dc2278bb9b91e5294767211e51f09aad9c0e2ff5b82a31b"}, ] sphinx = [ - {file = "Sphinx-3.3.1-py3-none-any.whl", hash = "sha256:d4e59ad4ea55efbb3c05cde3bfc83bfc14f0c95aa95c3d75346fcce186a47960"}, - {file = "Sphinx-3.3.1.tar.gz", hash = "sha256:1e8d592225447104d1172be415bc2972bd1357e3e12fdc76edf2261105db4300"}, + {file = "Sphinx-3.4.3-py3-none-any.whl", hash = "sha256:c314c857e7cd47c856d2c5adff514ac2e6495f8b8e0f886a8a37e9305dfea0d8"}, + {file = "Sphinx-3.4.3.tar.gz", hash = "sha256:41cad293f954f7d37f803d97eb184158cfd90f51195131e94875bc07cd08b93c"}, ] sphinx-autodoc-typehints = [ {file = "sphinx-autodoc-typehints-1.11.1.tar.gz", hash = "sha256:244ba6d3e2fdb854622f643c7763d6f95b6886eba24bec28e86edf205e4ddb20"}, @@ -3101,8 +3203,8 @@ termcolor = [ {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, ] testfixtures = [ - {file = "testfixtures-6.17.0-py2.py3-none-any.whl", hash = "sha256:ebcc3e024d47bb58a60cdc678604151baa0c920ae2814004c89ac9066de31b2c"}, - {file = "testfixtures-6.17.0.tar.gz", hash = "sha256:fa7c170df68ca6367eda061e9ec339ae3e6d3679c31e04033f83ef97a7d7d0ce"}, + {file = "testfixtures-6.17.1-py2.py3-none-any.whl", hash = "sha256:9ed31e83f59619e2fa17df053b241e16e0608f4580f7b5a9333a0c9bdcc99137"}, + {file = "testfixtures-6.17.1.tar.gz", hash = "sha256:5ec3a0dd6f71cc4c304fbc024a10cc293d3e0b852c868014b9f233203e149bda"}, ] "testing.common.database" = [ {file = "testing.common.database-2.0.3-py2.py3-none-any.whl", hash = "sha256:e3ed492bf480a87f271f74c53b262caf5d85c8bc09989a8f534fa2283ec52492"}, @@ -3121,34 +3223,43 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tqdm = [ - {file = "tqdm-4.54.1-py2.py3-none-any.whl", hash = "sha256:d4f413aecb61c9779888c64ddf0c62910ad56dcbe857d8922bb505d4dbff0df1"}, - {file = "tqdm-4.54.1.tar.gz", hash = "sha256:38b658a3e4ecf9b4f6f8ff75ca16221ae3378b2e175d846b6b33ea3a20852cf5"}, + {file = "tqdm-4.56.0-py2.py3-none-any.whl", hash = "sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a"}, + {file = "tqdm-4.56.0.tar.gz", hash = "sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65"}, ] twilio = [ - {file = "twilio-6.50.1.tar.gz", hash = "sha256:dd8371c9b4ea422d6de7526b63b587da82e8488f2b3f6b1258d2cad6e4006a65"}, + {file = "twilio-6.51.0.tar.gz", hash = "sha256:de98a05858e6efdf87bfa4c8f7e773adf1885cfcbb6531356840bcd17dc4c444"}, ] typed-ast = [ - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, - {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, - {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, - {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, - {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, - {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, - {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"}, + {file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"}, + {file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"}, + {file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"}, + {file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"}, + {file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"}, + {file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"}, + {file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"}, + {file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"}, + {file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"}, + {file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"}, + {file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"}, + {file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"}, + {file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"}, + {file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"}, + {file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"}, ] typing-extensions = [ {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, diff --git a/pyproject.toml b/pyproject.toml index cfb02aa3649f591f00fac32004c0a397e43a09ab..9a388a12d9da6e4e195a175072832dc8ce7a59fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "AlekSIS-Core" -version = "2.0a3.dev0" +version = "2.0a4.dev0" packages = [ { include = "aleksis" } ] @@ -38,7 +38,7 @@ Django = "^3.0" django-any-js = "^1.0" django-debug-toolbar = "^2.0" django-middleware-global-request = "^0.1.2" -django-menu-generator = "^1.0.4" +django-menu-generator-ng = "^1.2.0" django-tables2 = "^2.1" Pillow = "^8.0" django-phonenumber-field = {version = "<5.1", extras = ["phonenumbers"]} @@ -72,7 +72,7 @@ Celery = {version="^5.0.0", optional=true, extras=["django", "redis"]} django-celery-results = {version="^2.0.0", optional=true} django-celery-beat = {version="^2.0.0", optional=true} django-celery-email = {version="^3.0.0", optional=true} -django-jsonstore = "^0.4.1" +django-jsonstore = "^0.5.0" django-polymorphic = "^3.0.0" django-colorfield = "^0.3.0" django-bleach = "^0.6.1" @@ -93,6 +93,7 @@ django-cachalot = "^2.3.2" django-prometheus = "^2.1.0" importlib-metadata = {version = "^3.0.0", python = "<3.9"} django-model-utils = "^4.0.0" +bs4 = "^0.0.1" [tool.poetry.extras] ldap = ["django-auth-ldap"] diff --git a/tox.ini b/tox.ini index 98fce3af53aa48d1eac2e28454c26e7b74c23f72..e1283a1b72150383c5c2c9b7fbf43abb6cdf6ea2 100644 --- a/tox.ini +++ b/tox.ini @@ -9,9 +9,11 @@ whitelist_externals = poetry skip_install = true envdir = {toxworkdir}/globalenv commands_pre = - - poetry install + poetry install + poetry run aleksis-admin yarn install + poetry run aleksis-admin collectstatic --no-input commands = - - poetry run pytest --cov=. {posargs} aleksis/ + poetry run pytest --cov=. {posargs} aleksis/ [testenv:selenium] setenv =