Skip to content
Snippets Groups Projects
  1. Dec 01, 2024
    • Jonathan Weth's avatar
      Finish widget for planned absences · f778196c
      Jonathan Weth authored
      f778196c
    • Jonathan Weth's avatar
      Add first try of a planned absences widget · da41fc7e
      Jonathan Weth authored
      diff --git a/aleksis/apps/kolego/frontend/components/widgets/PlannedAbsencesForPersonWidget.vue b/aleksis/apps/kolego/frontend/components/widgets/PlannedAbsencesForPersonWidget.vue
      new file mode 100644
      index 0000000..14fd1d9
      --- /dev/null
      +++ b/aleksis/apps/kolego/frontend/components/widgets/PlannedAbsencesForPersonWidget.vue
      @@ -0,0 +1,97 @@
      +<template>
      +  <v-card>
      +    <v-card-title>
      +      {{ $t("kolego.widgets.planned_absences.title") }}
      +    </v-card-title>
      +    <c-r-u-d-iterator
      +      i18n-key="alsijil.coursebook.statistics"
      +      :gql-query="gqlQuery"
      +      :gql-additional-query-args="gqlQueryArgs"
      +      :enable-create="false"
      +      :enable-edit="false"
      +      :enable-search="false"
      +      :items-per-page="-1"
      +      :elevated="false"
      +    >
      +      <template #default="{ items }">
      +        <v-list>
      +          <v-list-item v-for="item in items" :key="item.id">
      +            <v-list-item-content>
      +              <v-list-item-title>
      +                <template
      +                  v-if="
      +                    $parseISODate(item.datetimeStart).hasSame(
      +                      $parseISODate(item.datetimeEnd),
      +                      'day',
      +                    )
      +                  "
      +                >
      +                  <time :datetime="item.datetimeStart" class="text-no-wrap">
      +                    {{ $d($parseISODate(item.datetimeStart), "short") }},
      +                  </time>
      +
      +                  <time :datetime="item.datetimeStart" class="text-no-wrap">
      +                    {{ $d($parseISODate(item.datetimeStart), "shortTime") }}
      +                  </time>
      +                  <span>-</span>
      +                  <time :datetime="item.datetimeEnd" class="text-no-wrap">
      +                    {{ $d($parseISODate(item.datetimeEnd), "shortTime") }}
      +                  </time>
      +                </template>
      +                <template v-else>
      +                  <time :datetime="item.datetimeStart" class="text-no-wrap">
      +                    {{ $d($parseISODate(item.datetimeStart), "shortDateTime") }}
      +                  </time>
      +                  <span>-</span>
      +                  <time :datetime="item.datetimeEnd" class="text-no-wrap">
      +                    {{ $d($parseISODate(item.datetimeEnd), "shortDateTime") }}
      +                  </time>
      +                </template>
      +
      +                <absence-reason-chip
      +                  :absence-reason="item.reason"
      +                  class="float-right"
      +                  small
      +                />
      +              </v-list-item-title>
      +              <v-list-item-subtitle>
      +                {{ item.comment }}
      +              </v-list-item-subtitle>
      +            </v-list-item-content>
      +            <v-list-item-icon>
      +              <v-btn icon color="red"
      +                ><v-icon>mdi-delete-outline</v-icon></v-btn
      +              >
      +            </v-list-item-icon>
      +          </v-list-item>
      +        </v-list>
      +      </template>
      +    </c-r-u-d-iterator>
      +  </v-card>
      +</template>
      +
      +<script>
      +import personOverviewCardMixin from "aleksis.core/mixins/personOverviewCardMixin.js";
      +import { absences } from "./absences.graphql";
      +import CRUDIterator from "aleksis.core/components/generic/CRUDIterator.vue";
      +import AbsenceReasonChip from "../AbsenceReasonChip.vue";
      +export default {
      +  name: "PlannedAbsencesForPersonWidget",
      +  mixins: [personOverviewCardMixin],
      +  components: { CRUDIterator, AbsenceReasonChip },
      +  data() {
      +    return {
      +      gqlQuery: absences,
      +    };
      +  },
      +  computed: {
      +    gqlQueryArgs() {
      +      return {
      +        person: this.person.id,
      +      };
      +    },
      +  },
      +};
      +</script>
      +
      +<style scoped></style>
      diff --git a/aleksis/apps/kolego/frontend/components/widgets/absences.graphql b/aleksis/apps/kolego/frontend/components/widgets/absences.graphql
      new file mode 100644
      index 0000000..652648d
      --- /dev/null
      +++ b/aleksis/apps/kolego/frontend/components/widgets/absences.graphql
      @@ -0,0 +1,27 @@
      +query absences($orderBy: [String], $filters: JSONString, $person: ID!) {
      +  items: plannedAbsencesForPerson(
      +    orderBy: $orderBy
      +    filters: $filters
      +    person: $person
      +  ) {
      +    id
      +    person {
      +      id
      +      fullName
      +    }
      +    reason {
      +      id
      +      shortName
      +      name
      +      colour
      +      default
      +    }
      +    comment
      +    datetimeStart
      +    datetimeEnd
      +    dateStart
      +    dateEnd
      +    canEdit
      +    canDelete
      +  }
      +}
      diff --git a/aleksis/apps/kolego/frontend/index.js b/aleksis/apps/kolego/frontend/index.js
      index 260b3d9..aae17e7 100644
      --- a/aleksis/apps/kolego/frontend/index.js
      +++ b/aleksis/apps/kolego/frontend/index.js
      @@ -1,3 +1,19 @@
      +export const collectionItems = {
      +  corePersonWidgets: [
      +    {
      +      key: "core-person-widgets",
      +      component: () =>
      +        import("./components/widgets/PlannedAbsencesForPersonWidget.vue"),
      +      shouldDisplay: () => true,
      +      colProps: {
      +        cols: 12,
      +        md: 6,
      +        lg: 4,
      +      },
      +    },
      +  ],
      +};
      +
       export default {
         meta: {
           inMenu: true,
      diff --git a/aleksis/apps/kolego/preferences.py b/aleksis/apps/kolego/preferences.py
      new file mode 100644
      index 0000000..b8748c0
      --- /dev/null
      +++ b/aleksis/apps/kolego/preferences.py
      @@ -0,0 +1,24 @@
      +from django.utils.translation import gettext_lazy as _
      +
      +from dynamic_preferences.preferences import Section
      +from dynamic_preferences.types import (
      +    ModelMultipleChoicePreference,
      +)
      +
      +from aleksis.core.models import GroupType
      +from aleksis.core.registries import site_preferences_registry
      +
      +kolego = Section("kolego", verbose_name=_("Absences"))
      +
      +
      +@site_preferences_registry.register
      +class GroupTypesViewPersonAbsences(ModelMultipleChoicePreference):
      +    section = kolego
      +    name = "group_types_view_person_absences"
      +    required = False
      +    default = []
      +    model = GroupType
      +    verbose_name = _(
      +        "User is allowed to view (planned) absences for members "
      +        "of groups the user is an owner of with these group types"
      +    )
      diff --git a/aleksis/apps/kolego/rules.py b/aleksis/apps/kolego/rules.py
      index 535b0f3..07cfd49 100644
      --- a/aleksis/apps/kolego/rules.py
      +++ b/aleksis/apps/kolego/rules.py
      @@ -8,6 +8,8 @@ from aleksis.core.util.predicates import (
           has_person,
       )
      
      +from .util.predicates import can_view_absences_for_person
      +
       view_absences_predicate = has_person & (
           has_global_perm("kolego.view_absence") | has_any_object("kolego.view_absence", Absence)
       )
      @@ -89,3 +91,8 @@ view_menu_predicate = has_person & (
           view_absences_predicate | view_absencereasons_predicate | view_absencereasontags_predicate
       )
       rules.add_perm("kolego.view_menu_rule", view_menu_predicate)
      +
      +view_person_absences_predicate = has_person & (
      +    has_global_perm("kolego.view_absence") | can_view_absences_for_person
      +)
      +rules.add_perm("kolego.view_person_absences_rule", view_person_absences_predicate)
      diff --git a/aleksis/apps/kolego/schema/__init__.py b/aleksis/apps/kolego/schema/__init__.py
      index bf93f42..961c4a6 100644
      --- a/aleksis/apps/kolego/schema/__init__.py
      +++ b/aleksis/apps/kolego/schema/__init__.py
      @@ -1,4 +1,5 @@
       from django.apps import apps
      +from django.utils import timezone
       from django.db.models import QuerySet
      
       import graphene
      @@ -6,6 +7,7 @@ import graphene_django_optimizer
       from guardian.shortcuts import get_objects_for_user
      
       from aleksis.apps.kolego.models.absence import Absence, AbsenceReason, AbsenceReasonTag
      +from aleksis.core.models import Person
       from aleksis.core.schema.base import FilterOrderList
       from aleksis.core.util.core_helpers import filter_active_school_term_by_date
      
      @@ -28,6 +30,7 @@ from .absence import (
       class Query(graphene.ObjectType):
           app_name = graphene.String()
           absences = FilterOrderList(AbsenceType)
      +    planned_absences_for_person = FilterOrderList(AbsenceType, person=graphene.ID(required=True))
           absence_reasons = FilterOrderList(AbsenceReasonType)
           absence_reason_tags = FilterOrderList(AbsenceReasonTagType)
           all_absence_reason_tags = FilterOrderList(AbsenceReasonTagType)
      @@ -47,6 +50,21 @@ class Query(graphene.ObjectType):
                   info,
               )
      
      +    @staticmethod
      +    def resolve_planned_absences_for_person(root, info, person: str, **kwargs):
      +        person = Person.objects.get(pk=person)
      +        if not info.context.user.has_perm("kolego.view_person_absences_rule", person):
      +            return []
      +        return graphene_django_optimizer.query(
      +            filter_active_school_term_by_date(
      +                info.context,
      +                Absence.objects.filter(person=person, datetime_end__gte=timezone.now()).order_by(
      +                    "datetime_start"
      +                ),
      +            ),
      +            info,
      +        )
      +
           @staticmethod
           def resolve_absencereasons(root, info, **kwargs) -> QuerySet:
               if not info.context.user.has_perm("kolego.fetch_absencereasons_rule"):
      diff --git a/aleksis/apps/kolego/util/predicates.py b/aleksis/apps/kolego/util/predicates.py
      new file mode 100644
      index 0000000..7db1702
      --- /dev/null
      +++ b/aleksis/apps/kolego/util/predicates.py
      @@ -0,0 +1,16 @@
      +from django.contrib.auth.models import User
      +
      +from rules import predicate
      +
      +from aleksis.core.models import Person
      +from aleksis.core.util.core_helpers import get_site_preferences
      +
      +
      +@predicate
      +def can_view_absences_for_person(user: User, obj: Person) -> bool:
      +    """Predicate for viewing absences of a person."""
      +    group_types = get_site_preferences()["alsijil__group_types_view_person_absences"]
      +    qs = obj.member_of.filter(owners=user.person)
      +    if not group_types.exists():
      +        return False
      +    return qs.filter(group_type__in=group_types).exists()
      da41fc7e
  2. Nov 22, 2024
  3. Nov 20, 2024
  4. Nov 06, 2024
  5. Oct 10, 2024
  6. Sep 18, 2024
  7. Aug 07, 2024
  8. Aug 01, 2024
  9. Jul 22, 2024
  10. Jul 18, 2024
  11. Jul 16, 2024
  12. Jun 17, 2024
  13. Jun 14, 2024
  14. Jun 12, 2024
  15. Jun 06, 2024
  16. May 27, 2024
  17. May 21, 2024
  18. May 16, 2024
  19. Feb 01, 2024
Loading