Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • AlekSIS/official/AlekSIS-App-Alsijil
  • sunweaver/AlekSIS-App-Alsijil
  • 8tincsoVluke/AlekSIS-App-Alsijil
  • perfreicpo/AlekSIS-App-Alsijil
  • noifobarep/AlekSIS-App-Alsijil
  • 7ingannisdo/AlekSIS-App-Alsijil
  • unmruntartpa/AlekSIS-App-Alsijil
  • balrorebta/AlekSIS-App-Alsijil
  • comliFdifwa/AlekSIS-App-Alsijil
  • 3ranaadza/AlekSIS-App-Alsijil
10 results
Show changes
Commits on Source (25)
Showing
with 162 additions and 74 deletions
...@@ -9,6 +9,19 @@ and this project adheres to `Semantic Versioning`_. ...@@ -9,6 +9,19 @@ and this project adheres to `Semantic Versioning`_.
Unreleased Unreleased
---------- ----------
`2.1.1`_ - 2022-09-01
---------------------
Fixed
~~~~~
* Register absence form wasn't accessible without direct access to class register.
* Printing the full group register failed when a person had no personal notes.
* Data checks reported all Lesson Documentations as being during Holidays if there was no Holiday object.
* Students were displayed multiple times in class register views.
* Absences were counted multiple times in some class register views.
* Group owners couldn't create new seating plans.
`2.1`_ - 2022-06-25 `2.1`_ - 2022-06-25
------------------- -------------------
...@@ -261,3 +274,4 @@ Fixed ...@@ -261,3 +274,4 @@ Fixed
.. _2.0: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/2.0 .. _2.0: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/2.0
.. _2.0.1: https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil/-/tags/2.0.1 .. _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: 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
...@@ -113,7 +113,7 @@ class LessonDocumentationOnHolidaysDataCheck(DataCheck): ...@@ -113,7 +113,7 @@ class LessonDocumentationOnHolidaysDataCheck(DataCheck):
documentations = LessonDocumentation.objects.not_empty().annotate_date_range() documentations = LessonDocumentation.objects.not_empty().annotate_date_range()
q = Q() q = Q(pk__in=[])
for holiday in holidays: for holiday in holidays:
q = q | Q(day_end__gte=holiday.date_start, day_start__lte=holiday.date_end) q = q | Q(day_end__gte=holiday.date_start, day_start__lte=holiday.date_end)
documentations = documentations.filter(q) documentations = documentations.filter(q)
...@@ -147,7 +147,7 @@ class PersonalNoteOnHolidaysDataCheck(DataCheck): ...@@ -147,7 +147,7 @@ class PersonalNoteOnHolidaysDataCheck(DataCheck):
personal_notes = PersonalNote.objects.not_empty().annotate_date_range() personal_notes = PersonalNote.objects.not_empty().annotate_date_range()
q = Q() q = Q(pk__in=[])
for holiday in holidays: for holiday in holidays:
q = q | Q(day_end__gte=holiday.date_start, day_start__lte=holiday.date_end) q = q | Q(day_end__gte=holiday.date_start, day_start__lte=holiday.date_end)
personal_notes = personal_notes.filter(q) personal_notes = personal_notes.filter(q)
......
...@@ -186,9 +186,11 @@ PersonalNoteFormSet = forms.modelformset_factory( ...@@ -186,9 +186,11 @@ PersonalNoteFormSet = forms.modelformset_factory(
class RegisterAbsenceForm(forms.Form): class RegisterAbsenceForm(forms.Form):
layout = Layout( layout = Layout(
Fieldset("", "person"),
Fieldset("", Row("date_start", "date_end"), Row("from_period", "to_period")), Fieldset("", Row("date_start", "date_end"), Row("from_period", "to_period")),
Fieldset("", Row("absent", "excused"), Row("excuse_type"), Row("remarks")), Fieldset("", Row("absent", "excused"), Row("excuse_type"), Row("remarks")),
) )
person = forms.ModelChoiceField(label=_("Person"), queryset=None, widget=Select2Widget)
date_start = forms.DateField(label=_("Start date"), initial=datetime.today) date_start = forms.DateField(label=_("Start date"), initial=datetime.today)
date_end = forms.DateField(label=_("End date"), initial=datetime.today) date_end = forms.DateField(label=_("End date"), initial=datetime.today)
from_period = forms.ChoiceField(label=_("Start period")) from_period = forms.ChoiceField(label=_("Start period"))
...@@ -203,10 +205,30 @@ class RegisterAbsenceForm(forms.Form): ...@@ -203,10 +205,30 @@ class RegisterAbsenceForm(forms.Form):
) )
remarks = forms.CharField(label=_("Remarks"), max_length=30, required=False) remarks = forms.CharField(label=_("Remarks"), max_length=30, required=False)
def __init__(self, *args, **kwargs): def __init__(self, request, *args, **kwargs):
self.request = request
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
period_choices = TimePeriod.period_choices period_choices = TimePeriod.period_choices
if self.request.user.has_perm("alsijil.register_absence"):
self.fields["person"].queryset = Person.objects.all()
else:
persons_qs = Person.objects.filter(
Q(
pk__in=get_objects_for_user(
self.request.user, "core.register_absence_person", Person
)
)
| Q(primary_group__owners=self.request.user.person)
| Q(
member_of__in=get_objects_for_user(
self.request.user, "core.register_absence_group", Group
)
)
).distinct()
self.fields["person"].queryset = persons_qs
self.fields["from_period"].choices = period_choices self.fields["from_period"].choices = period_choices
self.fields["to_period"].choices = period_choices self.fields["to_period"].choices = period_choices
self.fields["from_period"].initial = TimePeriod.period_min self.fields["from_period"].initial = TimePeriod.period_min
......
...@@ -8,14 +8,17 @@ msgstr "" ...@@ -8,14 +8,17 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-06-25 15:28+0200\n" "POT-Creation-Date: 2022-06-25 15:28+0200\n"
"PO-Revision-Date: 2022-06-22 19:59+0000\n" "PO-Revision-Date: 2022-07-03 02:56+0000\n"
"Last-Translator: Serhii Horichenko <m@sgg.im>\n" "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
"Language-Team: Russian <https://translate.edugit.org/projects/aleksis/aleksis-app-alsijil/ru/>\n" "Language-Team: Russian <https://translate.edugit.org/projects/aleksis/"
"aleksis-app-alsijil/ru/>\n"
"Language: ru\n" "Language: ru\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n"
"X-Generator: Weblate 4.12.1\n" "X-Generator: Weblate 4.12.1\n"
#: aleksis/apps/alsijil/actions.py:18 #: aleksis/apps/alsijil/actions.py:18
...@@ -133,7 +136,7 @@ msgstr "Домашняя работа на следующий урок" ...@@ -133,7 +136,7 @@ msgstr "Домашняя работа на следующий урок"
#: aleksis/apps/alsijil/forms.py:53 #: aleksis/apps/alsijil/forms.py:53
msgid "Carry over data to all other lessons with the same subject in this week" msgid "Carry over data to all other lessons with the same subject in this week"
msgstr "" msgstr "Перенести данные на все другие уроки с тем же предметом на этой неделе"
#: aleksis/apps/alsijil/forms.py:98 aleksis/apps/alsijil/forms.py:256 #: aleksis/apps/alsijil/forms.py:98 aleksis/apps/alsijil/forms.py:256
#: aleksis/apps/alsijil/forms.py:348 #: aleksis/apps/alsijil/forms.py:348
...@@ -210,7 +213,7 @@ msgstr "Замечания" ...@@ -210,7 +213,7 @@ msgstr "Замечания"
#: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63 #: aleksis/apps/alsijil/templates/alsijil/group_role/assigned_list.html:63
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/notes.html:28
msgid "Person" msgid "Person"
msgstr "Лицо" msgstr "Физлицо"
#: aleksis/apps/alsijil/forms.py:346 #: aleksis/apps/alsijil/forms.py:346
msgid "School term" msgid "School term"
...@@ -437,7 +440,7 @@ msgstr "Может назначать роль группы" ...@@ -437,7 +440,7 @@ msgstr "Может назначать роль группы"
#: aleksis/apps/alsijil/models.py:484 #: aleksis/apps/alsijil/models.py:484
msgid "Assigned person" msgid "Assigned person"
msgstr "Назначенное лицо" msgstr "Назначенное физлицо"
#: aleksis/apps/alsijil/models.py:489 aleksis/apps/alsijil/tables.py:105 #: aleksis/apps/alsijil/models.py:489 aleksis/apps/alsijil/tables.py:105
#: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128 #: aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html:128
...@@ -493,6 +496,8 @@ msgstr "Разрешить владельцам основных групп ре ...@@ -493,6 +496,8 @@ msgstr "Разрешить владельцам основных групп ре
#: aleksis/apps/alsijil/preferences.py:44 #: aleksis/apps/alsijil/preferences.py:44
msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups" msgid "Grant the owner of a parent group the same privileges as the owners of the respective child groups"
msgstr "" msgstr ""
"Наделить владельца родительской группы такими же правами, как у владельца "
"соответствующих дочерних групп"
#: aleksis/apps/alsijil/preferences.py:54 #: aleksis/apps/alsijil/preferences.py:54
msgid "Allow original teachers to edit their lessons although they are substituted" msgid "Allow original teachers to edit their lessons although they are substituted"
...@@ -507,16 +512,16 @@ msgid "This will carry over data only if the data in the following periods are e ...@@ -507,16 +512,16 @@ msgid "This will carry over data only if the data in the following periods are e
msgstr "Это перенесёт данные только в случае отсутствия данных в последующих уроках." msgstr "Это перенесёт данные только в случае отсутствия данных в последующих уроках."
#: aleksis/apps/alsijil/preferences.py:75 #: aleksis/apps/alsijil/preferences.py:75
#, fuzzy
#| msgid "Carry over data from first lesson period to the following lesson periods in lessons over multiple periods"
msgid "Allow carrying over data from any lesson period to all other lesson periods with the same lesson and in the same week" msgid "Allow carrying over data from any lesson period to all other lesson periods with the same lesson and in the same week"
msgstr "Переносить данные с первого урока в расписании на текущие уроки через несколько уроков" msgstr ""
"Разрешить перенос данных с любого урока на все такие же "
"уроки с таким же номером в расписании на той же неделе"
#: aleksis/apps/alsijil/preferences.py:79 #: aleksis/apps/alsijil/preferences.py:79
#, fuzzy
#| msgid "This will carry over data only if the data in the following periods are empty."
msgid "This will carry over data only if the data in the aforementioned periods are empty." msgid "This will carry over data only if the data in the aforementioned periods are empty."
msgstr "Это перенесёт данные только в случае отсутствия данных в последующих уроках." msgstr ""
"Это перенесёт данные только если в упомянутых выше уроках данные не "
"заполнены."
#: aleksis/apps/alsijil/preferences.py:88 #: aleksis/apps/alsijil/preferences.py:88
msgid "Carry over personal notes to all following lesson periods on the same day." msgid "Carry over personal notes to all following lesson periods on the same day."
...@@ -722,7 +727,7 @@ msgstr "Люди" ...@@ -722,7 +727,7 @@ msgstr "Люди"
#: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:36 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:36
msgid "Seating plan" msgid "Seating plan"
msgstr "План размещения" msgstr "План рассадки"
#: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:46 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:46
#: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:94 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:94
...@@ -759,7 +764,7 @@ msgstr "" ...@@ -759,7 +764,7 @@ msgstr ""
#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:10 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:10
msgid "Class register: person" msgid "Class register: person"
msgstr "Классный журнал: лицо" msgstr "Классный журнал: физлицо"
#: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:18 #: aleksis/apps/alsijil/templates/alsijil/class_register/person.html:18
#: aleksis/apps/alsijil/templates/alsijil/class_register/students_list.html:10 #: aleksis/apps/alsijil/templates/alsijil/class_register/students_list.html:10
...@@ -1144,7 +1149,7 @@ msgid "" ...@@ -1144,7 +1149,7 @@ msgid ""
"Seating plan for %(group)s in\n" "Seating plan for %(group)s in\n"
" %(room)s" " %(room)s"
msgstr "" msgstr ""
"План размещения %(group)s в\n" "План рассадки %(group)s в\n"
" %(room)s" " %(room)s"
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:14 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:14
...@@ -1156,13 +1161,13 @@ msgid "" ...@@ -1156,13 +1161,13 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
" Этот план размещения взят из родительской группы %(child_group)s.\n" " Этот план рассадки взят из родительской группы %(child_group)s.\n"
" При необходимости Вы можете настроить его под свою группу.\n" " При необходимости Вы можете настроить его под свою группу.\n"
" " " "
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:30 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:30
msgid "Edit seating plan" msgid "Edit seating plan"
msgstr "Редактировать план размещения" msgstr "Редактировать план рассадки"
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:37 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:37
msgid "Copy plan and edit" msgid "Copy plan and edit"
...@@ -1170,7 +1175,7 @@ msgstr "Скопировать план и отредактировать" ...@@ -1170,7 +1175,7 @@ msgstr "Скопировать план и отредактировать"
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:56 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:56
msgid "There is no seating plan for this lesson." msgid "There is no seating plan for this lesson."
msgstr "Для этого урока нет плана размещения." msgstr "Для этого урока нет плана рассадки."
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:64 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:64
#, python-format #, python-format
...@@ -1180,7 +1185,8 @@ msgid "" ...@@ -1180,7 +1185,8 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
" Создать новый план размещения %(group)s (%(subject)s) в %(room)s\n" " Создать новый план рассадки %(group)s (%(subject)s) в "
"%(room)s\n"
" " " "
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:78 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:78
...@@ -1191,7 +1197,7 @@ msgid "" ...@@ -1191,7 +1197,7 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
" Создать новый план размещения %(group)s в %(room)s\n" " Создать новый план рассадки %(group)s в %(room)s\n"
" " " "
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:6 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:6
......
...@@ -8,7 +8,7 @@ msgstr "" ...@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-06-25 15:28+0200\n" "POT-Creation-Date: 2022-06-25 15:28+0200\n"
"PO-Revision-Date: 2022-06-22 19:59+0000\n" "PO-Revision-Date: 2022-07-03 02:56+0000\n"
"Last-Translator: Serhii Horichenko <m@sgg.im>\n" "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
"Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/" "Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/"
"aleksis-app-alsijil/uk/>\n" "aleksis-app-alsijil/uk/>\n"
...@@ -16,10 +16,10 @@ msgstr "" ...@@ -16,10 +16,10 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 "
"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " "? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > "
"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " "14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % "
"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" "100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
"X-Generator: Weblate 4.12.1\n" "X-Generator: Weblate 4.12.1\n"
#: aleksis/apps/alsijil/actions.py:18 #: aleksis/apps/alsijil/actions.py:18
...@@ -143,7 +143,7 @@ msgstr "Домашня робота на наступний урок" ...@@ -143,7 +143,7 @@ msgstr "Домашня робота на наступний урок"
#: aleksis/apps/alsijil/forms.py:53 #: aleksis/apps/alsijil/forms.py:53
msgid "Carry over data to all other lessons with the same subject in this week" msgid "Carry over data to all other lessons with the same subject in this week"
msgstr "" msgstr "Перенести дані на усі інші уроки з таким самим предметом на цьому тижні"
#: aleksis/apps/alsijil/forms.py:98 aleksis/apps/alsijil/forms.py:256 #: aleksis/apps/alsijil/forms.py:98 aleksis/apps/alsijil/forms.py:256
#: aleksis/apps/alsijil/forms.py:348 #: aleksis/apps/alsijil/forms.py:348
...@@ -549,6 +549,8 @@ msgid "" ...@@ -549,6 +549,8 @@ msgid ""
"Grant the owner of a parent group the same privileges as the owners of the " "Grant the owner of a parent group the same privileges as the owners of the "
"respective child groups" "respective child groups"
msgstr "" msgstr ""
"Надати власнику батьківської групи такі самі повноваження, як і власникам "
"відповідних підлеглих груп"
#: aleksis/apps/alsijil/preferences.py:54 #: aleksis/apps/alsijil/preferences.py:54
#: aleksis/apps/alsijil/preferences.py:43 #: aleksis/apps/alsijil/preferences.py:43
...@@ -574,20 +576,19 @@ msgid "" ...@@ -574,20 +576,19 @@ msgid ""
msgstr "Це перенесе дані лише в тому разі, коли в поточних уроках даних немає." msgstr "Це перенесе дані лише в тому разі, коли в поточних уроках даних немає."
#: aleksis/apps/alsijil/preferences.py:75 #: aleksis/apps/alsijil/preferences.py:75
#, fuzzy
msgid "" msgid ""
"Allow carrying over data from any lesson period to all other " "Allow carrying over data from any lesson period to all other "
"lesson periods with the same lesson and in the same week" "lesson periods with the same lesson and in the same week"
msgstr "" msgstr ""
"Переносити дані з першого уроку в розкладі на поточні уроки через декілька " "Дозволити переносити дані з будь-якого уроку на усі інші "
"уроків" "уроки з таким самим номером урока на тому самому тижні"
#: aleksis/apps/alsijil/preferences.py:79 #: aleksis/apps/alsijil/preferences.py:79
#, fuzzy
msgid "" msgid ""
"This will carry over data only if the data in the aforementioned periods are " "This will carry over data only if the data in the aforementioned periods are "
"empty." "empty."
msgstr "Це перенесе дані лише в тому разі, коли в поточних уроках даних немає." msgstr ""
"Це перенесе дані лише в тому разі, коли у вищезгаданих уроках даних немає."
#: aleksis/apps/alsijil/preferences.py:88 #: aleksis/apps/alsijil/preferences.py:88
#: aleksis/apps/alsijil/preferences.py:63 #: aleksis/apps/alsijil/preferences.py:63
...@@ -819,7 +820,7 @@ msgstr "Особи" ...@@ -819,7 +820,7 @@ msgstr "Особи"
#: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:36 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:36
msgid "Seating plan" msgid "Seating plan"
msgstr "План розміщення" msgstr "План розсадження"
#: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:46 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:46
#: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:94 #: aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html:94
...@@ -1251,7 +1252,7 @@ msgid "" ...@@ -1251,7 +1252,7 @@ msgid ""
"Seating plan for %(group)s in\n" "Seating plan for %(group)s in\n"
" %(room)s" " %(room)s"
msgstr "" msgstr ""
"План розміщення %(group)s у\n" "План розсадження %(group)s у\n"
" %(room)s" " %(room)s"
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:14 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:14
...@@ -1265,14 +1266,14 @@ msgid "" ...@@ -1265,14 +1266,14 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
" Цей план розміщення взятий із батьківської групи " " Цей план розсадження взятий із батьківської групи "
"%(child_group)s.\n" "%(child_group)s.\n"
" При необхідності, Ви можете його налаштувати під свою групу.\n" " При необхідності, Ви можете його налаштувати під свою групу.\n"
" " " "
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:30 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:30
msgid "Edit seating plan" msgid "Edit seating plan"
msgstr "Редагувати план розміщення" msgstr "Редагувати план розсадження"
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:37 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:37
msgid "Copy plan and edit" msgid "Copy plan and edit"
...@@ -1280,7 +1281,7 @@ msgstr "Скопіювати план та відредагувати" ...@@ -1280,7 +1281,7 @@ msgstr "Скопіювати план та відредагувати"
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:56 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:56
msgid "There is no seating plan for this lesson." msgid "There is no seating plan for this lesson."
msgstr "Для цього уроку немає плану розміщення." msgstr "Для цього уроку немає плану розсадження."
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:64 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson/tabs/seating_plan.html:64
#, python-format #, python-format
...@@ -1291,7 +1292,7 @@ msgid "" ...@@ -1291,7 +1292,7 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
" Створити новий план розміщення %(group)s (%(subject)s) у " " Створити новий план розсадження %(group)s (%(subject)s) у "
"%(room)s\n" "%(room)s\n"
" " " "
...@@ -1303,7 +1304,7 @@ msgid "" ...@@ -1303,7 +1304,7 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
" Створити новий план розміщення %(group)s у %(room)s\n" " Створити новий план розсадження %(group)s у %(room)s\n"
" " " "
#: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:6 #: aleksis/apps/alsijil/templates/alsijil/partials/lesson_status.html:6
......
...@@ -89,6 +89,17 @@ MENUS = { ...@@ -89,6 +89,17 @@ MENUS = {
), ),
], ],
}, },
{
"name": _("Register absence"),
"url": "register_absence",
"icon": "rate_review",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
"alsijil.view_register_absence_rule",
),
],
},
{ {
"name": _("Excuse types"), "name": _("Excuse types"),
"url": "excuse_types", "url": "excuse_types",
......
...@@ -434,6 +434,7 @@ def generate_person_list_with_class_register_statistics( ...@@ -434,6 +434,7 @@ def generate_person_list_with_class_register_statistics(
"filtered_personal_notes", "filtered_personal_notes",
filter=Q(filtered_personal_notes__absent=True) filter=Q(filtered_personal_notes__absent=True)
& ~Q(filtered_personal_notes__excuse_type__count_as_absent=False), & ~Q(filtered_personal_notes__excuse_type__count_as_absent=False),
distinct=True,
), ),
excused=Count( excused=Count(
"filtered_personal_notes", "filtered_personal_notes",
...@@ -442,6 +443,7 @@ def generate_person_list_with_class_register_statistics( ...@@ -442,6 +443,7 @@ def generate_person_list_with_class_register_statistics(
filtered_personal_notes__excused=True, filtered_personal_notes__excused=True,
) )
& ~Q(filtered_personal_notes__excuse_type__count_as_absent=False), & ~Q(filtered_personal_notes__excuse_type__count_as_absent=False),
distinct=True,
), ),
excused_without_excuse_type=Count( excused_without_excuse_type=Count(
"filtered_personal_notes", "filtered_personal_notes",
...@@ -450,15 +452,16 @@ def generate_person_list_with_class_register_statistics( ...@@ -450,15 +452,16 @@ def generate_person_list_with_class_register_statistics(
filtered_personal_notes__excused=True, filtered_personal_notes__excused=True,
filtered_personal_notes__excuse_type__isnull=True, filtered_personal_notes__excuse_type__isnull=True,
), ),
distinct=True,
), ),
unexcused=Count( unexcused=Count(
"filtered_personal_notes", "filtered_personal_notes",
filter=Q(filtered_personal_notes__absent=True, filtered_personal_notes__excused=False), 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__late"),
tardiness_count=Count( tardiness_count=Count(
"filtered_personal_notes", "filtered_personal_notes", filter=Q(filtered_personal_notes__late__gt=0), distinct=True
filter=Q(filtered_personal_notes__late__gt=0),
), ),
) )
......
...@@ -156,15 +156,20 @@ view_week_personal_notes_predicate = has_person & ( ...@@ -156,15 +156,20 @@ view_week_personal_notes_predicate = has_person & (
add_perm("alsijil.view_week_personalnote_rule", view_week_personal_notes_predicate) add_perm("alsijil.view_week_personalnote_rule", view_week_personal_notes_predicate)
# Register absence # Register absence
register_absence_predicate = has_person & ( view_register_absence_predicate = has_person & (
( (
is_person_group_owner is_person_group_owner
& is_site_preference_set("alsijil", "register_absence_as_primary_group_owner") & is_site_preference_set("alsijil", "register_absence_as_primary_group_owner")
) )
| has_global_perm("alsijil.register_absence") | has_global_perm("alsijil.register_absence")
)
register_absence_predicate = has_person & (
view_register_absence_predicate
| has_object_perm("core.register_absence_person") | has_object_perm("core.register_absence_person")
| has_person_group_object_perm("core.register_absence_group") | has_person_group_object_perm("core.register_absence_group")
) )
add_perm("alsijil.view_register_absence_rule", view_register_absence_predicate)
add_perm("alsijil.register_absence_rule", register_absence_predicate) add_perm("alsijil.register_absence_rule", register_absence_predicate)
# View full register for group # View full register for group
......
{# -*- engine:django -*- #} {# -*- engine:django -*- #}
{% extends "core/base.html" %} {% extends "core/base.html" %}
{% load material_form i18n static %} {% load material_form i18n static any_js %}
{% block browser_title %}{% blocktrans %}Register absence{% endblocktrans %}{% endblock %} {% block browser_title %}{% blocktrans %}Register absence{% endblocktrans %}{% endblock %}
{% block page_title %}{% blocktrans %}Register absence{% endblocktrans %}{% endblock %} {% block page_title %}{% blocktrans %}Register absence{% endblocktrans %}{% endblock %}
{% block content %} {% block extra_head %}
<h6>{% trans "Person" %}: {{ person }}</h6> {{ form.media.css }}
{% include_css "select2-materialize" %}
{% endblock %}
{% block content %}
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
{% form form=register_absence_form %}{% endform %} {% form form=register_absence_form %}{% endform %}
...@@ -22,4 +25,7 @@ ...@@ -22,4 +25,7 @@
}); });
}); });
</script> </script>
{% include_js "select2-materialize" %}
{{ form.media.js }}
{% endblock %} {% endblock %}
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
<i class="material-icons left small orange-text">warning</i> <i class="material-icons left small orange-text">warning</i>
{% trans "There is no seating plan for this lesson." %} {% trans "There is no seating plan for this lesson." %}
</div> </div>
{% has_perm "stoelindeling.add_seatingplan_rule" user first_group as can_add %} {% has_perm "stoelindeling.create_seatingplan_rule" user first_group as can_add %}
{% if can_add %} {% if can_add %}
<div class="row margin-bottom"> <div class="row margin-bottom">
<div class="col s12"> <div class="col s12">
...@@ -69,7 +69,7 @@ ...@@ -69,7 +69,7 @@
</div> </div>
{% endif %} {% endif %}
{% for parent_group in first_group.parent_groups.all %} {% for parent_group in first_group.parent_groups.all %}
{% has_perm "stoelindeling.add_seatingplan_rule" user parent_group as can_add %} {% has_perm "stoelindeling.create_seatingplan_rule" user parent_group as can_add %}
{% if can_add %} {% if can_add %}
<div class="row"> <div class="row">
<div class="col s12"> <div class="col s12">
......
...@@ -48,6 +48,7 @@ urlpatterns = [ ...@@ -48,6 +48,7 @@ urlpatterns = [
name="delete_personal_note", name="delete_personal_note",
), ),
path("absence/new/<int:id_>/", views.register_absence, name="register_absence"), path("absence/new/<int:id_>/", views.register_absence, name="register_absence"),
path("absence/new/", views.register_absence, name="register_absence"),
path("extra_marks/", views.ExtraMarkListView.as_view(), name="extra_marks"), path("extra_marks/", views.ExtraMarkListView.as_view(), name="extra_marks"),
path( path(
"extra_marks/create/", "extra_marks/create/",
......
...@@ -240,8 +240,10 @@ def register_object( ...@@ -240,8 +240,10 @@ def register_object(
else: else:
persons = Person.objects.all() persons = Person.objects.all()
persons_qs = register_object.get_personal_notes(persons, wanted_week).filter( persons_qs = (
person__member_of__in=request.user.person.owner_of.all() register_object.get_personal_notes(persons, wanted_week)
.filter(person__member_of__in=request.user.person.owner_of.all())
.distinct()
) )
# Annotate group roles # Annotate group roles
...@@ -476,12 +478,16 @@ def week_view( ...@@ -476,12 +478,16 @@ def week_view(
if not request.user.has_perm("alsijil.view_week_personalnote_rule", instance): if not request.user.has_perm("alsijil.view_week_personalnote_rule", instance):
persons_qs = persons_qs.filter(pk=request.user.person.pk) persons_qs = persons_qs.filter(pk=request.user.person.pk)
elif group: elif group:
persons_qs = persons_qs.filter(member_of=group).filter( persons_qs = (
member_of__in=request.user.person.owner_of.all() persons_qs.filter(member_of=group)
.filter(member_of__in=request.user.person.owner_of.all())
.distinct()
) )
else: else:
persons_qs = persons_qs.filter(member_of__in=groups).filter( persons_qs = (
member_of__in=request.user.person.owner_of.all() persons_qs.filter(member_of__in=groups)
.filter(member_of__in=request.user.person.owner_of.all())
.distinct()
) )
# Prefetch object permissions for persons and groups the persons are members of # Prefetch object permissions for persons and groups the persons are members of
...@@ -736,7 +742,7 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: ...@@ -736,7 +742,7 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
prefetched_persons = [] prefetched_persons = []
for person in persons: for person in persons:
person.filtered_notes = sorted_personal_notes["person"][person.pk] person.filtered_notes = sorted_personal_notes["person"].get(person.pk, [])
prefetched_persons.append(person) prefetched_persons.append(person)
context["school_term"] = group.school_term context["school_term"] = group.school_term
...@@ -792,12 +798,16 @@ def my_students(request: HttpRequest) -> HttpResponse: ...@@ -792,12 +798,16 @@ def my_students(request: HttpRequest) -> HttpResponse:
new_groups = [] new_groups = []
for group in relevant_groups: for group in relevant_groups:
persons = group.generate_person_list_with_class_register_statistics( persons = (
group.members.prefetch_related( group.generate_person_list_with_class_register_statistics(
"primary_group__owners", group.members.prefetch_related(
Prefetch("member_of", queryset=relevant_groups, to_attr="member_of_prefetched"), "primary_group__owners",
Prefetch("member_of", queryset=relevant_groups, to_attr="member_of_prefetched"),
)
) )
).filter(member_of__in=request.user.person.owner_of.all()) .filter(member_of__in=request.user.person.owner_of.all())
.distinct()
)
persons_for_group = [] persons_for_group = []
for person in persons: for person in persons:
person.set_object_permission_checker(checker) person.set_object_permission_checker(checker)
...@@ -830,10 +840,10 @@ class StudentsList(PermissionRequiredMixin, DetailView): ...@@ -830,10 +840,10 @@ class StudentsList(PermissionRequiredMixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["group"] = self.object context["group"] = self.object
context[ context["persons"] = (
"persons" self.object.generate_person_list_with_class_register_statistics()
] = self.object.generate_person_list_with_class_register_statistics().filter( .filter(member_of__in=self.request.user.person.owner_of.all())
member_of__in=self.request.user.person.owner_of.all() .distinct()
) )
context["extra_marks"] = ExtraMark.objects.all() context["extra_marks"] = ExtraMark.objects.all()
context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True) context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True)
...@@ -1060,18 +1070,27 @@ def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResp ...@@ -1060,18 +1070,27 @@ def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResp
@never_cache @never_cache
@permission_required("alsijil.register_absence_rule", fn=objectgetter_optional(Person)) @permission_required("alsijil.register_absence_rule", fn=objectgetter_optional(Person))
def register_absence(request: HttpRequest, id_: int) -> HttpResponse: def register_absence(request: HttpRequest, id_: int = None) -> HttpResponse:
context = {} context = {}
person = get_object_or_404(Person, pk=id_) if id_:
person = get_object_or_404(Person, pk=id_)
else:
person = None
register_absence_form = RegisterAbsenceForm(request.POST or None) register_absence_form = RegisterAbsenceForm(
request, request.POST or None, initial={"person": person}
)
if request.method == "POST" and register_absence_form.is_valid(): if (
request.method == "POST"
and register_absence_form.is_valid()
and request.user.has_perm("alsijil.register_absence_rule", person)
):
confirmed = request.POST.get("confirmed", "0") == "1" confirmed = request.POST.get("confirmed", "0") == "1"
# Get data from form # Get data from form
# person = register_absence_form.cleaned_data["person"] person = register_absence_form.cleaned_data["person"]
start_date = register_absence_form.cleaned_data["date_start"] start_date = register_absence_form.cleaned_data["date_start"]
end_date = register_absence_form.cleaned_data["date_end"] end_date = register_absence_form.cleaned_data["date_end"]
from_period = register_absence_form.cleaned_data["from_period"] from_period = register_absence_form.cleaned_data["from_period"]
......
...@@ -31,7 +31,7 @@ author = "The AlekSIS Team" ...@@ -31,7 +31,7 @@ author = "The AlekSIS Team"
# The short X.Y version # The short X.Y version
version = "2.1" version = "2.1"
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = "2.1.1.dev0" release = "2.2.dev0"
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
......
[tool.poetry] [tool.poetry]
name = "AlekSIS-App-Alsijil" name = "AlekSIS-App-Alsijil"
version = "2.1.1.dev0" version = "2.2.dev0"
packages = [ packages = [
{ include = "aleksis" } { include = "aleksis" }
] ]
...@@ -48,7 +48,7 @@ secondary = true ...@@ -48,7 +48,7 @@ secondary = true
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9" python = "^3.9"
aleksis-core = "^2.7" aleksis-core = "^2.11"
aleksis-app-chronos = "^2.2" aleksis-app-chronos = "^2.2"
aleksis-app-stoelindeling = { version = "^1.0", optional = true } aleksis-app-stoelindeling = { version = "^1.0", optional = true }
......
...@@ -10,7 +10,7 @@ skip_install = true ...@@ -10,7 +10,7 @@ skip_install = true
envdir = {toxworkdir}/globalenv envdir = {toxworkdir}/globalenv
commands_pre = commands_pre =
poetry install poetry install
poetry run aleksis-admin yarn install poetry run aleksis-admin webpack_bundle
poetry run aleksis-admin collectstatic --no-input poetry run aleksis-admin collectstatic --no-input
commands = commands =
poetry run pytest --cov=. {posargs} aleksis/ poetry run pytest --cov=. {posargs} aleksis/
......