From b7b6c56d0ea493dc3ad77af24d182570a65637a8 Mon Sep 17 00:00:00 2001 From: jpt Date: Sat, 3 May 2025 19:56:10 -0500 Subject: [PATCH] add core app skelly --- apps/core/__init__.py | 0 apps/core/admin.py | 3 ++ apps/core/api.py | 51 ++++++++++++++++++++++ apps/core/apps.py | 6 +++ apps/core/migrations/0001_initial.py | 64 ++++++++++++++++++++++++++++ apps/core/migrations/__init__.py | 0 apps/core/models.py | 20 +++++++++ apps/core/tests.py | 3 ++ apps/core/views.py | 3 ++ config/settings.py | 1 + 10 files changed, 151 insertions(+) create mode 100644 apps/core/__init__.py create mode 100644 apps/core/admin.py create mode 100644 apps/core/api.py create mode 100644 apps/core/apps.py create mode 100644 apps/core/migrations/0001_initial.py create mode 100644 apps/core/migrations/__init__.py create mode 100644 apps/core/models.py create mode 100644 apps/core/tests.py create mode 100644 apps/core/views.py diff --git a/apps/core/__init__.py b/apps/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/core/admin.py b/apps/core/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/core/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/core/api.py b/apps/core/api.py new file mode 100644 index 0000000..10c03ef --- /dev/null +++ b/apps/core/api.py @@ -0,0 +1,51 @@ +from ninja import NinjaAPI +from ninja import Schema +import datetime +from ..accounts.models import User +from .models import ThingEdit, Thing as ThingModel + +api = NinjaAPI() + + +class Thing(Schema): + id: int + data: dict + type: str + + +def get_user_from_key(key: str): + # TODO: something real + return User.objects.get(username=key) + + +@api.post("/thing/") +def sync_thing(request, key: str, thing: Thing): + user = get_user_from_key(key) + action = "none" + try: + old_thing = ThingModel.objects.get(user=user, thing_id=thing.id) + if old_thing.data != thing.data: + ThingEdit.objects.create( + thing=old_thing, + data=old_thing.data, + timestamp=old_thing.synced_at, + ) + old_thing.data = thing.data + old_thing.synced_at = datetime.datetime.utcnow() + old_thing.save() + thing = old_thing + action = "updated" + except ThingModel.DoesNotExist: + thing = ThingModel.objects.create( + user=user, thing_id=thing.id, data=thing.data, type=thing.type + ) + action = "created" + # TODO: deleted + return {"action": action} + + +@api.get("/thing/{id}") +def get_thing(request, id: int, key: str): + user = get_user_from_key(key) + thing = ThingModel.objects.get(user=user, thing_id=id) + return thing.data diff --git a/apps/core/apps.py b/apps/core/apps.py new file mode 100644 index 0000000..ab0051e --- /dev/null +++ b/apps/core/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "apps.core" diff --git a/apps/core/migrations/0001_initial.py b/apps/core/migrations/0001_initial.py new file mode 100644 index 0000000..7ac5c65 --- /dev/null +++ b/apps/core/migrations/0001_initial.py @@ -0,0 +1,64 @@ +# Generated by Django 5.2 on 2025-05-04 00:55 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Thing", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("thing_id", models.PositiveIntegerField()), + ("data", models.JSONField()), + ("created_at", models.DateTimeField()), + ("synced_at", models.DateTimeField()), + ("deleted", models.BooleanField()), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="things", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "unique_together": {("thing_id", "user_id")}, + }, + ), + migrations.CreateModel( + name="ThingEdit", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("data", models.JSONField()), + ("timestamp", models.DateTimeField()), + ( + "thing", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="edits", + to="core.thing", + ), + ), + ], + ), + ] diff --git a/apps/core/migrations/__init__.py b/apps/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/core/models.py b/apps/core/models.py new file mode 100644 index 0000000..7efd74e --- /dev/null +++ b/apps/core/models.py @@ -0,0 +1,20 @@ +from django.db import models +from ..accounts.models import User + + +class Thing(models.Model): + thing_id = models.PositiveIntegerField() + user = models.ForeignKey(User, related_name="things", on_delete=models.CASCADE) + data = models.JSONField() + created_at = models.DateTimeField() + synced_at = models.DateTimeField() + deleted = models.BooleanField() + + class Meta: + unique_together = ("thing_id", "user_id") + + +class ThingEdit(models.Model): + thing = models.ForeignKey(Thing, related_name="edits", on_delete=models.CASCADE) + data = models.JSONField() + timestamp = models.DateTimeField() diff --git a/apps/core/tests.py b/apps/core/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/core/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/core/views.py b/apps/core/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apps/core/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/config/settings.py b/config/settings.py index 02d4ba4..f3f16bb 100644 --- a/config/settings.py +++ b/config/settings.py @@ -70,6 +70,7 @@ INSTALLED_APPS = [ "django_structlog", "django_typer", "apps.accounts", + "apps.core", ] MIDDLEWARE = [