Skip to content
Snippets Groups Projects
  1. Feb 12, 2025
  2. Feb 05, 2025
  3. Jan 13, 2025
  4. Jan 11, 2025
  5. Jan 08, 2025
  6. Jan 07, 2025
  7. Dec 04, 2024
  8. Dec 01, 2024
    • Jonathan Weth's avatar
      Merge branch 'fix/predicates' into 'master' · 23d23292
      Jonathan Weth authored
      Fix permissions
      
      See merge request !57
      23d23292
    • Jonathan Weth's avatar
      Fix permissions · 1496d4c0
      Jonathan Weth authored
      Verified
      1496d4c0
    • Jonathan Weth's avatar
      Merge branch 'fix/predicates' into 'master' · 3f043b81
      Jonathan Weth authored
      Fix predicates
      
      See merge request !56
      3f043b81
    • Jonathan Weth's avatar
      Fix predicates · 68e04df0
      Jonathan Weth authored
      Verified
      68e04df0
    • Jonathan Weth's avatar
      Merge branch '33-add-widget-to-persons-page-with-future-absences' into 'master' · a15cb370
      Jonathan Weth authored
      Resolve "Add widget to persons page with (future) absences"
      
      Closes #33
      
      See merge request !54
      a15cb370
    • 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
      Merge branch '32-only-allow-one-absence-at-any-point-of-time' into 'master' · 7bac57ff
      Jonathan Weth authored
      Resolve "Only allow one absence at any point of time"
      
      Closes #32
      
      See merge request !52
      7bac57ff
    • 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
  9. Nov 30, 2024
  10. Nov 29, 2024
  11. Nov 27, 2024
  12. Nov 22, 2024
  13. Nov 21, 2024
  14. Nov 20, 2024
Loading