diff --git a/apps/core/api.py b/apps/core/api.py index e8b8d03..9ef4268 100644 --- a/apps/core/api.py +++ b/apps/core/api.py @@ -3,7 +3,7 @@ from ninja import Schema from ninja.security import APIKeyQuery import datetime from ..accounts.models import User -from .models import ThingEdit, Thing as ThingModel +from .models import ThingEdit, Thing as ThingModel, View as ViewModel class ApiKey(APIKeyQuery): @@ -21,12 +21,31 @@ class ApiKey(APIKeyQuery): api = NinjaAPI(auth=ApiKey()) +class View(Schema): + name: str + columns: dict + filters: dict + sort: str + + class Thing(Schema): id: int data: dict type: str +@api.post("/view/") +def sync_view(request, view: View): + ViewModel.objects.update_or_create( + name=view.name, + defaults=dict( + columns=view.columns, + filters=view.filters, + sort=view.sort, + ), + ) + + @api.post("/thing/") def sync_thing(request, thing: Thing): action = "none" diff --git a/apps/core/migrations/0004_view_alter_thing_deleted.py b/apps/core/migrations/0004_view_alter_thing_deleted.py new file mode 100644 index 0000000..9ffc3db --- /dev/null +++ b/apps/core/migrations/0004_view_alter_thing_deleted.py @@ -0,0 +1,32 @@ +# Generated by Django 5.2 on 2025-05-04 02:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0003_alter_thing_created_at"), + ] + + operations = [ + migrations.CreateModel( + name="View", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("name", models.CharField(max_length=100)), + ("columns", models.JSONField()), + ("filters", models.JSONField()), + ("sort", models.CharField()), + ], + ), + migrations.AlterField( + model_name="thing", + name="deleted", + field=models.BooleanField(default=False), + ), + ] diff --git a/apps/core/models.py b/apps/core/models.py index 5ce1b9c..58eb078 100644 --- a/apps/core/models.py +++ b/apps/core/models.py @@ -2,6 +2,13 @@ from django.db import models from ..accounts.models import User +class View(models.Model): + name = models.CharField(max_length=100) + columns = models.JSONField() + filters = models.JSONField() + sort = models.CharField() + + class Thing(models.Model): thing_id = models.PositiveIntegerField() user = models.ForeignKey(User, related_name="things", on_delete=models.CASCADE) diff --git a/apps/core/views.py b/apps/core/views.py index 91ea44a..0248ecc 100644 --- a/apps/core/views.py +++ b/apps/core/views.py @@ -1,3 +1,39 @@ from django.shortcuts import render +from ..accounts.models import User +from .models import Thing, View -# Create your views here. + +def attr_then_dict(thing, field): + try: + return getattr(thing, field) + except AttributeError: + return thing.data.get(field, "") + + +def thing_to_row(view, thing): + """convert one thing to corresponding row dict""" + return [attr_then_dict(thing, col["field_name"]) for col in view.columns.values()] + + +def table(request): + token = request.GET.get("token") + view_name = request.GET.get("view") + user = User.objects.get(username=token) + view = View.objects.get(name=view_name) + things = Thing.objects.filter(user=user, deleted=False) + for field, val in view.filters.items(): + if field == "type": + things = things.filter(type=val) + else: + val = val.split(",") + if len(val) == 1: + things = things.filter(**{f"data__{field}": val}) + else: + things = things.filter(**{f"data__{field}__in": val}) + + things = things.order_by(*[f"data__{sort}" for sort in view.sort.split(",")]) + + # execute query + rows = (thing_to_row(view, thing) for thing in things) + + return render(request, "thingtable.html", {"rows": rows, "view": view}) diff --git a/config/urls.py b/config/urls.py index 3a3a600..80bcc74 100644 --- a/config/urls.py +++ b/config/urls.py @@ -3,11 +3,13 @@ from django.contrib import admin from django.urls import path, include from debug_toolbar.toolbar import debug_toolbar_urls from apps.core.api import api +from apps.core import views urlpatterns = [ path("djadmin/", admin.site.urls), path("accounts/", include("allauth.urls")), path("api/", api.urls), + path("table/", views.table), ] if settings.DEBUG and not settings.IS_TESTING: diff --git a/templates/thingtable.html b/templates/thingtable.html new file mode 100644 index 0000000..fd68ae0 --- /dev/null +++ b/templates/thingtable.html @@ -0,0 +1,20 @@ + + + +{% for col in view.columns %} + +{% endfor %} + + + +{% for row in rows %} + + {% for item in row %} + + {% endfor %} + +{% endfor %} + +
{{ col }}
+ {{ item }} +