Skip to content
Snippets Groups Projects
Verified Commit a14136a3 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Introduce tags for absence reasons

parent a44bc14f
No related branches found
No related tags found
1 merge request!32Resolve "Create tags for absence reasons"
Pipeline #191665 failed
Showing
with 411 additions and 8 deletions
<script setup>
import InlineCRUDList from "aleksis.core/components/generic/InlineCRUDList.vue";
</script>
<template>
<v-container>
<inline-c-r-u-d-list
:headers="headers"
:i18n-key="i18nKey"
create-item-i18n-key="kolego.absence_reason_tag.create"
:gql-query="gqlQuery"
:gql-create-mutation="gqlCreateMutation"
:gql-patch-mutation="gqlPatchMutation"
:gql-delete-mutation="gqlDeleteMutation"
:default-item="defaultItem"
>
<!-- eslint-disable-next-line vue/valid-v-slot -->
<template #name.field="{ attrs, on }">
<div aria-required="true">
<v-text-field
v-bind="attrs"
v-on="on"
:rules="$rules().required.build()"
/>
</div>
</template>
<!-- eslint-disable-next-line vue/valid-v-slot -->
<template #shortName.field="{ attrs, on }">
<div aria-required="true">
<v-text-field
v-bind="attrs"
v-on="on"
:rules="$rules().required.build()"
/>
</div>
</template>
</inline-c-r-u-d-list>
</v-container>
</template>
<script>
import formRulesMixin from "aleksis.core/mixins/formRulesMixin.js";
import {
absenceReasonTags,
createAbsenceReasonTags,
deleteAbsenceReasonTags,
updateAbsenceReasonTags,
} from "./absenceReasonTags.graphql";
export default {
name: "AbsenceReasonTags",
mixins: [formRulesMixin],
data() {
return {
headers: [
{
text: this.$t("kolego.absence_reason_tag.short_name"),
value: "shortName",
},
{
text: this.$t("kolego.absence_reason_tag.name"),
value: "name",
},
],
i18nKey: "kolego.absence_reason_tag",
gqlQuery: absenceReasonTags,
gqlCreateMutation: createAbsenceReasonTags,
gqlPatchMutation: updateAbsenceReasonTags,
gqlDeleteMutation: deleteAbsenceReasonTags,
defaultItem: {
shortName: "",
name: "",
},
};
},
};
</script>
<style scoped></style>
<script setup>
import ForeignKeyField from "aleksis.core/components/generic/forms/ForeignKeyField.vue";
</script>
<template>
<foreign-key-field
v-bind="$attrs"
v-on="$listeners"
:fields="headers"
create-item-i18n-key="kolego.absence_reason_tag.create"
:gql-query="gqlQuery"
:gql-create-mutation="gqlCreateMutation"
:gql-patch-mutation="{}"
:default-item="defaultItem"
multiple
chips
>
<!-- eslint-disable-next-line vue/valid-v-slot -->
<template #name.field="{ attrs, on }">
<div aria-required="true">
<v-text-field
v-bind="attrs"
v-on="on"
:rules="$rules().required.build()"
/>
</div>
</template>
<!-- eslint-disable-next-line vue/valid-v-slot -->
<template #shortName.field="{ attrs, on }">
<div aria-required="true">
<v-text-field
v-bind="attrs"
v-on="on"
:rules="$rules().required.build()"
/>
</div>
</template>
</foreign-key-field>
</template>
<script>
import {
allAbsenceReasonTags,
createAbsenceReasonTags,
} from "./absenceReasonTags.graphql";
import formRulesMixin from "aleksis.core/mixins/formRulesMixin.js";
export default {
name: "AbsenceReasonTagsField",
mixins: [formRulesMixin],
data() {
return {
headers: [
{
text: this.$t("kolego.absence_reason_tag.short_name"),
value: "shortName",
},
{
text: this.$t("kolego.absence_reason_tag.name"),
value: "name",
},
],
i18nKey: "kolego.absence_reason_tag",
gqlQuery: allAbsenceReasonTags,
gqlCreateMutation: createAbsenceReasonTags,
defaultItem: {
name: "",
shortName: "",
},
};
},
};
</script>
<style scoped></style>
<script setup>
import ColorField from "aleksis.core/components/generic/forms/ColorField.vue";
import InlineCRUDList from "aleksis.core/components/generic/InlineCRUDList.vue";
import AbsenceReasonTagsField from "./AbsenceReasonTagsField.vue";
</script>
<template>
......@@ -69,6 +70,15 @@ import InlineCRUDList from "aleksis.core/components/generic/InlineCRUDList.vue";
persistent-hint
/>
</template>
<template #tags="{ item }">
<span v-if="item.tags.length == 0"></span>
<v-chip v-for="tag in item.tags" :key="tag.id">{{ tag.name }}</v-chip>
</template>
<!-- eslint-disable-next-line vue/valid-v-slot -->
<template #tags.field="{ attrs, on }">
<absence-reason-tags-field v-bind="attrs" v-on="on" />
</template>
</inline-c-r-u-d-list>
</v-container>
</template>
......@@ -104,6 +114,10 @@ export default {
text: this.$t("kolego.absence_reason.default"),
value: "default",
},
{
text: this.$t("kolego.absence_reason.tags"),
value: "tags",
},
],
i18nKey: "kolego.absence_reason",
gqlQuery: absenceReasons,
......
query absenceReasonTags($orderBy: [String], $filters: JSONString) {
items: absenceReasonTags(orderBy: $orderBy, filters: $filters) {
id
shortName
name
canEdit
canDelete
}
}
query allAbsenceReasonTags($orderBy: [String], $filters: JSONString) {
items: allAbsenceReasonTags(orderBy: $orderBy, filters: $filters) {
id
shortName
name
canEdit
canDelete
}
}
mutation createAbsenceReasonTags($input: [BatchCreateAbsenceReasonTagInput]!) {
createAbsenceReasonTags(input: $input) {
items: absenceReasonTags {
id
shortName
name
canEdit
canDelete
}
}
}
mutation deleteAbsenceReasonTags($ids: [ID]!) {
deleteAbsenceReasonTags(ids: $ids) {
deletionCount
}
}
mutation updateAbsenceReasonTags($input: [BatchPatchAbsenceReasonTagInput]!) {
updateAbsenceReasonTags(input: $input) {
items: absenceReasonTags {
id
shortName
name
canEdit
canDelete
}
}
}
......@@ -7,6 +7,11 @@ query absenceReasons($orderBy: [String], $filters: JSONString) {
default
canEdit
canDelete
tags {
id
name
shortName
}
}
}
......@@ -20,6 +25,11 @@ mutation createAbsenceReasons($input: [BatchCreateAbsenceReasonInput]!) {
default
canEdit
canDelete
tags {
id
name
shortName
}
}
}
}
......@@ -40,6 +50,11 @@ mutation updateAbsenceReasons($input: [BatchPatchAbsenceReasonInput]!) {
default
canEdit
canDelete
tags {
id
name
shortName
}
}
}
}
......@@ -31,5 +31,17 @@ export default {
permission: "kolego.view_absencereasons_rule",
},
},
{
path: "absence_reason_tags",
component: () => import("./components/AbsenceReasonTags.vue"),
name: "kolego.absence_reason_tags",
meta: {
inMenu: true,
titleKey: "kolego.absence_reason_tag.menu_title",
icon: "mdi-tag-multiple-outline",
iconActive: "mdi-tag-multiple",
permission: "kolego.view_absencereasontags_rule",
},
},
],
};
......@@ -17,7 +17,15 @@
"colour": "Colour",
"default": "Default Absence Reason",
"default_helptext": "Will disable previous default when enabled",
"present": "Present"
"present": "Present",
"tags": "Tags"
},
"absence_reason_tag": {
"menu_title": "Absence Reason Tags",
"title_plural": "Absence Reason Tags",
"create": "Create absence reason tag",
"short_name": "Short name",
"name": "Name"
}
}
}
# Generated by Django 4.2.13 on 2024-07-16 11:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('kolego', '0003_refactor_absence'),
]
operations = [
migrations.CreateModel(
name='AbsenceReasonTag',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('managed_by_app_label', models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance')),
('extended_data', models.JSONField(default=dict, editable=False)),
('short_name', models.CharField(max_length=255, unique=True, verbose_name='Short name')),
('name', models.CharField(max_length=255, verbose_name='Name')),
],
options={
'verbose_name': 'Absence reason tag',
'verbose_name_plural': 'Absence reason tags',
},
),
migrations.AddField(
model_name='absencereason',
name='tags',
field=models.ManyToManyField(blank=True, to='kolego.absencereasontag', verbose_name='Tags'),
),
]
from .absence import Absence, AbsenceReason # noqa: F401
from .absence import Absence, AbsenceReason, AbsenceReasonTag # noqa: F401
......@@ -12,6 +12,21 @@ from aleksis.core.models import FreeBusy
from ..managers import AbsenceQuerySet
class AbsenceReasonTag(ExtensibleModel):
short_name = models.CharField(verbose_name=_("Short name"), max_length=255, unique=True)
name = models.CharField(verbose_name=_("Name"), max_length=255)
def __str__(self):
if self.name:
return f"{self.short_name} ({self.name})"
else:
return self.short_name
class Meta:
verbose_name = _("Absence reason tag")
verbose_name_plural = _("Absence reason tags")
class AbsenceReason(ExtensibleModel):
short_name = models.CharField(verbose_name=_("Short name"), max_length=255, unique=True)
name = models.CharField(verbose_name=_("Name"), max_length=255)
......@@ -29,6 +44,10 @@ class AbsenceReason(ExtensibleModel):
default = models.BooleanField(verbose_name=_("Default Reason"), default=False)
tags = models.ManyToManyField(
AbsenceReasonTag, blank=True, verbose_name=_("Tags"), related_name="absence_reasons"
)
def __str__(self):
if self.name:
return f"{self.short_name} ({self.name})"
......
import rules
from aleksis.apps.kolego.models.absence import Absence, AbsenceReason
from aleksis.apps.kolego.models.absence import Absence, AbsenceReason, AbsenceReasonTag
from aleksis.core.util.predicates import (
has_any_object,
has_global_perm,
......@@ -58,5 +58,34 @@ delete_absencereason_predicate = has_person & (
)
rules.add_perm("kolego.delete_absencereason_rule", delete_absencereason_predicate)
view_menu_predicate = has_person & (view_absences_predicate | view_absencereasons_predicate)
view_absencereasontags_predicate = has_person & (
has_global_perm("kolego.view_absencereasontag")
| has_any_object("kolego.view_absencereasontag", AbsenceReasonTag)
)
rules.add_perm("kolego.view_absencereasontags_rule", view_absencereasontags_predicate)
view_absencereasontag_predicate = has_person & (
has_global_perm("kolego.view_absencereasontag")
| has_object_perm("kolego.view_absencereasontag")
)
rules.add_perm("kolego.view_absencereasontag_rule", view_absencereasontag_predicate)
create_absencereasontag_predicate = has_person & (has_global_perm("kolego.add_absencereasontag"))
rules.add_perm("kolego.create_absencereasontag_rule", create_absencereasontag_predicate)
edit_absencereasontag_predicate = has_person & (
has_global_perm("kolego.change_absencereasontag")
| has_object_perm("kolego.change_absencereasontag")
)
rules.add_perm("kolego.edit_absencereasontag_rule", edit_absencereasontag_predicate)
delete_absencereasontag_predicate = has_person & (
has_global_perm("kolego.delete_absencereasontag")
| has_object_perm("kolego.delete_absencereasontag")
)
rules.add_perm("kolego.delete_absencereasontag_rule", delete_absencereasontag_predicate)
view_menu_predicate = has_person & (
view_absences_predicate | view_absencereasons_predicate | view_absencereasontags_predicate
)
rules.add_perm("kolego.view_menu_rule", view_menu_predicate)
......@@ -2,6 +2,7 @@ from django.apps import apps
import graphene
from aleksis.apps.kolego.models.absence import AbsenceReasonTag
from aleksis.core.schema.base import FilterOrderList
from .absence import (
......@@ -11,6 +12,10 @@ from .absence import (
AbsenceReasonBatchCreateMutation,
AbsenceReasonBatchDeleteMutation,
AbsenceReasonBatchPatchMutation,
AbsenceReasonTagBatchCreateMutation,
AbsenceReasonTagBatchDeleteMutation,
AbsenceReasonTagBatchPatchMutation,
AbsenceReasonTagType,
AbsenceReasonType,
AbsenceType,
)
......@@ -20,10 +25,15 @@ class Query(graphene.ObjectType):
app_name = graphene.String()
absences = FilterOrderList(AbsenceType)
absence_reasons = FilterOrderList(AbsenceReasonType)
absence_reason_tags = FilterOrderList(AbsenceReasonTagType)
all_absence_reason_tags = FilterOrderList(AbsenceReasonTagType)
def resolve_app_name(root, info, **kwargs) -> str:
return apps.get_app_config("kolego").verbose_name
def resolve_all_absence_reason_tags(root, info, **kwargs):
return AbsenceReasonTag.objects.managed_and_unmanaged()
class Mutation(graphene.ObjectType):
create_absences = AbsenceBatchCreateMutation.Field()
......@@ -33,3 +43,7 @@ class Mutation(graphene.ObjectType):
create_absence_reasons = AbsenceReasonBatchCreateMutation.Field()
delete_absence_reasons = AbsenceReasonBatchDeleteMutation.Field()
update_absence_reasons = AbsenceReasonBatchPatchMutation.Field()
create_absence_reason_tags = AbsenceReasonTagBatchCreateMutation.Field()
delete_absence_reason_tags = AbsenceReasonTagBatchDeleteMutation.Field()
update_absence_reason_tags = AbsenceReasonTagBatchPatchMutation.Field()
from datetime import timezone
from typing import Iterable, Union
from django.conf import settings
......@@ -14,18 +15,35 @@ from aleksis.core.schema.base import (
PermissionsTypeMixin,
)
from ..models import Absence, AbsenceReason
from ..models import Absence, AbsenceReason, AbsenceReasonTag
class AbsenceReasonTagType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType):
class Meta:
model = AbsenceReasonTag
fields = ("id", "short_name", "name")
filter_fields = {
"short_name": ["icontains", "exact"],
"name": ["icontains", "exact"],
}
@classmethod
def get_queryset(cls, queryset, info):
return get_objects_for_user(info.context.user, "kolego.view_absencereasontag", queryset)
class AbsenceReasonType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType):
class Meta:
model = AbsenceReason
fields = ("id", "short_name", "name", "colour", "default")
fields = ("id", "short_name", "name", "colour", "default", "tags")
filter_fields = {
"short_name": ["icontains", "exact"],
"name": ["icontains", "exact"],
}
def resolve_tags(root, info, **kwargs):
return root.tags.managed_and_unmanaged().filter(absence_reasons=root)
@classmethod
def get_queryset(cls, queryset, info):
if not info.context.user.has_perm("kolego.fetch_absencereasons_rule"):
......@@ -129,10 +147,18 @@ class AbsenceBatchPatchMutation(BaseBatchPatchMutation):
class AbsenceReasonBatchCreateMutation(BaseBatchCreateMutation):
class Meta:
model = AbsenceReason
fields = ("short_name", "name", "colour", "default")
fields = ("short_name", "name", "colour", "default", "tags")
optional_fields = ("name",)
permissions = ("kolego.create_absencereason_rule",)
@classmethod
def get_all_objs(cls, Model, ids: Iterable[Union[str, int]]):
return list(
Model.objects.managed_and_unmanaged().filter(
pk__in=[cls.resolve_id(id_) for id_ in ids]
)
)
class AbsenceReasonBatchDeleteMutation(BaseBatchDeleteMutation):
class Meta:
......@@ -143,5 +169,34 @@ class AbsenceReasonBatchDeleteMutation(BaseBatchDeleteMutation):
class AbsenceReasonBatchPatchMutation(BaseBatchPatchMutation):
class Meta:
model = AbsenceReason
fields = ("id", "short_name", "name", "colour", "default")
fields = ("id", "short_name", "name", "colour", "default", "tags")
permissions = ("kolego.edit_absencereason_rule",)
@classmethod
def get_all_objs(cls, Model, ids: Iterable[Union[str, int]]):
return list(
Model.objects.managed_and_unmanaged().filter(
pk__in=[cls.resolve_id(id_) for id_ in ids]
)
)
class AbsenceReasonTagBatchCreateMutation(BaseBatchCreateMutation):
class Meta:
model = AbsenceReasonTag
fields = ("short_name", "name")
optional_fields = ("name",)
permissions = ("kolego.create_absencereasontag_rule",)
class AbsenceReasonTagBatchDeleteMutation(BaseBatchDeleteMutation):
class Meta:
model = AbsenceReasonTag
permissions = ("kolego.delete_absencereasontag_rule",)
class AbsenceReasonTagBatchPatchMutation(BaseBatchPatchMutation):
class Meta:
model = AbsenceReasonTag
fields = ("id", "short_name", "name")
permissions = ("kolego.edit_absencereasontag_rule",)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment