From a4935700b1e882f20d00cea6043965787b991f19 Mon Sep 17 00:00:00 2001
From: George Katsikas <giorgakis.katsikas@gmail.com>
Date: Fri, 15 Mar 2024 16:39:22 +0100
Subject: [PATCH] add notes and pins models

related to #164
---
 scipost_django/pins/admin.py                  |  25 ++++-
 .../pins/migrations/0001_initial.py           | 103 ++++++++++++++++++
 scipost_django/pins/models.py                 |  61 ++++++++++-
 3 files changed, 187 insertions(+), 2 deletions(-)
 create mode 100644 scipost_django/pins/migrations/0001_initial.py

diff --git a/scipost_django/pins/admin.py b/scipost_django/pins/admin.py
index 6a7921658..7b6608d52 100644
--- a/scipost_django/pins/admin.py
+++ b/scipost_django/pins/admin.py
@@ -3,4 +3,27 @@ __license__ = "AGPL v3"
 
 from django.contrib import admin
 
-# Register your models here.
+from pins.models import Note
+
+
+@admin.register(Note)
+class NoteAdmin(admin.ModelAdmin):
+    list_display = ("id", "regarding__object", "title", "author", "created", "modified")
+    list_filter = ("created", "modified", "visibility", "regarding_content_type")
+    search_fields = ("title", "author__username")
+    date_hierarchy = "created"
+    ordering = ("-created",)
+    readonly_fields = ("created", "modified", "regarding")
+    fields = (
+        "title",
+        "description",
+        "regarding",
+        "visibility",
+        "author",
+        "created",
+        "modified",
+    )
+    autocomplete_fields = ("author",)
+
+    def regarding__object(self, obj):
+        return str(obj.regarding)
diff --git a/scipost_django/pins/migrations/0001_initial.py b/scipost_django/pins/migrations/0001_initial.py
new file mode 100644
index 000000000..bbfa7c661
--- /dev/null
+++ b/scipost_django/pins/migrations/0001_initial.py
@@ -0,0 +1,103 @@
+# Generated by Django 4.2.10 on 2024-03-15 15:53
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        (
+            "scipost",
+            "0041_alter_remark_contributor_alter_remark_recommendation_and_more",
+        ),
+        ("contenttypes", "0002_remove_content_type_name"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="Note",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("title", models.CharField(max_length=255)),
+                ("description", models.TextField()),
+                (
+                    "visibility",
+                    models.CharField(
+                        choices=[
+                            ("self", "Private"),
+                            ("internal", "Internal"),
+                            ("public", "Public"),
+                        ],
+                        default="self",
+                        max_length=10,
+                    ),
+                ),
+                (
+                    "regarding_object_id",
+                    models.PositiveIntegerField(blank=True, null=True),
+                ),
+                ("created", models.DateTimeField(auto_now_add=True)),
+                ("modified", models.DateTimeField(auto_now=True)),
+                (
+                    "author",
+                    models.ForeignKey(
+                        null=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="scipost.contributor",
+                    ),
+                ),
+                (
+                    "regarding_content_type",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="contenttypes.contenttype",
+                    ),
+                ),
+            ],
+            options={
+                "ordering": ("-created",),
+                "default_related_name": "notes",
+            },
+        ),
+        migrations.CreateModel(
+            name="Pin",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("due_date", models.DateField(blank=True, null=True)),
+                ("created", models.DateTimeField(auto_now_add=True)),
+                (
+                    "note",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE, to="pins.note"
+                    ),
+                ),
+                (
+                    "user",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to=settings.AUTH_USER_MODEL,
+                    ),
+                ),
+            ],
+        ),
+    ]
diff --git a/scipost_django/pins/models.py b/scipost_django/pins/models.py
index dbbe360b1..88a7ff9ed 100644
--- a/scipost_django/pins/models.py
+++ b/scipost_django/pins/models.py
@@ -2,5 +2,64 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 from django.db import models
+from django.contrib.contenttypes.fields import GenericForeignKey
 
-# Create your models here.
+
+class Note(models.Model):
+    """
+    A note regarding a (generic) object.
+    """
+
+    class Meta:
+        ordering = ("-created",)
+        default_related_name = "notes"
+
+    VISIBILITY_PRIVATE = "self"
+    VISIBILITY_INTERNAL = "internal"
+    VISIBILITY_PUBLIC = "public"  # Notes for everyone
+    VISIBILITY_CHOICES = (
+        (VISIBILITY_PRIVATE, "Private"),  # For creator only
+        (VISIBILITY_INTERNAL, "Internal"),  # For group of object managers
+        (VISIBILITY_PUBLIC, "Public"),  # For everyone
+    )
+
+    title = models.CharField(max_length=255)
+    description = models.TextField()
+    visibility = models.CharField(
+        max_length=10,
+        default=VISIBILITY_PRIVATE,
+        choices=VISIBILITY_CHOICES,
+    )
+
+    regarding_content_type = models.ForeignKey(
+        "contenttypes.ContentType",
+        on_delete=models.CASCADE,
+    )
+    regarding_object_id = models.PositiveIntegerField(blank=True, null=True)
+    regarding = GenericForeignKey("regarding_content_type", "regarding_object_id")
+
+    author = models.ForeignKey(
+        "scipost.Contributor",
+        on_delete=models.CASCADE,
+        null=True,
+    )
+    created = models.DateTimeField(auto_now_add=True)
+    modified = models.DateTimeField(auto_now=True)
+
+    def __str__(self):
+        return self.title
+
+
+class Pin(models.Model):
+    """
+    A pin associates a note with a user, making it visible in their task list.
+    """
+
+    note = models.ForeignKey(Note, on_delete=models.CASCADE)
+    user = models.ForeignKey("auth.User", on_delete=models.CASCADE)
+    due_date = models.DateField(blank=True, null=True)
+
+    created = models.DateTimeField(auto_now_add=True)
+
+    def __str__(self):
+        return f"Pin of {self.note} for {self.user}"
-- 
GitLab