From bbfa780288951673de661fafd9a84a7af57732c9 Mon Sep 17 00:00:00 2001
From: magicfelix <felix@felix-zauberer.de>
Date: Tue, 17 Sep 2024 10:53:29 +0200
Subject: [PATCH] Implement models

---
 aleksis/apps/maka/migrations/0001_initial.py | 248 +++++++++++++++++++
 aleksis/apps/maka/models/__init__.py         | 115 +++++++++
 2 files changed, 363 insertions(+)
 create mode 100644 aleksis/apps/maka/migrations/0001_initial.py

diff --git a/aleksis/apps/maka/migrations/0001_initial.py b/aleksis/apps/maka/migrations/0001_initial.py
new file mode 100644
index 0000000..b717138
--- /dev/null
+++ b/aleksis/apps/maka/migrations/0001_initial.py
@@ -0,0 +1,248 @@
+# Generated by Django 5.1.1 on 2024-09-17 17:15
+
+import colorfield.fields
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ("core", "0065_calendarevent_update_timezone_constraints"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="EffortType",
+            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)),
+                ("name", models.CharField(max_length=255, verbose_name="Name")),
+                (
+                    "color",
+                    colorfield.fields.ColorField(
+                        blank=True,
+                        default="",
+                        image_field=None,
+                        max_length=25,
+                        samples=None,
+                        verbose_name="Color",
+                    ),
+                ),
+                (
+                    "icon",
+                    models.CharField(
+                        blank=True, choices=[("", "")], max_length=50, verbose_name="Icon"
+                    ),
+                ),
+                ("default", models.BooleanField(verbose_name="Default")),
+            ],
+            options={
+                "abstract": False,
+            },
+        ),
+        migrations.CreateModel(
+            name="GradeSet",
+            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)),
+                ("name", models.CharField(max_length=255, unique=True, verbose_name="Name")),
+                (
+                    "groups",
+                    models.ManyToManyField(
+                        blank=True,
+                        null=True,
+                        related_name="grade_sets",
+                        to="core.group",
+                        verbose_name="Groups using this grade set",
+                    ),
+                ),
+            ],
+            options={
+                "abstract": False,
+            },
+        ),
+        migrations.CreateModel(
+            name="GradeChoice",
+            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)),
+                ("name", models.CharField(max_length=255, verbose_name="Name")),
+                ("value", models.CharField(max_length=255, verbose_name="Value")),
+                ("order", models.PositiveSmallIntegerField(verbose_name="Order")),
+                (
+                    "grade_set",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="grade_choices",
+                        to="maka.gradeset",
+                        verbose_name="Grade set",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "Grade choice",
+                "verbose_name_plural": "Grade choices",
+            },
+        ),
+        migrations.CreateModel(
+            name="Effort",
+            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)),
+                ("name", models.CharField(max_length=255, verbose_name="Name")),
+                ("weight", models.PositiveSmallIntegerField(default=1, verbose_name="Weight")),
+                (
+                    "grade_set",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="+",
+                        to="maka.gradeset",
+                        verbose_name="Grade set",
+                    ),
+                ),
+                (
+                    "group",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.PROTECT,
+                        related_name="efforts",
+                        to="core.group",
+                        verbose_name="Group",
+                    ),
+                ),
+                (
+                    "effort_type",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="efforts",
+                        to="maka.efforttype",
+                        verbose_name="Effort type",
+                    ),
+                ),
+            ],
+            options={
+                "abstract": False,
+            },
+        ),
+        migrations.CreateModel(
+            name="Grade",
+            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)),
+                (
+                    "person",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="grades",
+                        to="core.person",
+                        verbose_name="Person",
+                    ),
+                ),
+                (
+                    "grade",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="+",
+                        to="maka.gradechoice",
+                        verbose_name="Grade",
+                    ),
+                ),
+                (
+                    "effort",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="grades",
+                        to="maka.effort",
+                        verbose_name="Effort",
+                    ),
+                ),
+            ],
+            options={
+                "abstract": False,
+            },
+        ),
+        migrations.AddConstraint(
+            model_name="gradechoice",
+            constraint=models.UniqueConstraint(
+                fields=("grade_set", "name"), name="unique_gradeset_name"
+            ),
+        ),
+        migrations.AddConstraint(
+            model_name="gradechoice",
+            constraint=models.UniqueConstraint(
+                fields=("grade_set", "value"), name="unique_gradeset_value"
+            ),
+        ),
+    ]
diff --git a/aleksis/apps/maka/models/__init__.py b/aleksis/apps/maka/models/__init__.py
index e69de29..cd729e0 100644
--- a/aleksis/apps/maka/models/__init__.py
+++ b/aleksis/apps/maka/models/__init__.py
@@ -0,0 +1,115 @@
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+
+from colorfield.fields import ColorField
+
+from aleksis.core.mixins import ExtensibleModel
+from aleksis.core.models import Group, Person
+from aleksis.core.util.model_helpers import ICONS
+
+
+class GradeSet(ExtensibleModel):
+    """Set of grades."""
+    name = models.CharField(verbose_name=_("Name"), max_length=255, unique=True)
+    groups = models.ManyToManyField(
+        "core.Group",
+        verbose_name=_("Groups using this grade set"),
+        blank=True,
+        null=True,
+        related_name="grade_sets",
+    )
+
+
+class GradeChoice(ExtensibleModel):
+    """Single grade choice in a set of grades."""
+    grade_set = models.ForeignKey(
+        GradeSet,
+        on_delete=models.CASCADE,
+        verbose_name=_("Grade set"),
+        related_name="grade_choices",
+    )
+    name = models.CharField(verbose_name=_("Name"), max_length=255)
+    value = models.CharField(verbose_name=_("Value"), max_length=255)
+    order = models.PositiveSmallIntegerField(verbose_name=_("Order"))
+
+    class Meta:
+        verbose_name = _("Grade choice")
+        verbose_name_plural = _("Grade choices")
+        constraints = [
+            models.UniqueConstraint(
+                fields=["grade_set", "name"],
+                name="unique_gradeset_name",
+            ),
+            models.UniqueConstraint(
+                fields=["grade_set", "value"],
+                name="unique_gradeset_value",
+            ),
+        ]
+
+
+class EffortType(ExtensibleModel):
+    """Abstract type that can be rated."""
+    name = models.CharField(verbose_name=_("Name"), max_length=255)
+    color = ColorField(verbose_name=_("Color"), blank=True)
+    icon = models.CharField(
+        max_length=50, blank=True, choices=[("", "")] + ICONS, verbose_name=_("Icon")
+    )
+    default = models.BooleanField(verbose_name=_("Default"))
+
+    def save(self, *args, **kwargs):
+        if self.default:
+            # select all other active items
+            qs = EffortType.objects.filter(default=True)
+            # except self (if self already exists)
+            if self.pk:
+                qs = qs.exclude(pk=self.pk)
+            # and deactive them
+            qs.update(default=False)
+
+        super().save(*args, **kwargs)
+
+
+class Effort(ExtensibleModel):
+    """Concrete occurence of a effort type."""
+    name = models.CharField(verbose_name=_("Name"), max_length=255)
+    weight = models.PositiveSmallIntegerField(verbose_name=_("Weight"), default=1)
+    effort_type = models.ForeignKey(
+        EffortType,
+        on_delete=models.CASCADE,
+        verbose_name=_("Effort type"),
+        related_name="efforts",
+    )
+    group = models.ForeignKey(
+        Group,
+        on_delete=models.PROTECT,
+        verbose_name=_("Group"),
+        related_name="efforts",
+    )
+    grade_set = models.ForeignKey(
+        GradeSet,
+        on_delete=models.CASCADE,
+        verbose_name=_("Grade set"),
+        related_name="+",
+    )
+
+
+class Grade(ExtensibleModel):
+    """Grade of a person for a effort."""
+    person = models.ForeignKey(
+        Person,
+        on_delete=models.CASCADE,
+        verbose_name=_("Person"),
+        related_name="grades",
+    )
+    grade = models.ForeignKey(
+        GradeChoice,
+        on_delete=models.CASCADE,
+        verbose_name=_("Grade"),
+        related_name="+",
+    )
+    effort = models.ForeignKey(
+        Effort,
+        on_delete=models.CASCADE,
+        verbose_name=_("Effort"),
+        related_name="grades",
+    )
-- 
GitLab