Skip to content
Snippets Groups Projects
  1. Dec 01, 2024
    • Jonathan Weth's avatar
      Finish widget for planned absences · f778196c
      Jonathan Weth authored
      Verified
      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()
      Verified
      da41fc7e
    • Jonathan Weth's avatar
      Verified
      a7a6b029
    • Jonathan Weth's avatar
      Refactor overlapping logic · 812c00fe
      Jonathan Weth authored
      Verified
      812c00fe
    • Hangzhi Yu's avatar
      Reformat · c4ddcca2
      Hangzhi Yu authored
      c4ddcca2
    • Hangzhi Yu's avatar
    • Hangzhi Yu's avatar
      Correctly return modified objects in mutation · 8ced4059
      Hangzhi Yu authored
      8ced4059
  2. Nov 30, 2024
  3. Nov 29, 2024
  4. Nov 27, 2024
  5. Nov 22, 2024
  6. Nov 21, 2024
  7. Nov 20, 2024
  8. Nov 06, 2024
  9. Nov 03, 2024
  10. Oct 10, 2024
  11. Sep 18, 2024
  12. Aug 18, 2024
  13. Aug 17, 2024
  14. Aug 07, 2024
  15. Aug 01, 2024
  16. Jul 22, 2024
  17. Jul 18, 2024
  18. Jul 16, 2024
Loading