diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..996cd5aad4d7331e9c74dcaa7eb6483a5116eed9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,216 @@ +module.exports = { + extends: [ + "eslint:recommended", + "plugin:vue/strongly-recommended", + // "plugin:prettier/recommended", + "plugin:@intlify/vue-i18n/recommended", + ], + rules: { + "no-unused-vars": "warn", + "vue/no-unused-vars": "off", + "vue/multi-word-component-names": "off", + "@intlify/vue-i18n/key-format-style": [ + "error", + "snake_case", + { + splitByDots: false, + }, + ], + // "@intlify/vue-i18n/no-unused-keys": ["warn", {}], + "@intlify/vue-i18n/no-raw-text": [ + "error", + { + ignoreNodes: ["v-icon"], + ignorePattern: "^[-–—·#:()\\[\\]&\\.\\s]+$", + }, + ], + // Fixes for prettier (avoid eslint-config-prettier) + // The following rules can be used in some cases. See the README for more + // information. (These are marked with `0` instead of `"off"` so that a + // script can distinguish them.) + curly: 0, + "lines-around-comment": 0, + "max-len": 0, + "no-confusing-arrow": 0, + "no-mixed-operators": 0, + "no-tabs": 0, + "no-unexpected-multiline": 0, + quotes: 0, + "@typescript-eslint/quotes": 0, + "babel/quotes": 0, + "vue/html-self-closing": 0, + "vue/max-len": 0, + + // The rest are rules that you never need to enable when using Prettier. + "array-bracket-newline": "off", + "array-bracket-spacing": "off", + "array-element-newline": "off", + "arrow-parens": "off", + "arrow-spacing": "off", + "block-spacing": "off", + "brace-style": "off", + "comma-dangle": "off", + "comma-spacing": "off", + "comma-style": "off", + "computed-property-spacing": "off", + "dot-location": "off", + "eol-last": "off", + "func-call-spacing": "off", + "function-call-argument-newline": "off", + "function-paren-newline": "off", + "generator-star": "off", + "generator-star-spacing": "off", + "implicit-arrow-linebreak": "off", + indent: "off", + "jsx-quotes": "off", + "key-spacing": "off", + "keyword-spacing": "off", + "linebreak-style": "off", + "multiline-ternary": "off", + "newline-per-chained-call": "off", + "new-parens": "off", + "no-arrow-condition": "off", + "no-comma-dangle": "off", + "no-extra-parens": "off", + "no-extra-semi": "off", + "no-floating-decimal": "off", + "no-mixed-spaces-and-tabs": "off", + "no-multi-spaces": "off", + "no-multiple-empty-lines": "off", + "no-reserved-keys": "off", + "no-space-before-semi": "off", + "no-trailing-spaces": "off", + "no-whitespace-before-property": "off", + "no-wrap-func": "off", + "nonblock-statement-body-position": "off", + "object-curly-newline": "off", + "object-curly-spacing": "off", + "object-property-newline": "off", + "one-var-declaration-per-line": "off", + "operator-linebreak": "off", + "padded-blocks": "off", + "quote-props": "off", + "rest-spread-spacing": "off", + semi: "off", + "semi-spacing": "off", + "semi-style": "off", + "space-after-function-name": "off", + "space-after-keywords": "off", + "space-before-blocks": "off", + "space-before-function-paren": "off", + "space-before-function-parentheses": "off", + "space-before-keywords": "off", + "space-in-brackets": "off", + "space-in-parens": "off", + "space-infix-ops": "off", + "space-return-throw-case": "off", + "space-unary-ops": "off", + "space-unary-word-ops": "off", + "switch-colon-spacing": "off", + "template-curly-spacing": "off", + "template-tag-spacing": "off", + "unicode-bom": "off", + "wrap-iife": "off", + "wrap-regex": "off", + "yield-star-spacing": "off", + "@babel/object-curly-spacing": "off", + "@babel/semi": "off", + "@typescript-eslint/brace-style": "off", + "@typescript-eslint/comma-dangle": "off", + "@typescript-eslint/comma-spacing": "off", + "@typescript-eslint/func-call-spacing": "off", + "@typescript-eslint/indent": "off", + "@typescript-eslint/keyword-spacing": "off", + "@typescript-eslint/member-delimiter-style": "off", + "@typescript-eslint/no-extra-parens": "off", + "@typescript-eslint/no-extra-semi": "off", + "@typescript-eslint/object-curly-spacing": "off", + "@typescript-eslint/semi": "off", + "@typescript-eslint/space-before-blocks": "off", + "@typescript-eslint/space-before-function-paren": "off", + "@typescript-eslint/space-infix-ops": "off", + "@typescript-eslint/type-annotation-spacing": "off", + "babel/object-curly-spacing": "off", + "babel/semi": "off", + "flowtype/boolean-style": "off", + "flowtype/delimiter-dangle": "off", + "flowtype/generic-spacing": "off", + "flowtype/object-type-curly-spacing": "off", + "flowtype/object-type-delimiter": "off", + "flowtype/quotes": "off", + "flowtype/semi": "off", + "flowtype/space-after-type-colon": "off", + "flowtype/space-before-generic-bracket": "off", + "flowtype/space-before-type-colon": "off", + "flowtype/union-intersection-spacing": "off", + "react/jsx-child-element-spacing": "off", + "react/jsx-closing-bracket-location": "off", + "react/jsx-closing-tag-location": "off", + "react/jsx-curly-newline": "off", + "react/jsx-curly-spacing": "off", + "react/jsx-equals-spacing": "off", + "react/jsx-first-prop-new-line": "off", + "react/jsx-indent": "off", + "react/jsx-indent-props": "off", + "react/jsx-max-props-per-line": "off", + "react/jsx-newline": "off", + "react/jsx-one-expression-per-line": "off", + "react/jsx-props-no-multi-spaces": "off", + "react/jsx-tag-spacing": "off", + "react/jsx-wrap-multilines": "off", + "standard/array-bracket-even-spacing": "off", + "standard/computed-property-even-spacing": "off", + "standard/object-curly-even-spacing": "off", + "unicorn/empty-brace-spaces": "off", + "unicorn/no-nested-ternary": "off", + "unicorn/number-literal-case": "off", + "vue/array-bracket-newline": "off", + "vue/array-bracket-spacing": "off", + "vue/arrow-spacing": "off", + "vue/block-spacing": "off", + "vue/block-tag-newline": "off", + "vue/brace-style": "off", + "vue/comma-dangle": "off", + "vue/comma-spacing": "off", + "vue/comma-style": "off", + "vue/dot-location": "off", + "vue/func-call-spacing": "off", + "vue/html-closing-bracket-newline": "off", + "vue/html-closing-bracket-spacing": "off", + "vue/html-end-tags": "off", + "vue/html-indent": "off", + "vue/html-quotes": "off", + "vue/key-spacing": "off", + "vue/keyword-spacing": "off", + "vue/max-attributes-per-line": "off", + "vue/multiline-html-element-content-newline": "off", + "vue/multiline-ternary": "off", + "vue/mustache-interpolation-spacing": "off", + "vue/no-extra-parens": "off", + "vue/no-multi-spaces": "off", + "vue/no-spaces-around-equal-signs-in-attribute": "off", + "vue/object-curly-newline": "off", + "vue/object-curly-spacing": "off", + "vue/object-property-newline": "off", + "vue/operator-linebreak": "off", + "vue/quote-props": "off", + "vue/script-indent": "off", + "vue/singleline-html-element-content-newline": "off", + "vue/space-in-parens": "off", + "vue/space-infix-ops": "off", + "vue/space-unary-ops": "off", + "vue/template-curly-spacing": "off", + }, + settings: { + "vue-i18n": { + localeDir: "./aleksis/core/frontend/messages/*.{json}", + messageSyntaxVersion: "^8.0.0", + }, + }, + env: { + es2021: true, + }, + parserOptions: { + ecmaVersion: "latest", + }, +}; diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f8bcd06c637e4de890c6a8b96312a05f102ca0f8..b2274c0f1da0fa3578ec8730511431f71bb085a9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,17 +1,17 @@ include: - - project: "AlekSIS/official/AlekSIS" - file: /ci/general.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/prepare/lock.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/test/test.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/test/lint.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/test/security.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/build/dist.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/publish/pypi.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/docker/image.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/general.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/prepare/lock.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/test/test.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/test/lint.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/test/security.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/build/dist.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/publish/pypi.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/docker/image.yml diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000000000000000000000000000000000..38d141b743fd55678f50077c0617924475817095 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,91 @@ +# Byte-compiled / optimized / DLL files +*$py.class +*.py[cod] +__pycache__/ + +# Distribution / packaging +*.egg +*.egg-info/ +.Python +.eggs/ +.installed.cfg +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ + +# Installer logs +pip-delete-this-directory.txt +pip-log.txt + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# pyenv +.python-version + +# Environments +.env +.venv +ENV/ +env/ +venv/ + +# Editors +*~ +DEADJOE +\#*# + +# IntelliJ +.idea +.idea/ + +# Database +db.sqlite3 + +# Sphinx +docs/_build/ + +# TeX +*.aux + +# Generated files +/node_modules/ +/static/ +/whoosh_index/ +poetry.lock + +.coverage +.mypy_cache/ +.tox/ +htmlcov/ +maintenance_mode_state.txt +media/ +package-lock.json +yarn.lock + +# VSCode +.vscode/ +.history/ +*.code-workspace + +/cache + +# Add HTML files to avoid problems with unsupported Django templates +*.html + +# Do not check/reformat generated files +aleksis/core/util/licenses.json +.vite/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0fca141daca6c006186b994755c7047e3e5e74bc..909a4ea7ee838a66f6a7e834cda7baac9eaf1516 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,9 +14,42 @@ Added * Add feature to manage instructions and track whether groups have been instructed accordingly (e. g. for alarm plans). +Fixed +~~~~~ +* Migrating failed due to an incorrect field reference. + +`3.0`_ - 2023-05-15 +------------------- + +Fixed +~~~~~ +* In some cases, pages showing the count of extra marks and lessons with custom excuse types of + persons threw an error. +* The redirection to generated class register PDF printouts did not work. +* Some columns in the table showing statistics for the members of a group were labled wrongly. +* Absences with custom excuse types were not counted correctly. +* Tabs on the week overview page were not displayed. + +`3.0b0`_ - 2023-02-28 +--------------------- + +This version requires AlekSIS-Core 3.0. It is incompatible with any previous +version. + +Removed +~~~~~~~ + +* Legacy menu integration for AlekSIS-Core pre-3.0 + +Added +~~~~~ + +* Add SPA support for AlekSIS-Core 3.0 + Changed ~~~~~~~ +* [Dev] Rename the "late" field in the PersonalNote model to "tardiness". * Use new icon set inside of models and templates * Run full register printout generation in background @@ -25,6 +58,7 @@ Fixed * Extra marks and excused absences were counted multiple times in some class register views. * Substitution teachers couldn't see any persons in the person list of a substituted lesson. +* Events were shown for days not being inside the timetable schema in full register printout. `2.1.1`_ - 2022-09-01 --------------------- @@ -292,3 +326,5 @@ Fixed .. _2.0.1: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/2.0.1 .. _2.1: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/2.1 .. _2.1.1: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/2.1.1 +.. _3.0b0: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/3.0b0 +.. _3.0: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/3.0 diff --git a/aleksis/apps/alsijil/actions.py b/aleksis/apps/alsijil/actions.py index 9c307017ee629eec9f03cc170ed673af92b306b8..623a96b95ad28e319d16b4b2595f74352a03655d 100644 --- a/aleksis/apps/alsijil/actions.py +++ b/aleksis/apps/alsijil/actions.py @@ -41,7 +41,7 @@ def delete_personal_note(modeladmin, request, queryset): personal_note.reset_values() notes.append(personal_note) PersonalNote.objects.bulk_update( - notes, fields=["absent", "excused", "late", "excuse_type", "remarks"] + notes, fields=["absent", "excused", "tardiness", "excuse_type", "remarks"] ) diff --git a/aleksis/apps/alsijil/filters.py b/aleksis/apps/alsijil/filters.py index 1e03f7ba399699055a7fda25b4d8680f3617b6f3..7337b94f41f445ffcc595f13d4f55637e9072d30 100644 --- a/aleksis/apps/alsijil/filters.py +++ b/aleksis/apps/alsijil/filters.py @@ -27,20 +27,20 @@ class PersonalNoteFilter(FilterSet): data[name] = initial super().__init__(data, *args, **kwargs) - self.form.fields["late__lt"].label = _("Tardiness is lower than") - self.form.fields["late__gt"].label = _("Tardiness is bigger than") + self.form.fields["tardiness__lt"].label = _("Tardiness is lower than") + self.form.fields["tardiness__gt"].label = _("Tardiness is bigger than") self.form.layout = Layout( Row("subject"), Row("day_start", "day_end"), Row("absent", "excused", "excuse_type"), - Row("late__gt", "late__lt", "extra_marks"), + Row("tardiness__gt", "tardiness__lt", "extra_marks"), ) class Meta: model = PersonalNote fields = { "excused": ["exact"], - "late": ["lt", "gt"], + "tardiness": ["lt", "gt"], "absent": ["exact"], "excuse_type": ["exact"], "extra_marks": ["exact"], diff --git a/aleksis/apps/alsijil/forms.py b/aleksis/apps/alsijil/forms.py index d4353f381308cb079440c16d2362fb2e3f095cf3..8c6d0b5147fd4385639aac15464ed323b90f72f1 100644 --- a/aleksis/apps/alsijil/forms.py +++ b/aleksis/apps/alsijil/forms.py @@ -78,7 +78,7 @@ class LessonDocumentationForm(forms.ModelForm): class PersonalNoteForm(forms.ModelForm): class Meta: model = PersonalNote - fields = ["absent", "late", "excused", "excuse_type", "extra_marks", "remarks"] + fields = ["absent", "tardiness", "excused", "excuse_type", "extra_marks", "remarks"] person_name = forms.CharField(disabled=True) diff --git a/aleksis/apps/alsijil/frontend/index.js b/aleksis/apps/alsijil/frontend/index.js new file mode 100644 index 0000000000000000000000000000000000000000..38293a43aeee9f3b9dc25a725f2531ebae71388c --- /dev/null +++ b/aleksis/apps/alsijil/frontend/index.js @@ -0,0 +1,407 @@ +import { + notLoggedInValidator, + hasPersonValidator, +} from "aleksis.core/routeValidators"; + +export default { + meta: { + inMenu: true, + titleKey: "alsijil.menu_title", + icon: "mdi-account-group-outline", + validators: [hasPersonValidator], + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + children: [ + { + path: "lesson", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.lessonPeriod", + meta: { + inMenu: true, + titleKey: "alsijil.lesson.menu_title", + icon: "mdi-alarm", + permission: "alsijil.view_lesson_menu_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "lesson/:year(\\d+)/:week(\\d+)/:id_(\\d+)", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.lessonPeriodByCWAndID", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "extra_lesson/:id_(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.extraLessonByID", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "event/:id_(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.eventByID", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "week/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.weekView", + meta: { + inMenu: true, + titleKey: "alsijil.week.menu_title", + icon: "mdi-view-week-outline", + permission: "alsijil.view_week_menu_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "week/:year(\\d+)/:week(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.weekViewByWeek", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "week/year/cw/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.weekViewPlaceholders", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "week/:type_/:id_(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.weekViewByTypeAndID", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "week/year/cw/:type_/:id_(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.weekViewPlaceholdersByTypeAndID", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "week/:year(\\d+)/:week(\\d+)/:type_/:id_(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.weekViewByWeekTypeAndID", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "print/group/:id_(\\d+)", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.fullRegisterGroup", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "groups/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.myGroups", + meta: { + inMenu: true, + titleKey: "alsijil.groups.menu_title", + icon: "mdi-account-multiple-outline", + permission: "alsijil.view_my_groups_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "groups/:pk(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.studentsList", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "persons/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.myStudents", + meta: { + inMenu: true, + titleKey: "alsijil.persons.menu_title", + icon: "mdi-account-school-outline", + permission: "alsijil.view_my_students_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "instructions/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.instructions", + meta: { + inMenu: true, + titleKey: "alsijil.instruction.menu_title", + icon: "mdi-folder-check-outline", + permission: "alsijil.view_instructions_rule" + } + }, + { + path: "persons/:id_(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.overviewPerson", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "me/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.overviewMe", + meta: { + inMenu: true, + titleKey: "alsijil.my_overview.menu_title", + icon: "mdi-chart-box-outline", + permission: "alsijil.view_person_overview_menu_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "notes/:pk(\\d+)/delete/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.deletePersonalNote", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "absence/new/:id_(\\d+)/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.registerAbsenceWithID", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "absence/new/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.registerAbsence", + meta: { + inMenu: true, + titleKey: "alsijil.absence.menu_title", + icon: "mdi-message-alert-outline", + permission: "alsijil.view_register_absence_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "extra_marks/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.extraMarks", + meta: { + inMenu: true, + titleKey: "alsijil.extra_marks.menu_title", + icon: "mdi-label-variant-outline", + permission: "alsijil.view_extramarks_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "extra_marks/create/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.createExtraMark", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "extra_marks/:pk(\\d+)/edit/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.editExtraMark", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "extra_marks/:pk(\\d+)/delete/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.deleteExtraMark", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "excuse_types/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.excuseTypes", + meta: { + inMenu: true, + titleKey: "alsijil.excuse_types.menu_title", + icon: "mdi-label-outline", + permission: "alsijil.view_excusetypes_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "excuse_types/create/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.createExcuseType", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "excuse_types/:pk(\\d+)/edit/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.editExcuseType", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "excuse_types/:pk(\\d+)/delete/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.deleteExcuseType", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "group_roles/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.groupRoles", + meta: { + inMenu: true, + titleKey: "alsijil.group_roles.menu_title_manage", + icon: "mdi-clipboard-plus-outline", + permission: "alsijil.view_grouproles_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "group_roles/create/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.createGroupRole", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "group_roles/:pk(\\d+)/edit/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.editGroupRole", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "group_roles/:pk(\\d+)/delete/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.deleteGroupRole", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "groups/:pk(\\d+)/group_roles/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.assignedGroupRoles", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "groups/:pk(\\d+)/group_roles/assign/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.assignGroupRole", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "groups/:pk(\\d+)/group_roles/:role_pk(\\d+)/assign/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.assignGroupRoleByRolePK", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "group_roles/assignments/:pk(\\d+)/edit/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.editGroupRoleAssignment", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "group_roles/assignments/:pk(\\d+)/stop/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.stopGroupRoleAssignment", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "group_roles/assignments/:pk(\\d+)/delete/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.deleteGroupRoleAssignment", + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "group_roles/assignments/assign/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.assignGroupRoleMultiple", + meta: { + inMenu: true, + titleKey: "alsijil.group_roles.menu_title_assign", + icon: "mdi-clipboard-account-outline", + permission: "alsijil.assign_grouprole_for_multiple_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + { + path: "all/", + component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), + name: "alsijil.allRegisterObjects", + meta: { + inMenu: true, + titleKey: "alsijil.all_lessons.menu_title", + icon: "mdi-format-list-text", + permission: "alsijil.view_register_objects_list_rule", + }, + props: { + byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, + }, + }, + ], +}; diff --git a/aleksis/apps/alsijil/frontend/messages/de.json b/aleksis/apps/alsijil/frontend/messages/de.json new file mode 100644 index 0000000000000000000000000000000000000000..09456c74481a13e390b1e08e2389d027d8c6974d --- /dev/null +++ b/aleksis/apps/alsijil/frontend/messages/de.json @@ -0,0 +1,36 @@ +{ + "alsijil": { + "menu_title": "Klassenbuch", + "lesson": { + "menu_title": "Aktuelle Unterrichtsstunde" + }, + "week": { + "menu_title": "Aktuelle Woche" + }, + "groups": { + "menu_title": "Meine Gruppen" + }, + "persons": { + "menu_title": "Meine Schülerinnen und Schüler" + }, + "absence": { + "menu_title": "Abwesenheit eintragen" + }, + "my_overview": { + "menu_title": "Meine Übersicht" + }, + "excuse_types": { + "menu_title": "Entschuldigungsarten" + }, + "group_roles": { + "menu_title_assign": "Gruppenrollen zuweisen", + "menu_title_manage": "Gruppenrollen verwalten" + }, + "extra_marks": { + "menu_title": "Zusätzliche Markierungen" + }, + "all_lessons": { + "menu_title": "Alle Stunden" + } + } +} diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json new file mode 100644 index 0000000000000000000000000000000000000000..cd9798229b0d867611da8dc6b89dfe02eae91f85 --- /dev/null +++ b/aleksis/apps/alsijil/frontend/messages/en.json @@ -0,0 +1,36 @@ +{ + "alsijil": { + "lesson": { + "menu_title": "Current lesson" + }, + "week": { + "menu_title": "Current week" + }, + "groups": { + "menu_title": "My groups" + }, + "persons": { + "menu_title": "My students" + }, + "absence": { + "menu_title": "Register absence" + }, + "my_overview": { + "menu_title": "My overview" + }, + "extra_marks": { + "menu_title": "Extra marks" + }, + "excuse_types": { + "menu_title": "Excuse types" + }, + "group_roles": { + "menu_title_manage": "Manage group roles", + "menu_title_assign": "Assign group roles" + }, + "all_lessons": { + "menu_title": "All lessons" + }, + "menu_title": "Class register" + } +} diff --git a/aleksis/apps/alsijil/frontend/messages/ru.json b/aleksis/apps/alsijil/frontend/messages/ru.json new file mode 100644 index 0000000000000000000000000000000000000000..8fb7aea426577ea2406c1579dbe44b39b70e5ef6 --- /dev/null +++ b/aleksis/apps/alsijil/frontend/messages/ru.json @@ -0,0 +1,36 @@ +{ + "alsijil": { + "my_overview": { + "menu_title": "Мой обзор" + }, + "group_roles": { + "menu_title_manage": "Управление ролÑми групп", + "menu_title_assign": "Ðазначить роль группы" + }, + "all_lessons": { + "menu_title": "Ð’Ñе уроки" + }, + "menu_title": "КлаÑÑный журнал", + "lesson": { + "menu_title": "Текущий урок" + }, + "week": { + "menu_title": "Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð½ÐµÐ´ÐµÐ»Ñ" + }, + "groups": { + "menu_title": "Мои группы" + }, + "persons": { + "menu_title": "Мои Ñтуденты" + }, + "absence": { + "menu_title": "РегиÑÑ‚Ñ€Ð°Ñ†Ð¸Ñ Ð¾Ñ‚ÑутÑтвиÑ" + }, + "extra_marks": { + "menu_title": "Дополнительные отметки" + }, + "excuse_types": { + "menu_title": "Типы объÑÑнительных" + } + } +} diff --git a/aleksis/apps/alsijil/frontend/messages/uk.json b/aleksis/apps/alsijil/frontend/messages/uk.json new file mode 100644 index 0000000000000000000000000000000000000000..a29573bea059ddf91b6ae156be5d76914dd2ca50 --- /dev/null +++ b/aleksis/apps/alsijil/frontend/messages/uk.json @@ -0,0 +1,36 @@ +{ + "alsijil": { + "week": { + "menu_title": "Поточний тиждень" + }, + "groups": { + "menu_title": "Мої групи" + }, + "persons": { + "menu_title": "Мої Ñтуденти" + }, + "absence": { + "menu_title": "РеєÑÑ‚Ñ€Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´ÑутноÑті" + }, + "my_overview": { + "menu_title": "Мій оглÑд" + }, + "extra_marks": { + "menu_title": "Додаткові відмітки" + }, + "excuse_types": { + "menu_title": "Типи поÑÑнень" + }, + "group_roles": { + "menu_title_manage": "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð»Ñми групи", + "menu_title_assign": "Призначити роль групи" + }, + "all_lessons": { + "menu_title": "УÑÑ– уроки" + }, + "menu_title": "КлаÑний журнал", + "lesson": { + "menu_title": "Поточний урок" + } + } +} diff --git a/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po index 3619b388cb11b49e194af013e385a0b6d67b3ef4..fde32a7d86c67b9f2344ca1a4aa59435fcf86cfa 100644 --- a/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po +++ b/aleksis/apps/alsijil/locale/ru/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-08-15 10:22+0200\n" -"PO-Revision-Date: 2022-07-03 02:56+0000\n" +"POT-Creation-Date: 2022-06-25 15:28+0200\n" +"PO-Revision-Date: 2023-05-26 04:38+0000\n" "Last-Translator: Serhii Horichenko <m@sgg.im>\n" "Language-Team: Russian <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/ru/>\n" "Language: ru\n" @@ -355,7 +355,7 @@ msgstr "Короткое имÑ" #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:12 #: aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html:23 msgid "Name" -msgstr "ИмÑ" +msgstr "Полное имÑ" #: aleksis/apps/alsijil/models.py:60 aleksis/apps/alsijil/tables.py:41 msgid "Count as absent" diff --git a/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po b/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po index f1bfeaa57fbc10f7104dac8e4e079330a234fe73..ff5ccddf60a3017b9db6076adc9a0c2a9cf16dc4 100644 --- a/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po +++ b/aleksis/apps/alsijil/locale/uk/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-08-15 10:22+0200\n" -"PO-Revision-Date: 2022-07-03 02:56+0000\n" +"POT-Creation-Date: 2022-06-25 15:28+0200\n" +"PO-Revision-Date: 2023-01-25 05:58+0000\n" "Last-Translator: Serhii Horichenko <m@sgg.im>\n" "Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/uk/>\n" "Language: uk\n" @@ -429,7 +429,7 @@ msgstr "Додаткова відмітка" #: aleksis/apps/alsijil/models.py:469 aleksis/apps/alsijil/models.py:530 msgid "Icon" -msgstr "Іконка" +msgstr "Піктограма" #: aleksis/apps/alsijil/models.py:470 msgid "Colour" diff --git a/aleksis/apps/alsijil/managers.py b/aleksis/apps/alsijil/managers.py index 9d97e58091af8ab2149122d7e51f767faccb61f6..c9e26fa77a83dfe76212478d58e36c08dca4f4a9 100644 --- a/aleksis/apps/alsijil/managers.py +++ b/aleksis/apps/alsijil/managers.py @@ -119,7 +119,7 @@ class PersonalNoteQuerySet(RegisterObjectRelatedQuerySet, QuerySet): def not_empty(self): """Get all not empty personal notes.""" return self.filter( - ~Q(remarks="") | Q(absent=True) | ~Q(late=0) | Q(extra_marks__isnull=False) + ~Q(remarks="") | Q(absent=True) | ~Q(tardiness=0) | Q(extra_marks__isnull=False) ) diff --git a/aleksis/apps/alsijil/menus.py b/aleksis/apps/alsijil/menus.py deleted file mode 100644 index 2edf685e21345b9f2dbf1faefa804053c40ec117..0000000000000000000000000000000000000000 --- a/aleksis/apps/alsijil/menus.py +++ /dev/null @@ -1,150 +0,0 @@ -from django.utils.translation import gettext_lazy as _ - -MENUS = { - "NAV_MENU_CORE": [ - { - "name": _("Class register"), - "url": "#", - "svg_icon": "mdi:book-open-outline", - "root": True, - "validators": [ - "menu_generator.validators.is_authenticated", - "aleksis.core.util.core_helpers.has_person", - ], - "submenu": [ - { - "name": _("Current lesson"), - "url": "lesson_period", - "svg_icon": "mdi:alarm", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_lesson_menu_rule", - ), - ], - }, - { - "name": _("Current week"), - "url": "week_view", - "svg_icon": "mdi:view-week-outline", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_week_menu_rule", - ), - ], - }, - { - "name": _("My groups"), - "url": "my_groups", - "svg_icon": "mdi:account-multiple-outline", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_my_groups_rule", - ), - ], - }, - { - "name": _("My overview"), - "url": "overview_me", - "svg_icon": "mdi:chart-box-outline", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_person_overview_menu_rule", - ), - ], - }, - { - "name": _("My students"), - "url": "my_students", - "svg_icon": "mdi:account-school-outline", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_my_students_rule", - ), - ], - }, - { - "name": _("Instructions"), - "url": "instructions", - "icon": "rule_folder", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_instructions_rule", - ), - ], - }, - { - "name": _("Assign group role"), - "url": "assign_group_role_multiple", - "svg_icon": "mdi:clipboard-account-outline", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.assign_grouprole_for_multiple_rule", - ), - ], - }, - { - "name": _("All lessons"), - "url": "all_register_objects", - "svg_icon": "mdi:format-list-text", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_register_objects_list_rule", - ), - ], - }, - { - "name": _("Register absence"), - "url": "register_absence", - "icon": "rate_review", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_register_absence_rule", - ), - ], - }, - { - "name": _("Excuse types"), - "url": "excuse_types", - "svg_icon": "mdi:label-outline", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_excusetypes_rule", - ), - ], - }, - { - "name": _("Extra marks"), - "url": "extra_marks", - "svg_icon": "mdi:label-variant-outline", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_extramarks_rule", - ), - ], - }, - { - "name": _("Manage group roles"), - "url": "group_roles", - "svg_icon": "mdi:clipboard-plus-outline", - "validators": [ - ( - "aleksis.core.util.predicates.permission_validator", - "alsijil.view_grouproles_rule", - ), - ], - }, - ], - } - ] -} diff --git a/aleksis/apps/alsijil/migrations/0001_initial.py b/aleksis/apps/alsijil/migrations/0001_initial.py index 4a88b98b89eb6b5ed6aa914096c032b689710bde..e49edfac8a4cdb1f2684c3f2667d3e1ef18de003 100644 --- a/aleksis/apps/alsijil/migrations/0001_initial.py +++ b/aleksis/apps/alsijil/migrations/0001_initial.py @@ -130,7 +130,7 @@ class Migration(migrations.Migration): "verbose_name": "Personal note", "verbose_name_plural": "Personal notes", "ordering": [ - "lesson_period__lesson__date_start", + "lesson_period__lesson__validity__date_start", "week", "lesson_period__period__weekday", "lesson_period__period__period", @@ -194,7 +194,7 @@ class Migration(migrations.Migration): "verbose_name": "Lesson documentation", "verbose_name_plural": "Lesson documentations", "ordering": [ - "lesson_period__lesson__date_start", + "lesson_period__lesson__validity__date_start", "week", "lesson_period__period__weekday", "lesson_period__period__period", diff --git a/aleksis/apps/alsijil/migrations/0017_rename_late_to_tardiness.py b/aleksis/apps/alsijil/migrations/0017_rename_late_to_tardiness.py new file mode 100644 index 0000000000000000000000000000000000000000..50dd5a18d2ce0271a3a145ff1f3605aeb1b6813e --- /dev/null +++ b/aleksis/apps/alsijil/migrations/0017_rename_late_to_tardiness.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.15 on 2022-08-27 15:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('alsijil', '0016_add_not_counted_excuse_types'), + ] + + operations = [ + migrations.RenameField( + model_name='personalnote', + old_name='late', + new_name='tardiness', + ), + ] diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py index 2beb3c113c13556d99f6541a6bcb34c9cd0baaed..abbedd709dd3604172b831c2c931a2ea5f198b5a 100644 --- a/aleksis/apps/alsijil/model_extensions.py +++ b/aleksis/apps/alsijil/model_extensions.py @@ -52,7 +52,7 @@ def mark_absent( to_period: Optional[int] = None, dry_run: bool = False, ): - """Mark a person absent for all lessons in a day, optionally starting with a selected period number. + """Mark a person absent for all lessons in a day, optionally starting with a period number. This function creates `PersonalNote` objects for every `LessonPeriod` and `ExtraLesson` the person participates in on the selected day and marks them as absent/excused. @@ -326,12 +326,12 @@ def get_tardinesses(self, week: Optional[CalendarWeek] = None) -> QuerySet: """Get all personal notes of late persons for this lesson.""" if not week: week = self.week - return self.personal_notes.filter(week=week.week, year=week.year, late__gt=0) + return self.personal_notes.filter(week=week.week, year=week.year, tardiness__gt=0) def get_tardinesses_simple(self, week: Optional[CalendarWeek] = None) -> QuerySet: """Get all personal notes of late persons for this event/extra lesson.""" - return self.personal_notes.filter(late__gt=0) + return self.personal_notes.filter(tardiness__gt=0) Event.method(get_tardinesses_simple, "get_tardinesses") @@ -459,9 +459,11 @@ def generate_person_list_with_class_register_statistics( filter=Q(filtered_personal_notes__absent=True, filtered_personal_notes__excused=False), distinct=True, ), - tardiness=Sum("filtered_personal_notes__late"), + tardiness=Sum("filtered_personal_notes__tardiness"), tardiness_count=Count( - "filtered_personal_notes", filter=Q(filtered_personal_notes__late__gt=0), distinct=True + "filtered_personal_notes", + filter=Q(filtered_personal_notes__tardiness__gt=0), + distinct=True, ), ) @@ -480,7 +482,7 @@ def generate_person_list_with_class_register_statistics( persons = persons.annotate( **{ excuse_type.count_label: Count( - "filtered_personal_notes__absent", + "filtered_personal_notes", filter=Q( filtered_personal_notes__absent=True, filtered_personal_notes__excuse_type=excuse_type, diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py index 277e2bfe03d6495767cf27c15782f627f7eccf2d..0b651945f31604f09230fad7739ccfb17902a9d1 100644 --- a/aleksis/apps/alsijil/models.py +++ b/aleksis/apps/alsijil/models.py @@ -71,7 +71,7 @@ class ExcuseType(ExtensibleModel): @property def count_label(self): - return f"{self.short_name}_count" + return f"excuse_type_{self.id}_count" class Meta: ordering = ["name"] @@ -250,7 +250,7 @@ class PersonalNote(RegisterObjectRelatedMixin, ExtensibleModel): ) absent = models.BooleanField(default=False) - late = models.PositiveSmallIntegerField(default=0) + tardiness = models.PositiveSmallIntegerField(default=0) excused = models.BooleanField(default=False) excuse_type = models.ForeignKey( ExcuseType, @@ -282,7 +282,7 @@ class PersonalNote(RegisterObjectRelatedMixin, ExtensibleModel): defaults = PersonalNote() self.absent = defaults.absent - self.late = defaults.late + self.tardiness = defaults.tardiness self.excused = defaults.excused self.excuse_type = defaults.excuse_type self.remarks = defaults.remarks @@ -450,7 +450,7 @@ class ExtraMark(ExtensibleModel): @property def count_label(self): - return f"{self.short_name}_count" + return f"extra_mark_{self.id}_count" class Meta: ordering = ["short_name"] diff --git a/aleksis/apps/alsijil/static/css/alsijil/alsijil.css b/aleksis/apps/alsijil/static/css/alsijil/alsijil.css index 3e14ca014661f0f80d1df639d02949db6ffb59e9..a30fb99bb8b239ca38f7f4d627329594387741c6 100644 --- a/aleksis/apps/alsijil/static/css/alsijil/alsijil.css +++ b/aleksis/apps/alsijil/static/css/alsijil/alsijil.css @@ -9,52 +9,52 @@ table a.tr-link { } .collapsible-icon-right { - align-self: end; - flex-grow: 100; - text-align: right!important; + align-self: end; + flex-grow: 100; + text-align: right !important; } @media only screen and (min-width: 1201px) { - .hide-on-extra-large-only { - display: none; - } + .hide-on-extra-large-only { + display: none; + } } @media only screen and (max-width: 1200px) { - .show-on-extra-large { - display: none; - } + .show-on-extra-large { + display: none; + } } @media only screen and (max-width: 600px) { - .collection .collection-item.avatar { - padding-left: 20px; - } - .collection .collection-item.avatar:not(.circle-clipper) > .circle { - position: relative; - margin-bottom: 10px; - } + .collection .collection-item.avatar { + padding-left: 20px; + } + .collection .collection-item.avatar:not(.circle-clipper) > .circle { + position: relative; + margin-bottom: 10px; + } } .collapsible li .show-on-active { - display: none; + display: none; } .collapsible li.active .show-on-active { - display: block; + display: block; } th.chip-height { - height: 67px; - line-height: 2.2; + height: 67px; + line-height: 2.2; } .collection-item.chip-height { - height: 52px; - line-height: 2.2; + height: 52px; + line-height: 2.2; } li.collection-item.button-height { - height: 58px; - line-height: 2.5; + height: 58px; + line-height: 2.5; } diff --git a/aleksis/apps/alsijil/static/css/alsijil/full_register.css b/aleksis/apps/alsijil/static/css/alsijil/full_register.css index 9068a104cb2464a0cb95073e3ee8a66bddfaf21e..533c84326887b2050cb66edadcf92706f64b963d 100644 --- a/aleksis/apps/alsijil/static/css/alsijil/full_register.css +++ b/aleksis/apps/alsijil/static/css/alsijil/full_register.css @@ -1,72 +1,76 @@ -table.small-print, td.small-print, th.small-print { - font-size: 10pt; +table.small-print, +td.small-print, +th.small-print { + font-size: 10pt; } #signatures { - padding-top: 3em; + padding-top: 3em; } #signatures .signature { - display: inline-block; - width: 12em; - border-top: 1px solid; - margin-right: 20px; + display: inline-block; + width: 12em; + border-top: 1px solid; + margin-right: 20px; } tr { - border-bottom: 1px solid rgba(0, 0, 0, 0.3); + border-bottom: 1px solid rgba(0, 0, 0, 0.3); } -td, th { - padding: 1px; +td, +th { + padding: 1px; } tr.lessons-day-first { - border-top: 3px solid rgba(0, 0, 0, 0.3); + border-top: 3px solid rgba(0, 0, 0, 0.3); } -td.rotate, th.rotate { - text-align: center; - transform: rotate(-90deg); +td.rotate, +th.rotate { + text-align: center; + transform: rotate(-90deg); } tr.lesson-cancelled td { - background-color: #EF9A9A; + background-color: #ef9a9a; } tr.lesson-substituted td { - background-color: #ffb74d; + background-color: #ffb74d; } td.lesson-notes { - font-size: 80%; + font-size: 80%; } td.lesson-notes span.lesson-note-absent { - color: #cc0000; + color: #cc0000; } td.lesson-notes span.lesson-note-late { - color: #ff9933; + color: #ff9933; } td.lesson-notes span.lesson-note-excused { - color: #009933; + color: #009933; } table.person-info { - border: none; + border: none; } table.person-info td.person-img { - text-align: center; + text-align: center; } table.person-info td.person-img img { - max-height: 30mm; + max-height: 30mm; } img.max-size-600 { - max-width: 600px; - max-height: 600px; + max-width: 600px; + max-height: 600px; } diff --git a/aleksis/apps/alsijil/static/css/alsijil/lesson.css b/aleksis/apps/alsijil/static/css/alsijil/lesson.css index cb2d9399c9d865c7116c2f8137274edc1ec501d0..48937d33d5a763d9d8ac6c4dbdfa4c9eae573ac3 100644 --- a/aleksis/apps/alsijil/static/css/alsijil/lesson.css +++ b/aleksis/apps/alsijil/static/css/alsijil/lesson.css @@ -1,133 +1,138 @@ .alsijil-check-box { - margin-right: 10px; + margin-right: 10px; } .alsijil-check-box [type="checkbox"] { - padding-left: 30px; + padding-left: 30px; } .alsijil-lesson-cancelled { - text-decoration: line-through; + text-decoration: line-through; } -.alsijil-tardiness-text{ - vertical-align: super; +.alsijil-tardiness-text { + vertical-align: super; } - @media only screen and (max-width: 992px) { - .no-mobile-card { - border: unset; - padding: unset; - margin: unset; - box-shadow: unset; - } - .no-mobile-card .card-content { - padding: unset; - } - table.alsijil-table.horizontal-on-small { - display: block; - max-width: calc(100vw - 40px); - } - table.alsijil-table.horizontal-on-small thead { - display: none; - } - table.alsijil-table.horizontal-on-small tbody { - overflow-x: scroll; - display: flex; - column-gap: 1rem; - flex-wrap: nowrap; - align-items: stretch; - scroll-snap-type: x proximity; - } - - table.alsijil-table.horizontal-on-small tr { - flex-basis: min(75vw, 400px); - flex-shrink: 0; - flex-grow: 1; - border-radius: 8px; - display: flex; - flex-direction: column; - justify-content: space-between; - scroll-snap-align: center; - transition: all .5s; - margin: 0.5rem 0 1rem 0; - background-color: #fff!important; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.12), 0 1px 5px 0 rgba(0, 0, 0, 0.2); - padding: 24px; - } - table.alsijil-table.horizontal-on-small tr:first-of-type { - margin-inline-start: .4rem; - -moz-margin-start: .4rem; - -webkit-margin-start: .4rem; - } - - table.alsijil-table.horizontal-on-small tr:last-of-type { - margin-inline-end: .4rem; - -moz-margin-end: .4rem; - -webkit-margin-end: .4rem; - } - table.alsijil-table.horizontal-on-small td.center-align { - text-align: left; - } - table.alsijil-table.horizontal-on-small .person-name { - font-size: 24px; - font-weight: 300; - display: block; - line-height: 32px; - margin-bottom: 8px; - } -} - -.alsijil-time-head, .alsijil-object-head { + .no-mobile-card { + border: unset; + padding: unset; + margin: unset; + box-shadow: unset; + } + .no-mobile-card .card-content { + padding: unset; + } + table.alsijil-table.horizontal-on-small { + display: block; + max-width: calc(100vw - 40px); + } + table.alsijil-table.horizontal-on-small thead { + display: none; + } + table.alsijil-table.horizontal-on-small tbody { + overflow-x: scroll; + display: flex; + column-gap: 1rem; + flex-wrap: nowrap; + align-items: stretch; + scroll-snap-type: x proximity; + } + + table.alsijil-table.horizontal-on-small tr { + flex-basis: min(75vw, 400px); + flex-shrink: 0; + flex-grow: 1; + border-radius: 8px; + display: flex; + flex-direction: column; + justify-content: space-between; + scroll-snap-align: center; + transition: all 0.5s; + margin: 0.5rem 0 1rem 0; + background-color: #fff !important; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), + 0 3px 1px -2px rgba(0, 0, 0, 0.12), 0 1px 5px 0 rgba(0, 0, 0, 0.2); + padding: 24px; + } + table.alsijil-table.horizontal-on-small tr:first-of-type { + margin-inline-start: 0.4rem; + -moz-margin-start: 0.4rem; + -webkit-margin-start: 0.4rem; + } + + table.alsijil-table.horizontal-on-small tr:last-of-type { + margin-inline-end: 0.4rem; + -moz-margin-end: 0.4rem; + -webkit-margin-end: 0.4rem; + } + table.alsijil-table.horizontal-on-small td.center-align { + text-align: left; + } + table.alsijil-table.horizontal-on-small .person-name { + font-size: 24px; + font-weight: 300; display: block; + line-height: 32px; + margin-bottom: 8px; + } +} + +.alsijil-time-head, +.alsijil-object-head { + display: block; } .alsijil-time-head { - font-size: 2rem; - line-height: 1.1; + font-size: 2rem; + line-height: 1.1; } .alsijil-object-head { - font-size: 3rem; + font-size: 3rem; } @media only screen and (max-width: 600px) { - .alsijil-time-head { - font-size: 1.5rem; - } - - .alsijil-object-head { - font-size: 2.2rem; - line-height: 1.4; - } + .alsijil-time-head { + font-size: 1.5rem; + } + + .alsijil-object-head { + font-size: 2.2rem; + line-height: 1.4; + } } .alsijil-nav { - line-height: 36px; + line-height: 36px; } .alsijil-header-nav-button { - height: 66px; - padding: 0; + height: 66px; + padding: 0; } .alsijil-header-nav-button.left { - margin-right: 5px; + margin-right: 5px; } .alsijil-header-nav-button.right { - margin-left: 5px; + margin-left: 5px; } .alsijil-header-nav-button i.material-icons { - line-height: 60px; - height: 60px; - font-size: 40px; + line-height: 60px; + height: 60px; + font-size: 40px; } .alsijil-nav-header { - width: calc(100% + 40px); - padding: 10px 20px; - margin: -10px -20px 0; -} \ No newline at end of file + width: calc(100% + 40px); + padding: 10px 20px; + margin: -10px -20px 0; +} + +.tabs-icons .tab svg.iconify { + display: block; +} diff --git a/aleksis/apps/alsijil/static/css/alsijil/person.css b/aleksis/apps/alsijil/static/css/alsijil/person.css index b5a59aae95235f696a06d0641215b1392dd5b2a3..d385d7b69e031fafd4c21d037b70b8f973e99b74 100644 --- a/aleksis/apps/alsijil/static/css/alsijil/person.css +++ b/aleksis/apps/alsijil/static/css/alsijil/person.css @@ -1,101 +1,100 @@ span.input-field.inline > .select-wrapper > input { - color: red; - padding: 14px 0 0 0; - line-height: 2px; - height: 36px; - vertical-align: middle; + color: red; + padding: 14px 0 0 0; + line-height: 2px; + height: 36px; + vertical-align: middle; } span.input-field.inline > .select-wrapper .caret { - top: 12px !important; + top: 12px !important; } @media screen and (min-width: 1400px) { - li.collection-item form { - margin: -30px 0 -30px 0; - } + li.collection-item form { + margin: -30px 0 -30px 0; + } - li.collection-item#title #select_all_span { - margin-top: 5px; - } + li.collection-item#title #select_all_span { + margin-top: 5px; + } } .collection { - overflow: visible; - overflow-x: hidden; + overflow: visible; + overflow-x: hidden; } #select_all_container { - display: none; + display: none; } #select_all_box:indeterminate + span:not(.lever):before { - top: -4px; - left: -6px; - width: 10px; - height: 12px; - border-top: none; - border-left: none; - border-right: white 2px solid; - border-bottom: none; - transform: rotate(90deg); - backface-visibility: hidden; - transform-origin: 100% 100%; - + top: -4px; + left: -6px; + width: 10px; + height: 12px; + border-top: none; + border-left: none; + border-right: white 2px solid; + border-bottom: none; + transform: rotate(90deg); + backface-visibility: hidden; + transform-origin: 100% 100%; } #select_all_box:indeterminate + span:not(.lever):after { - top: 0; - width: 20px; - height: 20px; - border: 2px solid currentColor; - background-color: currentColor; - z-index: 0; + top: 0; + width: 20px; + height: 20px; + border: 2px solid currentColor; + background-color: currentColor; + z-index: 0; } #select_all_box_text { - color: #9e9e9e !important; + color: #9e9e9e !important; } td.material-icons { - display: table-cell; + display: table-cell; } .medium-high { - position: relative; - top: 50%; - left: 50%; - transform: translate(-50%, 50%); + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, 50%); } @media screen and (min-width: 600px) { - /* On medium and up devices */ - .medium-high-right { - float: right; - transform: translate(0%, 50%); - } + /* On medium and up devices */ + .medium-high-right { + float: right; + transform: translate(0%, 50%); + } } @media screen and (max-width: 600px) { - /* Only on small devices */ - .full-width-s { - width: 100%; - } - - #heading { - display: block; - } - #heading + a { - float: none!important; - } + /* Only on small devices */ + .full-width-s { + width: 100%; + } + + #heading { + display: block; + } + #heading + a { + float: none !important; + } } .overflow-x-scroll { - overflow-x: scroll; + overflow-x: scroll; } figure.modal-content figcaption { - font-weight: 300; - font-size: 2.28rem; - line-height: 110%; + font-weight: 300; + font-size: 2.28rem; + line-height: 110%; } diff --git a/aleksis/apps/alsijil/static/css/alsijil/week_view.css b/aleksis/apps/alsijil/static/css/alsijil/week_view.css index ccd1595e0fa5ad3f3c10ee8857c644723892e6f2..a42111f55e31d3b3f518873218c76fd246106afe 100644 --- a/aleksis/apps/alsijil/static/css/alsijil/week_view.css +++ b/aleksis/apps/alsijil/static/css/alsijil/week_view.css @@ -1,116 +1,122 @@ @media screen and (max-width: 600px) { - #toggle-row button[type=submit] { - width: 100%; - margin-bottom: 1em; - } + #toggle-row button[type="submit"] { + width: 100%; + margin-bottom: 1em; + } } .horizontal-scroll-container { - overflow-x: scroll; - display: flex; - column-gap: 1rem; - flex-wrap: nowrap; - align-items: stretch; - scroll-snap-type: x proximity; + overflow-x: scroll; + display: flex; + column-gap: 1rem; + flex-wrap: nowrap; + align-items: stretch; + scroll-snap-type: x proximity; } .horizontal-scroll-container.vertical { - flex-wrap: wrap; - overflow-x: inherit; + flex-wrap: wrap; + overflow-x: inherit; } .horizontal-scroll-container.vertical .horizontal-scroll-card { - margin-inline: 0; + margin-inline: 0; } dl { - margin: 0; - padding: 0; + margin: 0; + padding: 0; } dt { - font-weight: bold; + font-weight: bold; } dd { - margin: 0; - padding: unset; + margin: 0; + padding: unset; } .horizontal-scroll-card { - flex-basis: min(75vw, 400px); - flex-shrink: 0; - flex-grow: 1; - border-radius: 8px; - display: flex; - flex-direction: column; - justify-content: space-between; - scroll-snap-align: center; - transition: all .5s; + flex-basis: min(75vw, 400px); + flex-shrink: 0; + flex-grow: 1; + border-radius: 8px; + display: flex; + flex-direction: column; + justify-content: space-between; + scroll-snap-align: center; + transition: all 0.5s; } .horizontal-scroll-card:first-of-type { - margin-inline-start: .4rem; - -moz-margin-start: .4rem; - -webkit-margin-start: .4rem; + margin-inline-start: 0.4rem; + -moz-margin-start: 0.4rem; + -webkit-margin-start: 0.4rem; } .horizontal-scroll-card:last-of-type { - margin-inline-end: .4rem; - -moz-margin-end: .4rem; - -webkit-margin-end: .4rem; + margin-inline-end: 0.4rem; + -moz-margin-end: 0.4rem; + -webkit-margin-end: 0.4rem; } .horizontal-scroll-card .card-action { - margin-bottom: 5px; + margin-bottom: 5px; } .horizontal-scroll-card .card-content .card-title { - display: flex; - justify-content: space-between; + display: flex; + justify-content: space-between; } .horizontal-scroll-card .card-content .card-title .subject { - flex-grow: 5; + flex-grow: 5; } .horizontal-scroll-card .one-line { - display: grid; - grid-auto-flow: column; - grid-template-rows: 1fr 1fr; + display: grid; + grid-auto-flow: column; + grid-template-rows: 1fr 1fr; } p.subtitle { - display: flex; - justify-content: space-between; - align-items: flex-end; + display: flex; + justify-content: space-between; + align-items: flex-end; } .btn-superflat ~ span { - line-height: 24px; + line-height: 24px; } -.btn-superflat, .btn-superflat:focus, .btn-superflat:active { - border: none; - line-height: 1; - height: 24px; - background: none; - font-weight: normal; +.btn-superflat, +.btn-superflat:focus, +.btn-superflat:active { + border: none; + line-height: 1; + height: 24px; + background: none; + font-weight: normal; } .btn-superflat i.material-icons { - vertical-align: middle; + vertical-align: middle; } .btn-superflat:hover { - cursor: pointer; + cursor: pointer; } .unfold-trigger i.material-icons { - transition: transform .5s 0s ease-in-out; - transform: rotate(-90deg); + transition: transform 0.5s 0s ease-in-out; + transform: rotate(-90deg); } .unfold-trigger.vertical i.material-icons { - transform: rotate(-180deg); + transform: rotate(-180deg); +} + +.tabs-icons .tab svg.iconify { + display: block; } diff --git a/aleksis/apps/alsijil/static/js/alsijil/week_view.js b/aleksis/apps/alsijil/static/js/alsijil/week_view.js index c5daa0451b63a63aac4e8b7703beaaa6d8b26fe5..69124b9c41e656948bbced89b06b8d4edaf3b3c2 100644 --- a/aleksis/apps/alsijil/static/js/alsijil/week_view.js +++ b/aleksis/apps/alsijil/static/js/alsijil/week_view.js @@ -1,21 +1,20 @@ $(document).ready(function () { - $("#id_group").change(function () { - $("#id_teacher").val("").formSelect(); - }); - $("#id_teacher").change(function () { - $("#id_group").val("").formSelect(); - }); - $("#toggle-row.pre-hidden").hide(); - + $("#id_group").change(function () { + $("#id_teacher").val("").formSelect(); + }); + $("#id_teacher").change(function () { + $("#id_group").val("").formSelect(); + }); + $("#toggle-row.pre-hidden").hide(); }); $("#toggle-button").click(function () { - $("#toggle-row").toggle(); -}) + $("#toggle-row").toggle(); +}); $(".unfold-trigger").click(function (event) { - let target = event.target; - target.classList.toggle("vertical"); - let next_container = $(target).parent().next(".horizontal-scroll-container"); - if (next_container.length >= 1) { - next_container[0].classList.toggle("vertical"); - } -}) \ No newline at end of file + let target = event.target; + target.classList.toggle("vertical"); + let next_container = $(target).parent().next(".horizontal-scroll-container"); + if (next_container.length >= 1) { + next_container[0].classList.toggle("vertical"); + } +}); diff --git a/aleksis/apps/alsijil/tables.py b/aleksis/apps/alsijil/tables.py index c82385265b37d6b37f365048b2ad8c4b6ae52cf1..a0ef1734e5eea326fd9a8fd0f858b71a89089082 100644 --- a/aleksis/apps/alsijil/tables.py +++ b/aleksis/apps/alsijil/tables.py @@ -115,7 +115,7 @@ class PersonalNoteTable(tables.Table): ) subject = tables.Column(verbose_name=_("Subject"), accessor=A("subject"), linkify=True) absent = tables.Column(verbose_name=_("Absent")) - late = tables.Column(verbose_name=_("Tardiness")) + tardiness = tables.Column(verbose_name=_("Tardiness")) excused = tables.Column(verbose_name=_("Excuse")) extra_marks = tables.Column(verbose_name=_("Extra marks"), accessor=A("extra_marks__all")) @@ -151,9 +151,9 @@ class PersonalNoteTable(tables.Table): return badge return "–" - def render_late(self, value): + def render_tardiness(self, value): if value: - content = _(f"{value}' late") + content = _(f"{value}' tardiness") context = dict(content=content, classes="orange white-text") return render_to_string("components/materialize-chips.html", context) else: diff --git a/aleksis/apps/alsijil/tasks.py b/aleksis/apps/alsijil/tasks.py index aa8de7b5a075b9a1ae37b129b1870482966577a5..7eaf8c01a5186ef08c12a5e4b19e9fc1e853d4d2 100644 --- a/aleksis/apps/alsijil/tasks.py +++ b/aleksis/apps/alsijil/tasks.py @@ -87,6 +87,11 @@ def generate_full_register_printout(group: int, file_object: int, recorder: Prog day = event.date_start + timedelta(days=i) event_copy = deepcopy(event) event_copy.annotate_day(day) + + # Skip event days if it isn't inside the timetable schema + if not (event_copy.raw_period_from_on_day and event_copy.raw_period_to_on_day): + continue + register_objects_by_day.setdefault(day, []).append( ( event_copy, diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html b/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html index fd376568067136e72107ec5f9363b4dc68ab4040..056bea93ca036f54869ad037f84b7575f3a2123d 100644 --- a/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html +++ b/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html @@ -3,7 +3,6 @@ {% load week_helpers material_form_internal material_form i18n static rules time_helpers %} {% block browser_title %}{% blocktrans %}Lesson{% endblocktrans %}{% endblock %} -{% block no_page_title %}{% endblock %} {% block extra_head %} {{ block.super }} <link rel="stylesheet" href="{% static 'css/alsijil/lesson.css' %}"/> @@ -13,48 +12,8 @@ {% endif %} {% endblock %} -{% block nav_content %} - <ul class="tabs tabs-transparent tabs-icons tabs-fixed-width"> - <li class="tab"> - <a href="#lesson-documentation"> - <i class="material-icons iconify" data-icon="mdi:message-bulleted"></i> - {% trans "Period" %} - </a> - </li> - {% if register_object.label_ != "lesson_period" or not register_object.get_substitution.cancelled or not request.site.preferences.alsijil__block_personal_notes_for_cancelled %} - <li class="tab"> - <a href="#personal-notes"> - <i class="material-icons iconify" data-icon="mdi:account-multiple-outline"></i> - {% trans "Persons" %} - </a> - </li> - {% endif %} - {% if with_seating_plan %} - <li class="tab"> - <a href="#seating-plan"> - <i class="material-icons iconify" data-icon="mdi:seat-outline"></i> - {% trans "Seating plan" %} - </a> - </li> - {% endif %} - {% if prev_lesson %} - {% has_perm "alsijil.view_lessondocumentation_rule" user prev_lesson as can_view_prev_lesson_documentation %} - {% if prev_lesson.get_lesson_documentation and can_view_prev_lesson_documentation %} - <li class="tab"> - <a href="#previous-lesson"> - <i class="material-icons iconify" data-icon="mdi:history"></i> - {% trans "Previous" %} - </a> - </li> - {% endif %} - {% endif %} - <li class="tab"> - <a href="#more"> - <i class="material-icons iconify" data-icon="mdi:dots-horizontal"></i> - {% trans "More" %} - </a> - </li> - </ul> +{% block page_title %} + {% include "alsijil/partials/lesson/heading.html" %} {% endblock %} {% block content %} @@ -62,54 +21,50 @@ {% has_perm "alsijil.edit_lessondocumentation_rule" user register_object as can_edit_lesson_documentation %} {% has_perm "alsijil.edit_register_object_personalnote_rule" user register_object as can_edit_register_object_personalnote %} - {% if next_lesson_person or prev_lesson_person or back_to_week_url %} - <div class="row margin-bottom z-depth-1 alsijil-nav-header"> - <div class="col s12 no-padding"> - {# Back to week view #} - {% if back_to_week_url %} - <a href="{{ back_to_week_url }}" - class="btn secondary-color waves-light waves-effect margin-bottom {% if prev_lesson_person or next_lesson_person %}hide-on-extra-large-only{% endif %}"> - <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> {% trans "Week view" %} + <!-- Tab Buttons --> + <div class="col s12 margin-bottom"> + <ul class="tabs tabs-icons tabs-fixed-width"> + <li class="tab col"> + <a href="#lesson-documentation"> + <i class="material-icons iconify" data-icon="mdi:message-bulleted"></i> + {% trans "Period" %} + </a> + </li> + {% if register_object.label_ != "lesson_period" or not register_object.get_substitution.cancelled or not request.site.preferences.alsijil__block_personal_notes_for_cancelled %} + <li class="tab col"> + <a href="#personal-notes"> + <i class="material-icons iconify" data-icon="mdi:account-multiple-outline"></i> + {% trans "Persons" %} </a> - {% endif %} - - {% if prev_lesson_person or next_lesson_person %} - <div class="col s12 no-padding center alsijil-nav"> - {% if back_to_week_url %} - <a href="{{ back_to_week_url }}" - class="btn-flat secondary-color-text waves-light waves-effect left hide-on-med-and-down hide-on-large-only show-on-extra-large"> - <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> {% trans "Week view" %} - </a> - {% endif %} - - {# Previous lesson #} - <a class="btn-flat waves-effect waves-light left primary-color-text {% if not prev_lesson_person %}disabled{% endif %}" - title="{% trans "My previous lesson" %}" - {% if prev_lesson_person %} - href="{% url "lesson_period" prev_lesson_person.week.year prev_lesson_person.week.week prev_lesson_person.id %}" - {% endif %} - > - <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> - <span class="hide-on-small-only">{% trans "My previous lesson" %}</span> - <span class="hide-on-med-and-up">{% trans "Previous" %}</span> - </a> - {# Next lesson #} - <a class="btn-flat waves-effect waves-light right primary-color-text {% if not next_lesson_person %}disabled{% endif %}" - title="{% trans "My next lesson" %}" - {% if next_lesson_person %} - href="{% url "lesson_period" next_lesson_person.week.year next_lesson_person.week.week next_lesson_person.id %}" - {% endif %} - > - <i class="material-icons iconify right" data-icon="mdi:chevron-right"></i> - <span class="hide-on-small-only">{% trans "My next lesson" %}</span> - <span class="hide-on-med-and-up">{% trans "Next" %}</span> + </li> + {% endif %} + {% if with_seating_plan %} + <li class="tab col"> + <a href="#seating-plan"> + <i class="material-icons iconify" data-icon="mdi:seat-outline"></i> + {% trans "Seating plan" %} + </a> + </li> + {% endif %} + {% if prev_lesson %} + {% has_perm "alsijil.view_lessondocumentation_rule" user prev_lesson as can_view_prev_lesson_documentation %} + {% if prev_lesson.get_lesson_documentation and can_view_prev_lesson_documentation %} + <li class="tab col"> + <a href="#previous-lesson"> + <i class="material-icons iconify" data-icon="mdi:history"></i> + {% trans "Previous" %} </a> - <span class="truncate">{{ request.user.person }}</span> - </div> + </li> {% endif %} - </div> - </div> - {% endif %} + {% endif %} + <li class="tab col"> + <a href="#more"> + <i class="material-icons iconify" data-icon="mdi:dots-horizontal"></i> + {% trans "More" %} + </a> + </li> + </ul> + </div> <form method="post" class="row"> {% csrf_token %} @@ -148,8 +103,6 @@ </div> </div> {% else %} - {% include "alsijil/partials/lesson/heading.html" %} - <div class="row no-margin"> <div class="container"> <div class="card"> diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html b/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html index ce38ee671b6e51a9f4417cb8507dc530b2b81a48..d8c8273092fee51086fac6b24599da95fef486d0 100644 --- a/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html +++ b/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html @@ -11,35 +11,6 @@ <link rel="stylesheet" href="{% static 'css/alsijil/week_view.css' %}"/> {% endblock %} -{% block nav_content %} - {% if lesson_periods %} - <div class=""> - <ul class="tabs tabs-transparent tabs-icons tabs-fixed-width"> - <li class="tab col"> - <a class="active" href="#week-overview"> - <i class="material-icons iconify" data-icon="mdi:message-bulleted"></i> - {% trans "Lesson documentations" %} - </a> - </li> - <li class="tab col"> - <a href="#personal-notes"> - <i class="material-icons iconify" data-icon="mdi:account-multiple-outline"></i> - {% trans "Persons" %} - </a> - </li> - {% if group_roles %} - <li class="tab col"> - <a href="#group-roles"> - <i class="material-icons iconify" data-icon="mdi:clipboard-account-outline"></i> - {% trans "Group roles" %} - </a> - </li> - {% endif %} - </ul> - </div> - {% endif %} -{% endblock %} - {% block content %} <script type="text/javascript" src="{% static "js/helper.js" %}"></script> {{ week_select|json_script:"week_select" }} @@ -97,6 +68,33 @@ </p> {% endif %} + {% if lesson_periods %} + <div class="col s12 margin-bottom"> + <ul class="tabs tabs-icons tabs-fixed-width"> + <li class="tab col"> + <a class="active" href="#week-overview"> + <i class="material-icons iconify" data-icon="mdi:message-bulleted"></i> + {% trans "Lesson documentations" %} + </a> + </li> + <li class="tab col"> + <a href="#personal-notes"> + <i class="material-icons iconify" data-icon="mdi:account-multiple-outline"></i> + {% trans "Persons" %} + </a> + </li> + {% if group_roles %} + <li class="tab col"> + <a href="#group-roles"> + <i class="material-icons iconify" data-icon="mdi:clipboard-account-outline"></i> + {% trans "Group roles" %} + </a> + </li> + {% endif %} + </ul> + </div> + {% endif %} + {% if lesson_periods %} <div class="row"> <div class="col s12" id="week-overview"> diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/heading.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/heading.html index 4b768265a510df03c6fab59b40cb4e356f90290a..132e97f05acd0216d59f89a12cab08b96e123cc5 100644 --- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/heading.html +++ b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/heading.html @@ -1,5 +1,54 @@ {% load i18n %} +{% if next_lesson_person or prev_lesson_person or back_to_week_url %} + <div class="row margin-bottom alsijil-nav-header"> + <div class="col s12 no-padding"> + {# Back to week view #} + {% if back_to_week_url %} + <a href="{{ back_to_week_url }}" + class="btn secondary-color waves-light waves-effect margin-bottom {% if prev_lesson_person or next_lesson_person %}hide-on-extra-large-only{% endif %}"> + <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> {% trans "Week view" %} + </a> + {% endif %} + + {% if prev_lesson_person or next_lesson_person %} + <div class="col s12 no-padding center alsijil-nav"> + {% if back_to_week_url %} + <a href="{{ back_to_week_url }}" + class="btn-flat secondary-color-text waves-light waves-effect left hide-on-med-and-down hide-on-large-only show-on-extra-large"> + <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> {% trans "Week view" %} + </a> + {% endif %} + + {# Previous lesson #} + <a class="btn-flat waves-effect waves-light left primary-color-text {% if not prev_lesson_person %}disabled{% endif %}" + title="{% trans "My previous lesson" %}" + {% if prev_lesson_person %} + href="{% url "lesson_period" prev_lesson_person.week.year prev_lesson_person.week.week prev_lesson_person.id %}" + {% endif %} + > + <i class="material-icons iconify left" data-icon="mdi:chevron-left"></i> + <span class="hide-on-small-only">{% trans "My previous lesson" %}</span> + <span class="hide-on-med-and-up">{% trans "Previous" %}</span> + </a> + {# Next lesson #} + <a class="btn-flat waves-effect waves-light right primary-color-text {% if not next_lesson_person %}disabled{% endif %}" + title="{% trans "My next lesson" %}" + {% if next_lesson_person %} + href="{% url "lesson_period" next_lesson_person.week.year next_lesson_person.week.week next_lesson_person.id %}" + {% endif %} + > + <i class="material-icons iconify right" data-icon="mdi:chevron-right"></i> + <span class="hide-on-small-only">{% trans "My next lesson" %}</span> + <span class="hide-on-med-and-up">{% trans "Next" %}</span> + </a> + <span class="truncate">{{ request.user.person }}</span> + </div> + {% endif %} + </div> + </div> +{% endif %} + <h1> <span class="right hide-on-small-only"> {% include "alsijil/partials/lesson_status.html" with register_object=register_object css_class="medium" %} diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html index 7a67a23ed9a2c3a2f08d299f344b583fc13ffe24..98b18fe153a8bb8f292ebe6ea20286df357680f0 100644 --- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html +++ b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/documentation.html @@ -1,6 +1,5 @@ {% load i18n material_form_internal material_form data_helpers %} -{% include "alsijil/partials/lesson/heading.html" %} {% include "alsijil/partials/lesson/prev_next.html" with with_save=0 %} <div class="hide-on-med-and-up margin-bottom"> diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/more.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/more.html index b7e010125077dd003e529bb85432dfc64adad7ab..ffc7706488757871e43de4e71da9e73802273a3f 100644 --- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/more.html +++ b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/more.html @@ -1,7 +1,5 @@ {% load i18n %} -{% include "alsijil/partials/lesson/heading.html" %} - {% if group_roles %} {% include "alsijil/group_role/partials/assigned_roles.html" with roles=group_roles group=register_object.get_groups.first back_url=back_url %} {% endif %} diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html index c1a1e0030ddf7b28a77362a3dbe3637ddb879357..1b013252a8f316979cdf5bdcc87ef1c7463b6e36 100644 --- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html +++ b/aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html @@ -1,6 +1,5 @@ {% load i18n material_form_internal material_form time_helpers %} -{% include "alsijil/partials/lesson/heading.html" %} {% include "alsijil/partials/lesson/prev_next.html" with with_save=1 %} {% if not blocked_because_holidays %} @@ -54,7 +53,7 @@ </td> <td> <div class="input-field"> - {{ form.late }} + {{ form.tardiness }} <label for="{{ form.absent.id_for_label }}"> {% trans "Tardiness (in m)" %} </label> @@ -109,9 +108,9 @@ <i class="material-icons iconify center" data-icon="mdi:{{ form.absent.value|yesno:"check,close" }}"></i> </td> <td> - <i class="material-icons iconify center" data-icon="mdi:{{ form.late.value|yesno:"check,close" }}"></i> + <i class="material-icons iconify center" data-icon="mdi:{{ form.tardiness.value|yesno:"check,close" }}"></i> <span class="alsijil-tardiness-text"> - {% if form.late.value %}{{ form.late.value|to_time|time:"i\m" }}{% endif %} + {% if form.tardiness.value %}{{ form.tardiness.value|to_time|time:"i\m" }}{% endif %} </span> </td> <td> diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html b/aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html index bb60dbbd0984d1dab0130ada052cb423288aee2e..69ce9c6c9a011744eb2b3bed80228fe3147ff01b 100644 --- a/aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html +++ b/aleksis/apps/alsijil/templates/alsijil/partials/persons_with_stats.html @@ -12,11 +12,13 @@ <th rowspan="2">{% trans "Name" %}</th> <th rowspan="2">{% trans "Primary group" %}</th> <th colspan="{{ excuse_types.count|add:4 }}">{% trans "Absences" %}</th> - <th colspan="{{ excuse_types_not_absent.count }}">{% trans "Uncounted Absences" %}</th> - <th rowspan="2">{% trans "Tardiness" %}</th> + {% if excuse_types_not_absent %} + <th colspan="{{ excuse_types_not_absent.count }}">{% trans "Uncounted Absences" %}</th> + {% endif %} {% if extra_marks %} <th colspan="{{ extra_marks.count }}">{% trans "Extra marks" %}</th> {% endif %} + <th rowspan="2">{% trans "Tardiness" %}</th> <th rowspan="2"></th> </tr> <tr class="hide-on-large-only"> @@ -36,12 +38,12 @@ ({{ excuse_type.short_name }}) </th> {% endfor %} - <th class="truncate chip-height">{% trans "Tardiness" %}</th> {% for extra_mark in extra_marks %} <th class="chip-height"> {{ extra_mark.short_name }} </th> {% endfor %} + <th class="truncate chip-height">{% trans "Tardiness" %}</th> <th rowspan="2"></th> </tr> <tr class="hide-on-med-and-down"> @@ -110,12 +112,6 @@ </span> </td> {% endfor %} - <td> - <span class="chip orange white-text" title="{% trans "Tardiness" %}"> - {% firstof person.tardiness|to_time|time:"H\h i\m" "–" %} - </span> - <span class="chip orange white-text" title="{% trans "Count of tardiness" %}">{{ person.tardiness_count }} ×</span> - </td> {% for extra_mark in extra_marks %} <td> <span class="chip grey white-text" title="{{ extra_mark.name }}"> @@ -123,6 +119,12 @@ </span> </td> {% endfor %} + <td> + <span class="chip orange white-text" title="{% trans "Tardiness" %}"> + {% firstof person.tardiness|to_time|time:"H\h i\m" "–" %} + </span> + <span class="chip orange white-text" title="{% trans "Count of tardiness" %}">{{ person.tardiness_count }} ×</span> + </td> <td> <a class="btn primary waves-effect waves-light" href="{% url "overview_person" person.pk %}"> diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/tardinesses.html b/aleksis/apps/alsijil/templates/alsijil/partials/tardinesses.html index d11a94ea16f4ebd23a41dce65aca7980e1893172..ca20a9c4d1f420736febd149e657a67e855d45f4 100644 --- a/aleksis/apps/alsijil/templates/alsijil/partials/tardinesses.html +++ b/aleksis/apps/alsijil/templates/alsijil/partials/tardinesses.html @@ -2,6 +2,6 @@ {% for note in notes %} {% has_perm "alsijil.view_personalnote_rule" user note as can_view_personalnote %} {% if can_view_personalnote %} - <span>{{ note.person }} ({{ note.late }}'){% if not forloop.last %},{% endif %}</span> + <span>{{ note.person }} ({{ note.tardiness }}'){% if not forloop.last %},{% endif %}</span> {% endif %} {% endfor %} diff --git a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html index c8d71ff3f6b2162814f72a83cfbc14fa8c6f0b1b..c699287a00987ad99c1fd0c2d4617827670fae2f 100644 --- a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html +++ b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html @@ -357,7 +357,7 @@ <tbody> {% for note in person.filtered_notes %} - {% if note.absent or note.late or note.remarks or note.extra_marks.all %} + {% if note.absent or note.tardiness or note.remarks or note.extra_marks.all %} <tr> {% if note.date %} <td>{{ note.date }}</td> @@ -389,8 +389,8 @@ {% endif %} </td> <td> - {% if note.late %} - {{ note.late }}' + {% if note.tardiness %} + {{ note.tardiness }}' {% endif %} </td> <td> @@ -486,10 +486,10 @@ {% endif %} </span> {% endif %} - {% if note.late %} + {% if note.tardiness %} <span class="lesson-note-late"> {{ note.person.last_name }}, {{ note.person.first_name|slice:"0:1" }}. - ({{ note.late }}′) + ({{ note.tardiness }}′) {% if note.excused %} <span class="lesson-note-excused"> {% if note.excuse_type %} diff --git a/aleksis/apps/alsijil/tests/test_actions.py b/aleksis/apps/alsijil/tests/test_actions.py index 6f1bd6ef4ce3900ddc6ea7dac5df6f3a25dd90fc..8dd499ac953583637a3ade36c835aa0e5d6aae24 100644 --- a/aleksis/apps/alsijil/tests/test_actions.py +++ b/aleksis/apps/alsijil/tests/test_actions.py @@ -52,7 +52,7 @@ def _prepare_notes(): excused=True, excuse_type=excuse_type, ), - PersonalNote(person=person, event=_generate_event(date(2021, 10, 4)), late=10), + PersonalNote(person=person, event=_generate_event(date(2021, 10, 4)), tardiness=10), PersonalNote( person=person, event=_generate_event(date(2032, 10, 11)), remarks="Good work!" ), diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 9a333b3c1e2af83677f7edff6dd53959c566e49b..0208255e5b05f2c56cb1cd7e17cda5e4921545aa 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -15,7 +15,7 @@ from django.urls import reverse, reverse_lazy from django.utils import timezone from django.utils.decorators import method_decorator from django.utils.http import url_has_allowed_host_and_scheme -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from django.views import View from django.views.decorators.cache import never_cache from django.views.generic import DetailView @@ -33,6 +33,7 @@ from aleksis.apps.chronos.managers import TimetableType from aleksis.apps.chronos.models import Event, ExtraLesson, Holiday, LessonPeriod, TimePeriod from aleksis.apps.chronos.util.build import build_weekdays from aleksis.apps.chronos.util.date import get_weeks_for_year, week_weekday_to_date +from aleksis.core.decorators import pwa_cache from aleksis.core.mixins import ( AdvancedCreateView, AdvancedDeleteView, @@ -42,7 +43,7 @@ from aleksis.core.mixins import ( from aleksis.core.models import Group, PDFFile, Person, SchoolTerm from aleksis.core.util import messages from aleksis.core.util.celery_progress import render_progress_page -from aleksis.core.util.core_helpers import get_site_preferences, objectgetter_optional +from aleksis.core.util.core_helpers import get_site_preferences, has_person, objectgetter_optional from aleksis.core.util.predicates import check_global_permission from .filters import InstructionFilter, PersonalNoteFilter @@ -88,6 +89,7 @@ from .util.alsijil_helpers import ( ) +@pwa_cache @permission_required("alsijil.view_register_object_rule", fn=get_register_object_by_pk) # FIXME def register_object( request: HttpRequest, @@ -248,8 +250,7 @@ def register_object( "alsijil.view_register_object_personalnote_rule", register_object ): persons = Person.objects.filter( - Q(pk=request.user.person.pk) - | Q(person__member_of__in=request.user.person.owner_of.all()) + Q(pk=request.user.person.pk) | Q(member_of__in=request.user.person.owner_of.all()) ).distinct() else: persons = Person.objects.all() @@ -344,6 +345,7 @@ def register_object( return render(request, "alsijil/class_register/lesson.html", context) +@pwa_cache @permission_required("alsijil.view_week_rule", fn=get_timetable_instance_by_pk) def week_view( request: HttpRequest, @@ -554,10 +556,10 @@ def week_view( filtered_personal_notes__absent=True, filtered_personal_notes__excused=False ), ), - tardiness_sum=Sum("filtered_personal_notes__late"), + tardiness_sum=Sum("filtered_personal_notes__tardiness"), tardiness_count=Count( "filtered_personal_notes", - filter=Q(filtered_personal_notes__late__gt=0), + filter=Q(filtered_personal_notes__tardiness__gt=0), ), ) @@ -649,6 +651,7 @@ def week_view( return render(request, "alsijil/class_register/week_view.html", context) +@pwa_cache @permission_required( "alsijil.view_full_register_rule", fn=objectgetter_optional(Group, None, False) ) @@ -656,8 +659,11 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: group = get_object_or_404(Group, pk=id_) file_object = PDFFile.objects.create() + if has_person(request): + file_object.person = request.user.person + file_object.save() - redirect_url = reverse("redirect_to_pdf_file", args=[file_object.pk]) + redirect_url = f"/pdfs/{file_object.pk}" result = generate_full_register_printout.delay(group.pk, file_object.pk) @@ -685,6 +691,7 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: ) +@pwa_cache @permission_required("alsijil.view_my_students_rule") def my_students(request: HttpRequest) -> HttpResponse: context = {} @@ -725,6 +732,7 @@ def my_students(request: HttpRequest) -> HttpResponse: return render(request, "alsijil/class_register/persons.html", context) +@pwa_cache @permission_required( "alsijil.view_my_groups_rule", ) @@ -736,6 +744,7 @@ def my_groups(request: HttpRequest) -> HttpResponse: return render(request, "alsijil/class_register/groups.html", context) +@method_decorator(pwa_cache, "dispatch") class StudentsList(PermissionRequiredMixin, DetailView): model = Group template_name = "alsijil/class_register/students_list.html" @@ -755,6 +764,7 @@ class StudentsList(PermissionRequiredMixin, DetailView): return context +@pwa_cache @permission_required( "alsijil.view_person_overview_rule", fn=objectgetter_optional( @@ -802,7 +812,7 @@ def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResp personal_notes = ( allowed_personal_notes.not_empty() - .filter(Q(absent=True) | Q(late__gt=0) | ~Q(remarks="") | Q(extra_marks__isnull=False)) + .filter(Q(absent=True) | Q(tardiness__gt=0) | ~Q(remarks="") | Q(extra_marks__isnull=False)) .annotate( school_term_start=Case( When(event__isnull=False, then="event__school_term__date_start"), @@ -922,8 +932,10 @@ def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResp unexcused=Count("absent") ) ) - stat.update(personal_notes.aggregate(tardiness=Sum("late"))) - stat.update(personal_notes.filter(~Q(late=0)).aggregate(tardiness_count=Count("late"))) + stat.update(personal_notes.aggregate(tardiness=Sum("tardiness"))) + stat.update( + personal_notes.filter(~Q(tardiness=0)).aggregate(tardiness_count=Count("tardiness")) + ) for extra_mark in extra_marks: stat.update( @@ -1064,6 +1076,7 @@ class DeletePersonalNoteView(PermissionRequiredMixin, DetailView): return redirect("overview_person", note.person.pk) +@method_decorator(pwa_cache, "dispatch") class ExtraMarkListView(PermissionRequiredMixin, SingleTableView): """Table of all extra marks.""" @@ -1108,6 +1121,7 @@ class ExtraMarkDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDelete success_message = _("The extra mark has been deleted.") +@method_decorator(pwa_cache, "dispatch") class ExcuseTypeListView(PermissionRequiredMixin, SingleTableView): """Table of all excuse types.""" @@ -1152,6 +1166,7 @@ class ExcuseTypeDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDelet success_message = _("The excuse type has been deleted.") +@method_decorator(pwa_cache, "dispatch") class GroupRoleListView(PermissionRequiredMixin, SingleTableView): """Table of all group roles.""" @@ -1196,6 +1211,7 @@ class GroupRoleDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDelete success_message = _("The group role has been deleted.") +@method_decorator(pwa_cache, "dispatch") class AssignedGroupRolesView(PermissionRequiredMixin, DetailView): permission_required = "alsijil.view_assigned_grouproles_rule" model = Group @@ -1318,6 +1334,7 @@ class GroupRoleAssignmentDeleteView( return reverse("assigned_group_roles", args=[pk]) +@method_decorator(pwa_cache, "dispatch") class AllRegisterObjectsView(PermissionRequiredMixin, View): """Provide overview of all register objects for coordinators.""" diff --git a/docs/conf.py b/docs/conf.py index 59ca213449c1f46b86ef5340aaac7c21447cb4e8..b37b0f814f16734100037dfe13bc1d8a4e007456 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,9 +29,9 @@ copyright = "2019-2022 The AlekSIS team" author = "The AlekSIS Team" # The short X.Y version -version = "2.1" +version = "3.0" # The full version, including alpha/beta/rc tags -release = "2.2.dev0" +release = "3.0.1.dev0" # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index a3a4aea8d9b8f3cfd286cab366900bcce9ee5b9c..759cac17c8e175968e8fa17228d9204188321da6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "AlekSIS-App-Alsijil" -version = "2.2.dev0" +version = "3.0.1.dev0" packages = [ { include = "aleksis" } ] @@ -24,36 +24,33 @@ authors = [ "mirabilos <thorsten.glaser@teckids.org>", "Tom Teichler <tom.teichler@teckids.org>" ] -maintainers = [ - "Dominik George <dominik.george@teckids.org>", - "Jonathan Weth <dev@jonathanweth.de>", -] -license = "EUPL-1.2" -homepage = "https://aleksis.edugit.io/" -repository = "https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil" +maintainers = ["Jonathan Weth <dev@jonathanweth.de>", "Dominik George <dominik.george@teckids.org>"] +license = "EUPL-1.2-or-later" +homepage = "https://aleksis.org" +repository = "https://edugit.org/AlekSIS/official/AlekSIS-App-Alsijil" documentation = "https://aleksis.edugit.io/AlekSIS/docs/html/" classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", - "Framework :: Django :: 3.0", + "Framework :: Django :: 4.0", "Intended Audience :: Education", "Topic :: Education", "Typing :: Typed", ] +[[tool.poetry.source]] +name = "PyPI" +priority = "primary" + [[tool.poetry.source]] name = "gitlab" url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple" -secondary = true - +priority = "supplemental" [tool.poetry.dependencies] python = "^3.9" -aleksis-core = "^2.12" -aleksis-app-chronos = "^2.2" -aleksis-app-stoelindeling = { version = "^1.0", optional = true } - -[tool.poetry.dev-dependencies] -aleksis-builddeps = "*" +aleksis-core = "^3.0" +aleksis-app-chronos = "^3.0" +aleksis-app-stoelindeling = { version = "^2.0", optional = true } [tool.poetry.extras] seatingplans = ["aleksis-app-stoelindeling"] @@ -61,10 +58,52 @@ seatingplans = ["aleksis-app-stoelindeling"] [tool.poetry.plugins."aleksis.app"] alsijil = "aleksis.apps.alsijil.apps:AlsijilConfig" + +[tool.poetry.group.dev.dependencies] +django-stubs = "^4.2" + +safety = "^2.3.5" + +flake8 = "^6.0.0" +flake8-django = "~1.2.0" +flake8-fixme = "^1.1.1" +flake8-mypy = "^17.8.0" +flake8-bandit = "^4.1.1" +flake8-builtins = "^2.0.0" +flake8-docstrings = "^1.5.0" +flake8-rst-docstrings = "^0.3.0" + +black = ">=21.0" +flake8-black = "^0.3.0" + +isort = "^5.0.0" +flake8-isort = "^6.0.0" + +curlylint = "^0.13.0" + +[tool.poetry.group.test.dependencies] +pytest = "^7.2" +pytest-django = "^4.1" +pytest-django-testing-postgresql = "^0.2" +pytest-cov = "^4.0.0" +pytest-sugar = "^0.9.2" +selenium = "<4.10.0" +freezegun = "^1.1.0" + +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] +sphinx = "^7.0" +sphinxcontrib-django = "^2.3.0" +sphinxcontrib-svg2pdfconverter = "^1.1.1" +sphinx-autodoc-typehints = "^1.7" +sphinx_material = "^0.0.35" + [tool.black] line-length = 100 exclude = "/migrations/" [build-system] -requires = ["poetry>=1.0"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tox.ini b/tox.ini index 78e09567e193b72a299dfb3f776499420ecc04ec..66c9e773824ab52d5c0e5574016b83696d5736dc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,16 +1,15 @@ [tox] skipsdist = True skip_missing_interpreters = true -envlist = py37,py38,py39 +envlist = py39,py310,py311 [testenv] -whitelist_externals = poetry - sudo +allowlist_externals = poetry skip_install = true envdir = {toxworkdir}/globalenv commands_pre = poetry install - poetry run aleksis-admin webpack_bundle + poetry run aleksis-admin vite build poetry run aleksis-admin collectstatic --no-input commands = poetry run pytest --cov=. {posargs} aleksis/ @@ -27,6 +26,8 @@ commands = poetry run black --check --diff aleksis/ poetry run isort -c --diff --stdout aleksis/ poetry run flake8 {posargs} aleksis/ + poetry run sh -c "aleksis-admin yarn run prettier --check --ignore-path={toxinidir}/.prettierignore {toxinidir}" + poetry run sh -c "aleksis-admin yarn run eslint {toxinidir}/aleksis/**/*/frontend/**/*.{js,vue} --config={toxinidir}/.eslintrc.js --resolve-plugins-relative-to=." [testenv:security] commands = @@ -46,6 +47,7 @@ commands = poetry run make -C docs/ html {posargs} commands = poetry run isort aleksis/ poetry run black aleksis/ + poetry run sh -c "aleksis-admin yarn run prettier --write --ignore-path={toxinidir}/.prettierignore {toxinidir}" [testenv:makemessages] commands =