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 (17)
Showing
with 156 additions and 76 deletions
......@@ -70,7 +70,10 @@
</template>
</infinite-scrolling-date-sorted-c-r-u-d-iterator>
<v-scale-transition>
<absence-creation-dialog v-if="pageType === 'absences'" />
<absence-creation-dialog
v-if="pageType === 'absences'"
:absence-reasons="absenceReasons"
/>
</v-scale-transition>
</div>
</template>
......@@ -84,11 +87,11 @@ import CoursebookLoader from "./CoursebookLoader.vue";
import DocumentationModal from "./documentation/DocumentationModal.vue";
import DocumentationAbsencesModal from "./absences/DocumentationAbsencesModal.vue";
import AbsenceCreationDialog from "./absences/AbsenceCreationDialog.vue";
import { extraMarks } from "../extra_marks/extra_marks.graphql";
import { extraMarks } from "./queries/extraMarks.graphql";
import DocumentationLoader from "./documentation/DocumentationLoader.vue";
import sendToServerMixin from "./absences/sendToServerMixin";
import { absenceReasons } from "./absences/absenceReasons.graphql";
import { subjects } from "aleksis.apps.cursus/components/subject.graphql";
import { absenceReasons } from "./queries/absenceReasons.graphql";
import { subjects } from "./queries/subjects.graphql";
export default {
name: "Coursebook",
......
......@@ -30,6 +30,7 @@
:end-date="endDate"
:comment="comment"
:absence-reason="absenceReason"
:absence-reasons="absenceReasons"
@valid="formValid = $event"
@persons="persons = $event"
@start-date="startDate = $event"
......@@ -121,6 +122,12 @@ export default {
absenceReason: "",
};
},
props: {
absenceReasons: {
type: Array,
required: true,
},
},
mounted() {
this.addPermissions(["alsijil.view_register_absence_rule"]);
this.clearForm();
......
......@@ -61,6 +61,7 @@
<absence-reason-group-select
:rules="$rules().required.build()"
:value="absenceReason"
:custom-absence-reasons="absenceReasons"
@input="$emit('absence-reason', $event)"
/>
</div>
......@@ -115,6 +116,10 @@ export default {
type: String,
required: true,
},
absenceReasons: {
type: Array,
required: true,
},
},
computed: {
maxStartTime() {
......
......@@ -351,6 +351,7 @@ export default {
/>
<h4>{{ $t("alsijil.extra_marks.title_plural") }}</h4>
<extra-mark-buttons
:custom-extra-marks="extraMarks"
@input="handleMultipleAction('extraMark', $event)"
/>
<h4>{{ $t("alsijil.personal_notes.tardiness") }}</h4>
......
query absenceReasons($orderBy: [String], $filters: JSONString) {
items: coursebookAbsenceReasons(orderBy: $orderBy, filters: $filters) {
id
shortName
name
colour
default
canEdit
canDelete
}
}
......@@ -111,7 +111,6 @@ query documentationsForCoursebook(
oldId
canEdit
futureNotice
canDelete
futureNoticeParticipationStatus
canEditParticipationStatus
canViewParticipationStatus
......
query absenceReasons {
items: coursebookAbsenceReasons {
id
shortName
name
colour
default
}
}
query extraMarks {
items: extraMarks {
id
shortName
name
colourFg
colourBg
showInCoursebook
}
}
query subjects {
items: subjects {
id
name
shortName
colourFg
colourBg
}
}
......@@ -86,7 +86,7 @@ import AbsenceReasonChip from "aleksis.apps.kolego/components/AbsenceReasonChip.
import ExtraMarkChip from "aleksis.apps.alsijil/components/extra_marks/ExtraMarkChip.vue";
import { statisticsByGroup } from "./statistics.graphql";
import { absenceReasons } from "../absences/absenceReasons.graphql";
import { absenceReasons } from "../queries/absenceReasons.graphql";
import { extraMarks } from "../../extra_marks/extra_marks.graphql";
import { MODE } from "./modes";
......
......@@ -13,7 +13,7 @@ export default {
query: extraMarks,
update: (data) => data.items,
skip() {
return this.customExtraMarks > 0;
return this.customExtraMarks.length > 0;
},
},
},
......
......@@ -72,18 +72,6 @@ class GroupRoleAssignmentQuerySet(QuerySet):
class DocumentationManager(RecurrencePolymorphicManager):
"""Manager adding specific methods to documentations."""
def get_queryset(self):
"""Ensure often used related data are loaded as well."""
return (
super()
.get_queryset()
.select_related(
"course",
"subject",
)
.prefetch_related("teachers")
)
class ParticipationStatusManager(RecurrencePolymorphicManager):
"""Manager adding specific methods to participation statuses."""
......
......@@ -139,7 +139,6 @@ class Documentation(CalendarEvent):
events: list,
incomplete: Optional[bool] = False,
absences_exist: Optional[bool] = False,
request: Optional[HttpRequest] = None,
) -> tuple:
"""Get all the documentations for the events.
Create dummy documentations if none exist.
......@@ -149,11 +148,22 @@ class Documentation(CalendarEvent):
dummies = []
# Prefetch existing documentations to speed things up
existing_documentations = Documentation.objects.filter(
datetime_start__lte=datetime_end,
datetime_end__gte=datetime_start,
amends__in=[e["REFERENCE_OBJECT"] for e in events],
).prefetch_related("participations")
existing_documentations = (
Documentation.objects.filter(
datetime_start__lte=datetime_end,
datetime_end__gte=datetime_start,
amends__in=[e["REFERENCE_OBJECT"] for e in events],
)
.prefetch_related(
"participations",
"participations__person",
"participations__absence_reason",
"teachers",
"personal_notes",
"personal_notes__extra_mark",
)
.select_related("course", "subject")
)
for event in events:
if incomplete and event["STATUS"] == "CANCELLED":
......@@ -179,6 +189,7 @@ class Documentation(CalendarEvent):
)
):
continue
doc._amends_prefetched = event_reference_obj
docs.append(doc)
elif not absences_exist:
if event_reference_obj.amends:
......@@ -214,7 +225,6 @@ class Documentation(CalendarEvent):
start: datetime,
end: datetime,
incomplete: Optional[bool] = False,
request: Optional[HttpRequest] = None,
) -> tuple:
"""Get all the documentations for the person from start to end datetime.
Create dummy documentations if none exist.
......@@ -233,7 +243,7 @@ class Documentation(CalendarEvent):
with_reference_object=True,
)
return Documentation.get_documentations_for_events(start, end, events, incomplete, request)
return Documentation.get_documentations_for_events(start, end, events, incomplete)
@classmethod
def parse_dummy(
......@@ -292,14 +302,13 @@ class Documentation(CalendarEvent):
lesson_event.teachers,
)
obj, __ = cls.objects.get_or_create(
obj, __ = cls.objects.update_or_create(
datetime_start=datetime_start,
datetime_end=datetime_end,
course=course,
defaults=dict(subject=subject, amends=lesson_event),
amends=lesson_event,
defaults=dict(subject=subject, course=course),
)
obj.teachers.set(teachers.all())
obj.save()
# Create Participation Statuses
obj.touch()
......
......@@ -3,6 +3,7 @@ from datetime import datetime
from django.db.models import BooleanField, ExpressionWrapper, Q
import graphene
import graphene_django_optimizer
from aleksis.apps.chronos.models import LessonEvent
from aleksis.apps.cursus.models import Course
......@@ -13,10 +14,15 @@ from aleksis.core.models import Group, Person
from aleksis.core.schema.base import FilterOrderList
from aleksis.core.schema.group import GroupType
from aleksis.core.schema.person import PersonType
from aleksis.core.util.core_helpers import get_active_school_term, get_site_preferences, has_person
from aleksis.core.util.core_helpers import (
filter_active_school_term,
get_active_school_term,
get_site_preferences,
has_person,
)
from ..model_extensions import annotate_person_statistics_for_school_term
from ..models import Documentation, NewPersonalNote, ParticipationStatus
from ..models import Documentation, ExtraMark, NewPersonalNote, ParticipationStatus
from .absences import (
AbsencesForPersonsCreateMutation,
)
......@@ -47,7 +53,6 @@ from .statistics import StatisticsByPersonType
class Query(graphene.ObjectType):
documentations = FilterOrderList(DocumentationType)
documentations_by_course_id = FilterOrderList(
DocumentationType, course_id=graphene.ID(required=True)
)
......@@ -104,7 +109,7 @@ class Query(graphene.ObjectType):
)
)
)
return documentations
return graphene_django_optimizer.query(documentations, info)
def resolve_documentations_for_coursebook(
root,
......@@ -152,6 +157,10 @@ class Query(graphene.ObjectType):
}
)
school_term = get_active_school_term(info.context)
date_start = date_start if date_start > school_term.date_start else school_term.date_start
date_end = date_end if date_end < school_term.date_end else school_term.date_end
events = LessonEvent.get_single_events(
datetime.combine(date_start, datetime.min.time()),
datetime.combine(date_end, datetime.max.time()),
......@@ -167,7 +176,6 @@ class Query(graphene.ObjectType):
events,
incomplete,
absences_exist,
info.context,
)
return docs + dummies
......@@ -182,8 +190,10 @@ class Query(graphene.ObjectType):
else:
return []
school_term = get_active_school_term(info.context)
return (
Group.objects.for_current_school_term_or_all()
Group.objects.for_school_term(school_term)
.filter(
pk__in=Group.objects.filter(members=person)
.values_list("id", flat=True)
......@@ -211,6 +221,9 @@ class Query(graphene.ObjectType):
person = info.context.user.person
else:
return []
school_term = get_active_school_term(info.context)
return Course.objects.filter(
pk__in=(
Course.objects.filter(teachers=person)
......@@ -223,20 +236,22 @@ class Query(graphene.ObjectType):
)
)
)
).filter(groups__in=Group.objects.for_current_school_term_or_all())
).filter(groups__in=Group.objects.for_school_term(school_term))
@staticmethod
def resolve_absence_creation_persons(root, info, **kwargs):
if not info.context.user.has_perm("alsijil.register_absence"):
group_types = get_site_preferences()["alsijil__group_types_register_absence"]
school_term = get_active_school_term(info.context)
if group_types:
return Person.objects.filter(
member_of__in=Group.objects.filter(
member_of__in=Group.objects.for_school_term(school_term).filter(
owners=info.context.user.person, group_type__in=group_types
)
)
else:
return Person.objects.filter(member_of__owners=info.context.user.person)
qs = Person.objects.filter(member_of__owners=info.context.user.person)
return filter_active_school_term(info.context, qs, "member_of__school_term")
return Person.objects.all()
@staticmethod
......@@ -255,13 +270,18 @@ class Query(graphene.ObjectType):
person,
start,
end,
info.context,
)
lessons_for_person.append(LessonsForPersonType(id=person, lessons=docs + dummies))
return lessons_for_person
@staticmethod
def resolve_extra_marks(root, info, **kwargs):
if info.context.user.has_perm("alsijil.fetch_extramarks_rule"):
return ExtraMark.objects.all()
raise []
@staticmethod
def resolve_coursebook_absence_reasons(root, info, **kwargs):
if not info.context.user.has_perm("kolego.fetch_absencereasons_rule"):
......@@ -274,9 +294,12 @@ class Query(graphene.ObjectType):
if not info.context.user.has_perm("alsijil.view_person_statistics_rule", person):
return None
school_term = get_active_school_term(info.context)
return annotate_person_statistics_for_school_term(
Person.objects.filter(id=person.id), school_term
).first()
return graphene_django_optimizer.query(
annotate_person_statistics_for_school_term(
Person.objects.filter(id=person.id), school_term
).first(),
info,
)
@staticmethod
def resolve_participations_of_person(root, info, person):
......@@ -284,12 +307,15 @@ class Query(graphene.ObjectType):
if not info.context.user.has_perm("alsijil.view_person_statistics_rule", person):
return []
school_term = get_active_school_term(info.context)
return ParticipationStatus.objects.filter(
person=person,
absence_reason__isnull=False,
datetime_start__date__gte=school_term.date_start,
datetime_end__date__lte=school_term.date_end,
).order_by("-related_documentation__datetime_start")
return graphene_django_optimizer.query(
ParticipationStatus.objects.filter(
person=person,
absence_reason__isnull=False,
datetime_start__date__gte=school_term.date_start,
datetime_end__date__lte=school_term.date_end,
).order_by("-related_documentation__datetime_start"),
info,
)
@staticmethod
def resolve_personal_notes_for_person(root, info, person):
......@@ -297,13 +323,16 @@ class Query(graphene.ObjectType):
if not info.context.user.has_perm("alsijil.view_person_statistics_rule", person):
return []
school_term = get_active_school_term(info.context)
return NewPersonalNote.objects.filter(
person=person,
documentation__in=Documentation.objects.filter(
datetime_start__date__gte=school_term.date_start,
datetime_end__date__lte=school_term.date_end,
),
).order_by("-documentation__datetime_start")
return graphene_django_optimizer.query(
NewPersonalNote.objects.filter(
person=person,
documentation__in=Documentation.objects.filter(
datetime_start__date__gte=school_term.date_start,
datetime_end__date__lte=school_term.date_end,
),
).order_by("-documentation__datetime_start"),
info,
)
@staticmethod
def resolve_statistics_by_group(root, info, group):
......@@ -313,7 +342,9 @@ class Query(graphene.ObjectType):
school_term = get_active_school_term(info.context)
members = group.members.all()
return annotate_person_statistics_for_school_term(members, school_term, group=group)
return graphene_django_optimizer.query(
annotate_person_statistics_for_school_term(members, school_term, group=group), info
)
class Mutation(graphene.ObjectType):
......
from django.core.exceptions import PermissionDenied
import graphene
from graphene_django import bypass_get_queryset
from graphene_django.types import DjangoObjectType
from reversion import create_revision, set_comment, set_user
......@@ -32,7 +33,6 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
fields = (
"id",
"course",
"amends",
"subject",
"topic",
"homework",
......@@ -62,6 +62,14 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
old_id = graphene.ID(required=False)
@staticmethod
@bypass_get_queryset
def resolve_amends(root: Documentation, info, **kwargs):
if hasattr(root, "_amends_prefetched"):
return root._amends_prefetched
return root.amends
@staticmethod
@bypass_get_queryset
def resolve_teachers(root: Documentation, info, **kwargs):
if not str(root.pk).startswith("DUMMY") and hasattr(root, "teachers"):
return root.teachers
......@@ -100,6 +108,7 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
)
@staticmethod
@bypass_get_queryset
def resolve_participations(root: Documentation, info, **kwargs):
# A dummy documentation will not have any participations
if str(root.pk).startswith("DUMMY") or not hasattr(root, "participations"):
......@@ -112,6 +121,11 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
p for p in root.participations.all() if p.person == info.context.user.person
]
return []
# Annotate participations with prefetched documentation data for personal notes
for participation in root.participations.all():
participation._prefetched_documentation = root
return root.participations.all()
......
......@@ -23,12 +23,6 @@ class ExtraMarkType(
model = ExtraMark
fields = ("id", "short_name", "name", "colour_fg", "colour_bg", "show_in_coursebook")
@classmethod
def get_queryset(cls, queryset, info):
if info.context.user.has_perm("alsijil.fetch_extramarks_rule"):
return queryset
raise PermissionDenied()
class ExtraMarkBatchCreateMutation(BaseBatchCreateMutation):
class Meta:
......
......@@ -41,6 +41,12 @@ class ParticipationStatusType(
@staticmethod
def resolve_notes_with_extra_mark(root: ParticipationStatus, info, **kwargs):
if hasattr(root, "_prefetched_documentation"):
return [
p
for p in root._prefetched_documentation.personal_notes.all()
if p.person_id == root.person_id and p.extra_mark
]
return NewPersonalNote.objects.filter(
person=root.person,
documentation=root.related_documentation,
......@@ -49,6 +55,12 @@ class ParticipationStatusType(
@staticmethod
def resolve_notes_with_note(root: ParticipationStatus, info, **kwargs):
if hasattr(root, "_prefetched_documentation"):
return [
p
for p in root._prefetched_documentation.personal_notes.all()
if p.person_id == root.person_id and p.note
]
return NewPersonalNote.objects.filter(
person=root.person,
documentation=root.related_documentation,
......