diff options
Diffstat (limited to 'core')
| -rw-r--r-- | core/admin.py | 8 | ||||
| -rw-r--r-- | core/config.py | 20 | ||||
| -rw-r--r-- | core/context.py | 7 | ||||
| -rw-r--r-- | core/migrations/0001_initial.py | 63 | ||||
| -rw-r--r-- | core/migrations/__init__.py | 0 | ||||
| -rw-r--r-- | core/models/__init__.py | 1 | ||||
| -rw-r--r-- | core/models/config.py | 111 | 
7 files changed, 188 insertions, 22 deletions
diff --git a/core/admin.py b/core/admin.py new file mode 100644 index 0000000..e4a6ad0 --- /dev/null +++ b/core/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin + +from core.models import Config + + +@admin.register(Config) +class ConfigAdmin(admin.ModelAdmin): +    list_display = ["id", "key", "user", "identity"] diff --git a/core/config.py b/core/config.py deleted file mode 100644 index b9f6878..0000000 --- a/core/config.py +++ /dev/null @@ -1,20 +0,0 @@ -import pydantic - - -class Config(pydantic.BaseModel): - -    # Basic configuration options -    site_name: str = "takahē" -    identity_max_age: int = 24 * 60 * 60 - -    # Cached ORM object storage -    __singleton__ = None - -    class Config: -        env_prefix = "takahe_" - -    @classmethod -    def load(cls) -> "Config": -        if cls.__singleton__ is None: -            cls.__singleton__ = cls() -        return cls.__singleton__ diff --git a/core/context.py b/core/context.py index 17617b9..4346cbb 100644 --- a/core/context.py +++ b/core/context.py @@ -1,7 +1,10 @@ -from core.config import Config +from core.models import Config  def config_context(request):      return { -        "config": Config.load(), +        "config": Config.load_system(), +        "config_identity": ( +            Config.load_identity(request.identity) if request.identity else None +        ),      } diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..2c4731f --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,63 @@ +# Generated by Django 4.1.3 on 2022-11-16 21:23 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + +    initial = True + +    dependencies = [ +        ("users", "0002_identity_public_key_id"), +        migrations.swappable_dependency(settings.AUTH_USER_MODEL), +    ] + +    operations = [ +        migrations.CreateModel( +            name="Config", +            fields=[ +                ( +                    "id", +                    models.BigAutoField( +                        auto_created=True, +                        primary_key=True, +                        serialize=False, +                        verbose_name="ID", +                    ), +                ), +                ("key", models.CharField(max_length=500)), +                ("json", models.JSONField(blank=True, null=True)), +                ( +                    "image", +                    models.ImageField( +                        blank=True, null=True, upload_to="config/%Y/%m/%d/" +                    ), +                ), +                ( +                    "identity", +                    models.ForeignKey( +                        blank=True, +                        null=True, +                        on_delete=django.db.models.deletion.CASCADE, +                        related_name="configs", +                        to="users.identity", +                    ), +                ), +                ( +                    "user", +                    models.ForeignKey( +                        blank=True, +                        null=True, +                        on_delete=django.db.models.deletion.CASCADE, +                        related_name="configs", +                        to=settings.AUTH_USER_MODEL, +                    ), +                ), +            ], +            options={ +                "unique_together": {("key", "user", "identity")}, +            }, +        ), +    ] diff --git a/core/migrations/__init__.py b/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/core/migrations/__init__.py diff --git a/core/models/__init__.py b/core/models/__init__.py new file mode 100644 index 0000000..87bfe4e --- /dev/null +++ b/core/models/__init__.py @@ -0,0 +1 @@ +from .config import Config  # noqa diff --git a/core/models/config.py b/core/models/config.py new file mode 100644 index 0000000..8a2e40b --- /dev/null +++ b/core/models/config.py @@ -0,0 +1,111 @@ +from typing import ClassVar + +import pydantic +from django.db import models +from django.utils.functional import classproperty + + +class Config(models.Model): +    """ +    A configuration setting for either the server or a specific user or identity. + +    The possible options and their defaults are defined at the bottom of the file. +    """ + +    key = models.CharField(max_length=500) + +    user = models.ForeignKey( +        "users.user", +        blank=True, +        null=True, +        related_name="configs", +        on_delete=models.CASCADE, +    ) + +    identity = models.ForeignKey( +        "users.identity", +        blank=True, +        null=True, +        related_name="configs", +        on_delete=models.CASCADE, +    ) + +    json = models.JSONField(blank=True, null=True) +    image = models.ImageField(blank=True, null=True, upload_to="config/%Y/%m/%d/") + +    class Meta: +        unique_together = [ +            ("key", "user", "identity"), +        ] + +    @classproperty +    def system(cls): +        cls.system = cls.load_system() +        return cls.system + +    system: ClassVar["Config.ConfigOptions"]  # type: ignore + +    @classmethod +    def load_system(cls): +        """ +        Load all of the system config options and return an object with them +        """ +        values = {} +        for config in cls.objects.filter(user__isnull=True, identity__isnull=True): +            values[config.key] = config.image or config.json +        return cls.SystemOptions(**values) + +    @classmethod +    def load_user(cls, user): +        """ +        Load all of the user config options and return an object with them +        """ +        values = {} +        for config in cls.objects.filter(user=user, identity__isnull=True): +            values[config.key] = config.image or config.json +        return cls.UserOptions(**values) + +    @classmethod +    def load_identity(cls, identity): +        """ +        Load all of the identity config options and return an object with them +        """ +        values = {} +        for config in cls.objects.filter(user__isnull=True, identity=identity): +            values[config.key] = config.image or config.json +        return cls.IdentityOptions(**values) + +    @classmethod +    def set_system(cls, key, value): +        config_field = cls.SystemOptions.__fields__[key] +        if not isinstance(value, config_field.type_): +            raise ValueError(f"Invalid type for {key}: {type(value)}") +        cls.objects.update_or_create( +            key=key, +            defaults={"json": value}, +        ) + +    @classmethod +    def set_identity(cls, identity, key, value): +        config_field = cls.IdentityOptions.__fields__[key] +        if not isinstance(value, config_field.type_): +            raise ValueError(f"Invalid type for {key}: {type(value)}") +        cls.objects.update_or_create( +            identity=identity, +            key=key, +            defaults={"json": value}, +        ) + +    class SystemOptions(pydantic.BaseModel): + +        site_name: str = "takahē" +        highlight_color: str = "#449c8c" +        identity_max_age: int = 24 * 60 * 60 + +    class UserOptions(pydantic.BaseModel): + +        pass + +    class IdentityOptions(pydantic.BaseModel): + +        toot_mode: bool = False  | 
