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
    • Hangzhi Yu's avatar
      Reformat · c4ddcca2
      Hangzhi Yu authored
      c4ddcca2
    • 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 21, 2024
  5. Nov 20, 2024
  6. Nov 03, 2024
  7. Aug 17, 2024
  8. Jul 16, 2024
  9. Jun 17, 2024
  10. May 27, 2024
  11. May 21, 2024
  12. May 16, 2024
  13. Feb 01, 2024
  14. Jun 05, 2023
  15. May 27, 2023
Loading