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 (23)
Showing
with 199 additions and 41 deletions
......@@ -69,7 +69,10 @@
<DocumentationLoader />
</template>
</infinite-scrolling-date-sorted-c-r-u-d-iterator>
<absence-creation-dialog :absence-reasons="absenceReasons" />
<absence-creation-dialog
:absence-reasons="absenceReasons"
:affected-query="lastQuery"
/>
</div>
</template>
......
......@@ -96,7 +96,10 @@ import permissionsMixin from "aleksis.core/mixins/permissions.js";
import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
import { DateTime } from "luxon";
import { createAbsencesForPersons } from "./absenceCreation.graphql";
import {
clearAbsencesForPersons,
createAbsencesForPersons,
} from "./absenceCreation.graphql";
export default {
name: "AbsenceCreationDialog",
......@@ -152,13 +155,17 @@ export default {
confirm() {
this.handleLoading(true);
this.mutate(
createAbsencesForPersons,
this.absenceReason !== "present"
? createAbsencesForPersons
: clearAbsencesForPersons,
{
persons: this.persons.map((p) => p.id),
start: this.$toUTCISO(this.$parseISODate(this.startDate)),
end: this.$toUTCISO(this.$parseISODate(this.endDate)),
comment: this.comment,
reason: this.absenceReason,
...(this.absenceReason !== "present" && { comment: this.comment }),
...(this.absenceReason !== "present" && {
reason: this.absenceReason,
}),
},
(storedDocumentations, incomingStatuses) => {
incomingStatuses.forEach((newStatus) => {
......@@ -172,7 +179,7 @@ export default {
(part) => part.id === newStatus.id,
);
participationStatus.absenceReason = newStatus.absenceReason;
participationStatus.absenceReason = newStatus?.absenceReason;
participationStatus.isOptimistic = newStatus.isOptimistic;
});
......
......@@ -55,6 +55,7 @@
<v-text-field
:label="$t('forms.labels.comment')"
:value="comment"
:disabled="absenceReason == 'present'"
@input="$emit('comment', $event)"
/>
</v-row>
......@@ -62,6 +63,7 @@
<div aria-required="true">
<absence-reason-group-select
:rules="$rules().required.build()"
allow-empty
:value="absenceReason"
:custom-absence-reasons="absenceReasons"
@input="$emit('absence-reason', $event)"
......
......@@ -59,3 +59,23 @@ mutation createAbsencesForPersons(
}
}
}
mutation clearAbsencesForPersons(
$persons: [ID]!
$start: DateTime!
$end: DateTime!
) {
clearAbsencesForPersons(persons: $persons, start: $start, end: $end) {
ok
items: participationStatuses {
id
isOptimistic
relatedDocumentation {
id
}
absenceReason {
id
}
}
}
}
......@@ -143,7 +143,7 @@ export default {
},
// Group teachers by their substitution status (regular, removed)
teachersWithStatus() {
if (this.documentation?.amends?.amends) {
if (!this.documentation?.amends?.amends) {
// Only do grouping if documentation is based on substitution
return this.documentation.teachers;
}
......
......@@ -87,8 +87,7 @@
<v-col cols="12" md="6" class="pa-0 d-flex">
<v-list-item-avatar
v-if="
mode === MODE.PARTICIPATIONS &&
!$vuetify.breakpoint.mobile
mode === MODE.PARTICIPATIONS && showCheckbox(item)
"
>
<v-item v-slot="{ active, toggle }" :value="item.id">
......@@ -379,6 +378,9 @@ export default {
// Only ExtraMarks can be deleted
return item.canDelete && item.extraMark;
},
showCheckbox(item) {
return this.showEdit(item);
},
},
};
</script>
{}
......@@ -12,6 +12,7 @@
},
"coursebook": {
"absences": {
"absences": "Abwesenheiten",
"action_for_selected": "Ausgewählten Teilnehmer markieren als: | {count} ausgewählte Teilnehmer markieren als",
"button": "Abwesenheiten erfassen",
"lessons": "Keine Stunden | 1 Stunde | {count} Stunden",
......@@ -60,6 +61,29 @@
"present_number": "{present}/{total} anwesend"
},
"present_number": "{present}/{total} anwesend",
"print": {
"button": "Drucken",
"groups": "Gruppen",
"include": "Abschnitte",
"include_abbreviations": "Abkürzungen",
"include_coursebook": "Kursbuch",
"include_cover": "Deckblatt",
"include_members_table": "Tabelle aller Gruppenmitglieder mit Statistiken",
"include_person_overviews": "Detailseiten für alle Gruppenmitglieder",
"include_teachers_and_subjects_table": "Tabelle mit Lehrkräften und Fächern",
"title": "Kursbuchausdruck"
},
"statistics": {
"person_compact": {
"title": "Kursbuch · Statistiken"
},
"person_page": {
"summary": "Zusammenfassung",
"title": "Kursbuch · Statistiken · {fullName}"
},
"person_view_details": "Details",
"title_plural": "Statistiken"
},
"status": {
"available": "Kursbucheintrag vorhanden",
"cancelled": "Stunde fällt aus",
......@@ -90,28 +114,7 @@
},
"title_absences": "Kursbuch · Abwesenheiten",
"title_documentations": "Kursbuch",
"title_plural": "Kursbuch",
"statistics": {
"person_compact": {
"title": "Kursbuch · Statistiken"
},
"person_page": {
"title": "Kursbuch · Statistiken · {fullName}",
"summary": "Zusammenfassung"
}
},
"print": {
"button": "Drucken",
"title": "Kursbuchausdruck",
"groups": "Gruppen",
"include": "Abschnitte",
"include_cover": "Deckblatt",
"include_abbreviations": "Abkürzungen",
"include_members_table": "Tabelle aller Gruppenmitglieder mit Statistiken",
"include_teachers_and_subjects_table": "Tabelle mit Lehrkräften und Fächern",
"include_person_overviews": "Detailseiten für alle Gruppenmitglieder",
"include_coursebook": "Kursbuch"
}
"title_plural": "Kursbuch"
},
"excuse_types": {
"menu_title": "Entschuldigungsarten"
......@@ -155,11 +158,13 @@
"lesson_length_exceeded": "Die Verspätung überschreitet die Stundenlänge.",
"minutes_late": "pünktlich | eine Minute verspätet | {n} Minuten zu spät",
"minutes_late_current": "pünktlich (basierend auf der aktuellen Uhrzeit) | eine Minute zu spät (basierend auf der aktuellen Uhrzeit) | {n} Minuten zu spät (basierend auf der aktuellen Uhrzeit)",
"times_late": "nie | 1× | {n}×",
"no_results": "Keine Suchergebnisse für {search}",
"note": "Notiz",
"personal_notes": "Persönliche Notizen",
"tardiness": "Verspätung",
"tardiness_n": "Verspätungen | Verspätung | Verspätungen",
"tardiness_plural": "Verspätungen",
"tardiness_n": "Verspätungen | Verspätung | Verspätungen"
"times_late": "nie | 1× | {n}×"
},
"persons": {
"menu_title": "Meine Schüler*innen"
......
{}
{
"alsijil": {
"coursebook": {
"filter": {
"groups": "Greges"
},
"print": {
"groups": "Greges"
}
}
}
}
{}
......@@ -12,6 +12,9 @@
"coursebook": {
"filter": {
"groups": "Группы"
},
"print": {
"groups": "Группы"
}
},
"excuse_types": {
......
{}
......@@ -11,6 +11,7 @@
},
"coursebook": {
"absences": {
"absences": "Відсутності",
"action_for_selected": "Відмітити обраного відвідувача як: | Відмітити {count} відвідувачів як",
"button": "Зареєструвати відсутності",
"lessons": "Без уроків | 1 урок | {count} уроків",
......@@ -58,6 +59,9 @@
"present": "Присутній",
"present_number": "{present}/{total} присутніх"
},
"print": {
"groups": "Групи"
},
"status": {
"available": "Документація доступна",
"cancelled": "Урок скасований",
......@@ -132,7 +136,8 @@
"minutes_late": "вчасно | на хвилину пізніше | на {n} хвилин пізніше",
"minutes_late_current": "вчасно (на основі поточного часу) | на хвилину пізніше (на основі поточного часу) | на {n} хвилин пізніше (на основі поточного часу)",
"note": "Нотатка",
"tardiness": "Запізнення"
"tardiness": "Запізнення",
"tardiness_plural": "Запізнення"
},
"persons": {
"menu_title": "Мої студенти"
......
......@@ -498,6 +498,29 @@ class ParticipationStatus(CalendarEvent):
return participation_statuses
@classmethod
def clear_absence_by_datetimes(
cls, person: Person, start: datetime, end: datetime
) -> list["ParticipationStatus"]:
participation_statuses = []
events = cls.get_single_events(
start,
end,
None,
{"person": person},
with_reference_object=True,
)
for event in events:
participation_status = event["REFERENCE_OBJECT"]
participation_status.absence_reason = None
participation_status.base_absence = None
participation_status.save()
participation_statuses.append(participation_status)
return participation_statuses
def fill_from_kolego(self, kolego_absence: KolegoAbsence):
"""Take over data from a Kolego absence."""
self.base_absence = kolego_absence
......
......@@ -13,11 +13,13 @@ from aleksis.core.util.predicates import (
from .util.predicates import (
can_edit_documentation,
can_edit_participation_status,
can_edit_participation_status_for_documentation,
can_edit_personal_note,
can_register_absence_for_at_least_one_group,
can_register_absence_for_person,
can_view_documentation,
can_view_participation_status,
can_view_participation_status_for_documentation,
can_view_personal_note,
can_view_statistics_for_person,
has_person_group_object_perm,
......@@ -196,7 +198,8 @@ add_perm("alsijil.edit_documentation_rule", edit_documentation_predicate)
add_perm("alsijil.delete_documentation_rule", edit_documentation_predicate)
view_participation_status_for_documentation_predicate = has_person & (
has_global_perm("alsijil.change_participationstatus") | can_view_participation_status
has_global_perm("alsijil.change_participationstatus")
| can_view_participation_status_for_documentation
)
add_perm(
"alsijil.view_participation_status_for_documentation_rule",
......@@ -205,7 +208,10 @@ add_perm(
edit_participation_status_for_documentation_with_time_range_predicate = (
has_person
& (has_global_perm("alsijil.change_participationstatus") | can_edit_participation_status)
& (
has_global_perm("alsijil.change_participationstatus")
| can_edit_participation_status_for_documentation
)
& is_in_allowed_time_range_for_participation_status
)
add_perm(
......@@ -214,13 +220,30 @@ add_perm(
)
edit_participation_status_for_documentation_predicate = has_person & (
has_global_perm("alsijil.change_participationstatus") | can_edit_participation_status
has_global_perm("alsijil.change_participationstatus")
| can_edit_participation_status_for_documentation
)
add_perm(
"alsijil.edit_participation_status_for_documentation_rule",
edit_participation_status_for_documentation_predicate,
)
view_participation_status_predicate = has_person & (
has_global_perm("alsijil.view_participationstatus") | can_view_participation_status
)
add_perm(
"alsijil.view_participation_status_rule",
view_participation_status_predicate,
)
edit_participation_status_predicate = has_person & (
has_global_perm("alsijil.change_participationstatus") | can_edit_participation_status
)
add_perm(
"alsijil.edit_participation_status_rule",
edit_participation_status_predicate,
)
view_personal_note_predicate = has_person & (
has_global_perm("alsijil.change_newpersonalnote") | can_view_personal_note
)
......
......@@ -24,6 +24,7 @@ from aleksis.core.util.core_helpers import (
from ..model_extensions import annotate_person_statistics_for_school_term
from ..models import Documentation, ExtraMark, NewPersonalNote, ParticipationStatus
from .absences import (
AbsencesForPersonsClearMutation,
AbsencesForPersonsCreateMutation,
)
from .documentation import (
......@@ -352,6 +353,7 @@ class Mutation(graphene.ObjectType):
touch_documentation = TouchDocumentationMutation.Field()
update_participation_statuses = ParticipationStatusBatchPatchMutation.Field()
create_absences_for_persons = AbsencesForPersonsCreateMutation.Field()
clear_absences_for_persons = AbsencesForPersonsClearMutation.Field()
extend_participation_statuses = ExtendParticipationStatusToAbsenceBatchMutation.Field()
create_extra_marks = ExtraMarkBatchCreateMutation.Field()
......
......@@ -56,3 +56,44 @@ class AbsencesForPersonsCreateMutation(graphene.Mutation):
return AbsencesForPersonsCreateMutation(
ok=True, participation_statuses=participation_statuses
)
class AbsencesForPersonsClearMutation(graphene.Mutation):
class Arguments:
persons = graphene.List(graphene.ID, required=True)
start = graphene.DateTime(required=True)
end = graphene.DateTime(required=True)
ok = graphene.Boolean()
participation_statuses = graphene.List(ParticipationStatusType)
@classmethod
def mutate(
cls,
root,
info,
persons: list[str | int],
start: datetime.datetime,
end: datetime.datetime,
):
participation_statuses = []
persons = Person.objects.filter(pk__in=persons)
for person in persons:
if not info.context.user.has_perm("alsijil.register_absence_rule", person):
raise PermissionDenied()
participation_statuses += ParticipationStatus.clear_absence_by_datetimes(
person=person, start=start, end=end
)
Absence.clear_or_extend_absences_in_timespan(
person=person,
datetime_start=start,
datetime_end=end,
)
return AbsencesForPersonsClearMutation(
ok=True, participation_statuses=participation_statuses
)
......@@ -7,12 +7,10 @@ from reversion import create_revision, set_comment, set_user
from aleksis.apps.alsijil.util.predicates import (
can_edit_documentation,
can_edit_participation_status_for_documentation,
is_in_allowed_time_range,
is_in_allowed_time_range_for_participation_status,
)
from aleksis.apps.alsijil.util.predicates import (
can_edit_participation_status as can_edit_participation_status_predicate,
)
from aleksis.apps.chronos.schema import LessonEventType
from aleksis.apps.cursus.models import Subject
from aleksis.apps.cursus.schema import CourseType, SubjectType
......@@ -98,7 +96,7 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
@staticmethod
def resolve_can_edit_participation_status(root: Documentation, info, **kwargs):
"""Shows whether the user can edit all participation statuses of the documentation"""
return can_edit_participation_status_predicate(info.context.user, root)
return can_edit_participation_status_for_documentation(info.context.user, root)
@staticmethod
def resolve_can_view_participation_status(root: Documentation, info, **kwargs):
......
......@@ -68,6 +68,14 @@ class ParticipationStatusType(
note__isnull=False,
).exclude(note="")
@staticmethod
def resolve_can_edit(root: ParticipationStatus, info, **kwargs):
return info.context.user.has_perm("alsijil.edit_participation_status_rule", root)
@staticmethod
def resolve_can_delete(root: ParticipationStatus, info, **kwargs):
return info.context.user.has_perm("alsijil.edit_participation_status_rule", root)
class ParticipationStatusBatchPatchMutation(BaseBatchPatchMutation):
class Meta:
......