From 97601586c5dca3a1dea28693e3979459a69a3b3d Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Mon, 9 Mar 2026 16:08:28 -0400 Subject: [PATCH] new applets app for cross-board usage of Applet() & UserApplet() models; dashboard migrations reset and apps reseeded w. new default specs; core.settings & many tests thru-out suite updated accordingly --- src/apps/applets/__init__.py | 0 src/apps/applets/admin.py | 11 +++++ src/apps/applets/apps.py | 5 +++ .../migrations/0001_initial.py} | 15 ++++--- .../applets/migrations/0002_seed_applets.py | 29 ++++++++++++++ src/apps/applets/migrations/__init__.py | 0 src/apps/applets/models.py | 34 ++++++++++++++++ src/apps/applets/tests.py | 3 ++ .../applets/tests/integrated/test_models.py | 40 +++++++++++++++++++ src/apps/applets/views.py | 3 ++ src/apps/dashboard/admin.py | 10 ----- .../dashboard/migrations/0003_seed_applets.py | 17 -------- .../0004_applet_grid_cols_applet_grid_rows.py | 23 ----------- .../0005_set_applet_grid_defaults.py | 17 -------- .../migrations/0006_rename_theme_switcher.py | 18 --------- .../migrations/0007_seed_wallet_applet.py | 18 --------- src/apps/dashboard/models.py | 28 +------------ .../dashboard/tests/integrated/test_models.py | 36 +---------------- .../dashboard/tests/integrated/test_views.py | 3 +- src/apps/dashboard/views.py | 3 +- src/core/settings.py | 1 + src/functional_tests/base.py | 3 +- src/functional_tests/test_dashboard.py | 2 +- .../test_layout_and_styling.py | 3 -- src/functional_tests/test_my_lists.py | 1 - src/functional_tests/test_wallet.py | 2 +- 26 files changed, 142 insertions(+), 183 deletions(-) create mode 100644 src/apps/applets/__init__.py create mode 100644 src/apps/applets/admin.py create mode 100644 src/apps/applets/apps.py rename src/apps/{dashboard/migrations/0002_applet_userapplet.py => applets/migrations/0001_initial.py} (73%) create mode 100644 src/apps/applets/migrations/0002_seed_applets.py create mode 100644 src/apps/applets/migrations/__init__.py create mode 100644 src/apps/applets/models.py create mode 100644 src/apps/applets/tests.py create mode 100644 src/apps/applets/tests/integrated/test_models.py create mode 100644 src/apps/applets/views.py delete mode 100644 src/apps/dashboard/migrations/0003_seed_applets.py delete mode 100644 src/apps/dashboard/migrations/0004_applet_grid_cols_applet_grid_rows.py delete mode 100644 src/apps/dashboard/migrations/0005_set_applet_grid_defaults.py delete mode 100644 src/apps/dashboard/migrations/0006_rename_theme_switcher.py delete mode 100644 src/apps/dashboard/migrations/0007_seed_wallet_applet.py diff --git a/src/apps/applets/__init__.py b/src/apps/applets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/applets/admin.py b/src/apps/applets/admin.py new file mode 100644 index 0000000..0a7ceef --- /dev/null +++ b/src/apps/applets/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from apps.applets.models import Applet, UserApplet + + +@admin.register(Applet) +class AppletAdmin(admin.ModelAdmin): + list_display = ['slug', 'name', 'default_visible', 'grid_cols', 'grid_rows'] + list_editable = ['grid_cols', 'grid_rows'] + +admin.site.register(UserApplet) diff --git a/src/apps/applets/apps.py b/src/apps/applets/apps.py new file mode 100644 index 0000000..1f158c5 --- /dev/null +++ b/src/apps/applets/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AppletsConfig(AppConfig): + name = 'apps.applets' diff --git a/src/apps/dashboard/migrations/0002_applet_userapplet.py b/src/apps/applets/migrations/0001_initial.py similarity index 73% rename from src/apps/dashboard/migrations/0002_applet_userapplet.py rename to src/apps/applets/migrations/0001_initial.py index 0e92bff..d0fef9f 100644 --- a/src/apps/dashboard/migrations/0002_applet_userapplet.py +++ b/src/apps/applets/migrations/0001_initial.py @@ -1,14 +1,12 @@ -# Generated by Django 6.0 on 2026-03-04 20:34 - import django.db.models.deletion + from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): - + initial = True dependencies = [ - ('dashboard', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -19,7 +17,10 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('slug', models.SlugField(unique=True)), ('name', models.CharField(max_length=100)), + ('context', models.CharField(choices=[('dashboard', 'Dashboard'), ('gameboard', 'Gameboard')], default='dashboard', max_length=20)), ('default_visible', models.BooleanField(default=True)), + ('grid_cols', models.PositiveSmallIntegerField(default=12)), + ('grid_rows', models.PositiveSmallIntegerField(default=3)), ], ), migrations.CreateModel( @@ -27,11 +28,9 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('visible', models.BooleanField(default=True)), - ('applet', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dashboard.applet')), + ('applet', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='applets.applet')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_applets', to=settings.AUTH_USER_MODEL)), ], - options={ - 'unique_together': {('user', 'applet')}, - }, + options={'unique_together': {('user', 'applet')}}, ), ] diff --git a/src/apps/applets/migrations/0002_seed_applets.py b/src/apps/applets/migrations/0002_seed_applets.py new file mode 100644 index 0000000..83450a6 --- /dev/null +++ b/src/apps/applets/migrations/0002_seed_applets.py @@ -0,0 +1,29 @@ +from django.db import migrations + + +def seed_applets(apps, schema_editor): + Applet = apps.get_model('applets', 'Applet') + for slug, name, cols, rows, context in [ + ('wallet', 'Wallet', 12, 3, 'dashboard'), + ('new-list', 'New List', 9, 3, 'dashboard'), + ('my-lists', 'My Lists', 3, 3, 'dashboard'), + ('username', 'Username', 6, 3, 'dashboard'), + ('palette', 'Palette', 6, 3, 'dashboard'), + ('new-game', 'New Game', 4, 2, 'gameboard'), + ('my-games', 'My Games', 4, 4, 'gameboard'), + ('game-kit', 'Game Kit', 4, 2, 'gameboard'), + ]: + Applet.objects.get_or_create( + slug=slug, + defaults={'name': name, 'grid_cols': cols, 'grid_rows': rows, 'context': context}, + ) + + +class Migration(migrations.Migration): + dependencies = [ + ('applets', '0001_initial') + ] + + operations = [ + migrations.RunPython(seed_applets, migrations.RunPython.noop) + ] diff --git a/src/apps/applets/migrations/__init__.py b/src/apps/applets/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/applets/models.py b/src/apps/applets/models.py new file mode 100644 index 0000000..a1bad13 --- /dev/null +++ b/src/apps/applets/models.py @@ -0,0 +1,34 @@ +from django.db import models + +class Applet(models.Model): + DASHBOARD = "dashboard" + GAMEBOARD = "gameboard" + CONTEXT_CHOICES = [ + (DASHBOARD, "Dashboard"), + (GAMEBOARD, "Gameboard"), + ] + + slug = models.SlugField(unique=True) + name = models.CharField(max_length=100) + context = models.CharField(max_length=20, choices=CONTEXT_CHOICES, default=DASHBOARD) + default_visible = models.BooleanField(default=True) + grid_cols = models.PositiveSmallIntegerField(default=12) + grid_rows = models.PositiveSmallIntegerField(default=3) + + def __str__(self): + return self.name + +class UserApplet(models.Model): + user = models.ForeignKey( + "lyric.User", + related_name="user_applets", + on_delete=models.CASCADE, + ) + applet = models.ForeignKey( + Applet, + on_delete=models.CASCADE, + ) + visible = models.BooleanField(default=True) + + class Meta: + unique_together = ("user", "applet") diff --git a/src/apps/applets/tests.py b/src/apps/applets/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/src/apps/applets/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/src/apps/applets/tests/integrated/test_models.py b/src/apps/applets/tests/integrated/test_models.py new file mode 100644 index 0000000..a6fd282 --- /dev/null +++ b/src/apps/applets/tests/integrated/test_models.py @@ -0,0 +1,40 @@ +from django.db.utils import IntegrityError +from django.test import TestCase + +from apps.applets.models import Applet, UserApplet +from apps.lyric.models import User + + +class AppletModelTest(TestCase): + def setUp(self): + self.applet = Applet.objects.create( + slug="my-applet", name="My Applet", default_visible=True + ) + + def test_applet_can_be_created(self): + self.assertEqual(Applet.objects.get(slug="my-applet"), self.applet) + + def test_applet_slug_is_unique(self): + with self.assertRaises(IntegrityError): + Applet.objects.create(slug="my-applet", name="Second") + + def test_applet_str(self): + self.assertEqual(str(self.applet), "My Applet") + + def test_applet_grid_defaults(self): + self.assertEqual(self.applet.grid_cols, 12) + self.assertEqual(self.applet.grid_rows, 3) + +class UserAppletModelTest(TestCase): + def setUp(self): + self.user = User.objects.create(email="a@b.cde") + self.applet, _ = Applet.objects.get_or_create(slug="username", defaults={"name": "Username"}) + + def test_user_applet_links_user_to_applet(self): + ua = UserApplet.objects.create(user=self.user, applet=self.applet, visible=True) + self.assertIn(ua, self.user.user_applets.all()) + + def test_user_applet_unique_per_user_and_applet(self): + UserApplet.objects.create(user=self.user, applet=self.applet, visible=True) + with self.assertRaises(IntegrityError): + UserApplet.objects.create(user=self.user, applet=self.applet, visible=False) \ No newline at end of file diff --git a/src/apps/applets/views.py b/src/apps/applets/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/src/apps/applets/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/src/apps/dashboard/admin.py b/src/apps/dashboard/admin.py index 7157de1..694323f 100644 --- a/src/apps/dashboard/admin.py +++ b/src/apps/dashboard/admin.py @@ -1,11 +1 @@ from django.contrib import admin - -from apps.dashboard.models import Applet, UserApplet - - -@admin.register(Applet) -class AppletAdmin(admin.ModelAdmin): - list_display = ['slug', 'name', 'default_visible', 'grid_cols', 'grid_rows'] - list_editable = ['grid_cols', 'grid_rows'] - -admin.site.register(UserApplet) diff --git a/src/apps/dashboard/migrations/0003_seed_applets.py b/src/apps/dashboard/migrations/0003_seed_applets.py deleted file mode 100644 index 78c4666..0000000 --- a/src/apps/dashboard/migrations/0003_seed_applets.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.db import migrations - - -def seed_applets(apps, schema_editor): - Applet = apps.get_model("dashboard", "Applet") - Applet.objects.get_or_create(slug="username", defaults={"name": "Username"}) - Applet.objects.get_or_create(slug="theme-switcher", defaults={"name": "Theme Switcher"}) - - -class Migration(migrations.Migration): - dependencies = [ - ("dashboard", "0002_applet_userapplet"), - ] - - operations = [ - migrations.RunPython(seed_applets, migrations.RunPython.noop), - ] diff --git a/src/apps/dashboard/migrations/0004_applet_grid_cols_applet_grid_rows.py b/src/apps/dashboard/migrations/0004_applet_grid_cols_applet_grid_rows.py deleted file mode 100644 index 79a9f91..0000000 --- a/src/apps/dashboard/migrations/0004_applet_grid_cols_applet_grid_rows.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 6.0 on 2026-03-06 22:29 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('dashboard', '0003_seed_applets'), - ] - - operations = [ - migrations.AddField( - model_name='applet', - name='grid_cols', - field=models.PositiveSmallIntegerField(default=12), - ), - migrations.AddField( - model_name='applet', - name='grid_rows', - field=models.PositiveSmallIntegerField(default=3), - ), - ] diff --git a/src/apps/dashboard/migrations/0005_set_applet_grid_defaults.py b/src/apps/dashboard/migrations/0005_set_applet_grid_defaults.py deleted file mode 100644 index 87e605f..0000000 --- a/src/apps/dashboard/migrations/0005_set_applet_grid_defaults.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.db import migrations - - -def set_grid_defaults(apps, schema_editor): - Applet = apps.get_model("dashboard", "Applet") - Applet.objects.filter(slug__in=["username", "theme-switcher"]).update(grid_cols=6, grid_rows=3) - Applet.objects.get_or_create(slug="new-list", defaults={"name": "New List", "grid_cols": 9, "grid_rows": 3}) - Applet.objects.get_or_create(slug="my-lists", defaults={"name": "My Lists", "grid_cols": 3, "grid_rows": 3}) - -class Migration(migrations.Migration): - dependencies = [ - ("dashboard", "0004_applet_grid_cols_applet_grid_rows"), - ] - - operations = [ - migrations.RunPython(set_grid_defaults, migrations.RunPython.noop), - ] diff --git a/src/apps/dashboard/migrations/0006_rename_theme_switcher.py b/src/apps/dashboard/migrations/0006_rename_theme_switcher.py deleted file mode 100644 index 3c768cc..0000000 --- a/src/apps/dashboard/migrations/0006_rename_theme_switcher.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.db import migrations - - -def rename_theme_switcher(apps, schema_editor): - Applet = apps.get_model("dashboard", "Applet") - Applet.objects.filter(slug="theme-switcher").update( - slug="palette", name="Palette", grid_cols=6, grid_rows=3 - ) - - -class Migration(migrations.Migration): - dependencies = [ - ("dashboard", "0005_set_applet_grid_defaults"), - ] - - operations = [ - migrations.RunPython(rename_theme_switcher, migrations.RunPython.noop), - ] diff --git a/src/apps/dashboard/migrations/0007_seed_wallet_applet.py b/src/apps/dashboard/migrations/0007_seed_wallet_applet.py deleted file mode 100644 index 756fc0d..0000000 --- a/src/apps/dashboard/migrations/0007_seed_wallet_applet.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.db import migrations - - -def seed_wallet_applet(apps, schema_editor): - Applet = apps.get_model("dashboard", "Applet") - Applet.objects.get_or_create( - slug="wallet", - defaults={"name": "Wallet", "grid_cols": 12, "grid_rows": 3}, - ) - -class Migration(migrations.Migration): - dependencies = [ - ("dashboard", "0006_rename_theme_switcher"), - ] - - operations = [ - migrations.RunPython(seed_wallet_applet, migrations.RunPython.noop), - ] diff --git a/src/apps/dashboard/models.py b/src/apps/dashboard/models.py index 65b0e24..dc06461 100644 --- a/src/apps/dashboard/models.py +++ b/src/apps/dashboard/models.py @@ -36,30 +36,4 @@ class Item(models.Model): unique_together = ("list", "text") def __str__(self): - return self.text - -class Applet(models.Model): - slug = models.SlugField(unique=True) - name = models.CharField(max_length=100) - default_visible = models.BooleanField(default=True) - grid_cols = models.PositiveSmallIntegerField(default=12) - grid_rows = models.PositiveSmallIntegerField(default=3) - - def __str__(self): - return self.name - -class UserApplet(models.Model): - user = models.ForeignKey( - "lyric.User", - related_name="user_applets", - on_delete=models.CASCADE, - ) - applet = models.ForeignKey( - Applet, - on_delete=models.CASCADE, - ) - visible = models.BooleanField(default=True) - - class Meta: - unique_together = ("user", "applet") - + return self.text diff --git a/src/apps/dashboard/tests/integrated/test_models.py b/src/apps/dashboard/tests/integrated/test_models.py index 9414fe6..4564c88 100644 --- a/src/apps/dashboard/tests/integrated/test_models.py +++ b/src/apps/dashboard/tests/integrated/test_models.py @@ -2,7 +2,7 @@ from django.core.exceptions import ValidationError from django.db.utils import IntegrityError from django.test import TestCase -from apps.dashboard.models import Applet, Item, List, UserApplet +from apps.dashboard.models import Item, List from apps.lyric.models import User @@ -68,37 +68,3 @@ class ListModelTest(TestCase): Item.objects.create(list=list_, text="first item") Item.objects.create(list=list_, text="second item") self.assertEqual(list_.name, "first item") - -class AppletModelTest(TestCase): - def setUp(self): - self.applet = Applet.objects.create( - slug="my-applet", name="My Applet", default_visible=True - ) - - def test_applet_can_be_created(self): - self.assertEqual(Applet.objects.get(slug="my-applet"), self.applet) - - def test_applet_slug_is_unique(self): - with self.assertRaises(IntegrityError): - Applet.objects.create(slug="my-applet", name="Second") - - def test_applet_str(self): - self.assertEqual(str(self.applet), "My Applet") - - def test_applet_grid_defaults(self): - self.assertEqual(self.applet.grid_cols, 12) - self.assertEqual(self.applet.grid_rows, 3) - -class UserAppletModelTest(TestCase): - def setUp(self): - self.user = User.objects.create(email="a@b.cde") - self.applet, _ = Applet.objects.get_or_create(slug="username", defaults={"name": "Username"}) - - def test_user_applet_links_user_to_applet(self): - ua = UserApplet.objects.create(user=self.user, applet=self.applet, visible=True) - self.assertIn(ua, self.user.user_applets.all()) - - def test_user_applet_unique_per_user_and_applet(self): - UserApplet.objects.create(user=self.user, applet=self.applet, visible=True) - with self.assertRaises(IntegrityError): - UserApplet.objects.create(user=self.user, applet=self.applet, visible=False) diff --git a/src/apps/dashboard/tests/integrated/test_views.py b/src/apps/dashboard/tests/integrated/test_views.py index b55a020..8b2befe 100644 --- a/src/apps/dashboard/tests/integrated/test_views.py +++ b/src/apps/dashboard/tests/integrated/test_views.py @@ -5,11 +5,12 @@ from django.test import override_settings, TestCase from django.urls import reverse from django.utils import html +from apps.applets.models import Applet, UserApplet from apps.dashboard.forms import ( DUPLICATE_ITEM_ERROR, EMPTY_ITEM_ERROR, ) -from apps.dashboard.models import Applet, Item, List, UserApplet +from apps.dashboard.models import Item, List from apps.lyric.models import User diff --git a/src/apps/dashboard/views.py b/src/apps/dashboard/views.py index 2c52657..fc56525 100644 --- a/src/apps/dashboard/views.py +++ b/src/apps/dashboard/views.py @@ -8,8 +8,9 @@ from django.http import HttpResponse, HttpResponseForbidden, JsonResponse from django.shortcuts import redirect, render from django.views.decorators.csrf import ensure_csrf_cookie +from apps.applets.models import Applet, UserApplet from apps.dashboard.forms import ExistingListItemForm, ItemForm -from apps.dashboard.models import Applet, Item, List, UserApplet +from apps.dashboard.models import Item, List from apps.lyric.models import PaymentMethod, Token, User, Wallet diff --git a/src/core/settings.py b/src/core/settings.py index a50b85e..143b2d0 100644 --- a/src/core/settings.py +++ b/src/core/settings.py @@ -62,6 +62,7 @@ INSTALLED_APPS = [ 'apps.lyric', # Custom apps 'apps.api', + 'apps.applets', 'functional_tests', # Depend apps 'compressor', diff --git a/src/functional_tests/base.py b/src/functional_tests/base.py index c979bc1..d75bbd4 100644 --- a/src/functional_tests/base.py +++ b/src/functional_tests/base.py @@ -8,11 +8,10 @@ from pathlib import Path from selenium import webdriver from selenium.common.exceptions import WebDriverException from selenium.webdriver.common.by import By -from selenium.webdriver.common.keys import Keys from .container_commands import create_session_on_server, reset_database from .management.commands.create_session import create_pre_authenticated_session -from apps.dashboard.models import Applet +from apps.applets.models import Applet diff --git a/src/functional_tests/test_dashboard.py b/src/functional_tests/test_dashboard.py index 5f40159..9d8cd18 100644 --- a/src/functional_tests/test_dashboard.py +++ b/src/functional_tests/test_dashboard.py @@ -3,7 +3,7 @@ from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from .base import FunctionalTest -from apps.dashboard.models import Applet +from apps.applets.models import Applet class DashboardMaintenanceTest(FunctionalTest): diff --git a/src/functional_tests/test_layout_and_styling.py b/src/functional_tests/test_layout_and_styling.py index 50bf92b..915965f 100644 --- a/src/functional_tests/test_layout_and_styling.py +++ b/src/functional_tests/test_layout_and_styling.py @@ -1,6 +1,3 @@ -from selenium.webdriver.common.by import By -from selenium.webdriver.common.keys import Keys - from .base import FunctionalTest from .list_page import ListPage diff --git a/src/functional_tests/test_my_lists.py b/src/functional_tests/test_my_lists.py index a09b53b..c537e16 100644 --- a/src/functional_tests/test_my_lists.py +++ b/src/functional_tests/test_my_lists.py @@ -3,7 +3,6 @@ from selenium.webdriver.common.by import By from .base import FunctionalTest from .list_page import ListPage from .my_lists_page import MyListsPage -from apps.lyric.models import User class MyListsTest(FunctionalTest): diff --git a/src/functional_tests/test_wallet.py b/src/functional_tests/test_wallet.py index 4f31a4a..2f650ce 100644 --- a/src/functional_tests/test_wallet.py +++ b/src/functional_tests/test_wallet.py @@ -2,7 +2,7 @@ from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from .base import FunctionalTest -from apps.dashboard.models import Applet +from apps.applets.models import Applet class WalletDisplayTest(FunctionalTest):