Mehrere Dashboards und dieselbe Karte mehrfach
Zwei Grenzen sassen tief im Datenmodell. Erstens war "i" im gespeicherten Layout zugleich die Kartenart; da react-grid-layout eindeutige Kennungen verlangt, ging jede Art genau einmal - zwei Artikel gleichzeitig beobachten war damit unmoeglich. Zweitens war dashboard_layouts.user_id unique, also genau ein Dashboard je Benutzer. Jetzt ist "i" die Kennung dieser einen Karte und "type" ihre Art, dazu kommt "props" fuer das, was nur diese Karte angeht (etwa welcher Artikel). Aeltere Anordnungen haben kein "type"; fuer sie gilt die alte Kennung als Art, sodass gespeicherte Startseiten unveraendert weiterlaufen. Die Tabelle bekommt Name und Reihenfolge, das unique faellt. Sein Name haengt davon ab, wie die Tabelle entstanden ist, deshalb wird er in pg_constraint nachgeschlagen statt geraten. Eine Feinheit beim Anlegen: Wer noch auf der Admin-Vorgabe sitzt und sein erstes eigenes Dashboard anlegt, wuerde die Vorgabe schlagartig verlieren - sobald eigene Zeilen da sind, zaehlen nur noch die. Deshalb wird sie vorher uebernommen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -188,54 +188,120 @@ def test_activity_zaehlt_ein_und_auslagerungen(db, rice, user):
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- Anordnung
|
||||
def _erstes(antwort):
|
||||
return antwort.dashboards[0]
|
||||
|
||||
|
||||
def test_layout_faellt_auf_die_eingebaute_zurueck(db, user):
|
||||
antwort = dashboard.get_layout(db=db, user=user)
|
||||
antwort = dashboard.list_dashboards(db=db, user=user)
|
||||
assert antwort.source == "builtin"
|
||||
assert antwort.enforced is False
|
||||
assert antwort.has_default is False
|
||||
assert any(k["i"] == "expiry-all" for k in antwort.layout)
|
||||
assert len(antwort.dashboards) == 1
|
||||
assert any(k["type"] == "expiry-all" for k in _erstes(antwort).layout)
|
||||
|
||||
|
||||
def test_eigene_anordnung_wird_gespeichert(db, user):
|
||||
from app.schemas import DashboardLayoutIn
|
||||
from app.schemas import DashboardCreate, DashboardUpdate
|
||||
|
||||
eigen = [{"i": "shopping", "x": 0, "y": 0, "w": 6, "h": 3}]
|
||||
dashboard.put_layout(DashboardLayoutIn(layout=eigen), db=db, user=user)
|
||||
neu = dashboard.create_dashboard(
|
||||
DashboardCreate(name="Vorräte", layout=[]), db=db, user=user
|
||||
)
|
||||
eigen = [{"i": "k1", "type": "shopping", "x": 0, "y": 0, "w": 6, "h": 3, "props": {}}]
|
||||
dashboard.update_dashboard(neu.id, DashboardUpdate(layout=eigen), db=db, user=user)
|
||||
|
||||
antwort = dashboard.get_layout(db=db, user=user)
|
||||
antwort = dashboard.list_dashboards(db=db, user=user)
|
||||
assert antwort.source == "user"
|
||||
assert antwort.layout == eigen
|
||||
assert _erstes(antwort).name == "Vorräte"
|
||||
assert _erstes(antwort).layout == eigen
|
||||
|
||||
|
||||
def test_dieselbe_kartenart_mehrfach(db, user):
|
||||
"""Der eigentliche Zweck der Instanz-Kennung: zwei Karten derselben Art,
|
||||
jede auf einen anderen Artikel eingestellt."""
|
||||
from app.schemas import DashboardCreate
|
||||
|
||||
layout = [
|
||||
{"i": "a", "type": "product-timeline", "x": 0, "y": 0, "w": 6, "h": 6,
|
||||
"props": {"product_id": 1}},
|
||||
{"i": "b", "type": "product-timeline", "x": 6, "y": 0, "w": 6, "h": 6,
|
||||
"props": {"product_id": 2}},
|
||||
]
|
||||
neu = dashboard.create_dashboard(
|
||||
DashboardCreate(name="Beobachtung", layout=layout), db=db, user=user
|
||||
)
|
||||
assert len(neu.layout) == 2
|
||||
assert {k["props"]["product_id"] for k in neu.layout} == {1, 2}
|
||||
|
||||
|
||||
def test_alte_anordnung_bekommt_die_art_aus_der_kennung(db):
|
||||
"""Gespeicherte Anordnungen von frueher haben kein "type" - dort war die
|
||||
Kennung die Art. Ohne das Nachziehen waere jede alte Karte unbekannt."""
|
||||
alt = [{"i": "shopping", "x": 0, "y": 0, "w": 6, "h": 3}]
|
||||
normalisiert = dashboard._normalize(alt)
|
||||
assert normalisiert[0]["type"] == "shopping"
|
||||
assert normalisiert[0]["props"] == {}
|
||||
|
||||
|
||||
def test_erzwungene_vorgabe_verhindert_eigene_anordnung(db, user):
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.schemas import DashboardLayoutIn
|
||||
from app.schemas import DashboardCreate
|
||||
|
||||
vorgabe = [{"i": "status", "x": 0, "y": 0, "w": 12, "h": 1}]
|
||||
dashboard.put_default_layout(DashboardLayoutIn(layout=vorgabe), db=db, admin=user)
|
||||
dashboard.create_dashboard(DashboardCreate(name="Vorgabe", layout=[]), db=db, user=user)
|
||||
dashboard.put_default_dashboards(db=db, admin=user)
|
||||
dashboard.set_enforced(True, db=db, admin=user)
|
||||
|
||||
antwort = dashboard.get_layout(db=db, user=user)
|
||||
antwort = dashboard.list_dashboards(db=db, user=user)
|
||||
assert antwort.source == "default"
|
||||
assert antwort.enforced is True
|
||||
assert antwort.layout == vorgabe
|
||||
|
||||
with pytest.raises(HTTPException) as fehler:
|
||||
dashboard.put_layout(DashboardLayoutIn(layout=[]), db=db, user=user)
|
||||
dashboard.create_dashboard(DashboardCreate(name="X", layout=[]), db=db, user=user)
|
||||
assert fehler.value.status_code == 403
|
||||
|
||||
|
||||
def test_zuruecksetzen_stellt_die_vorgabe_wieder_her(db, user):
|
||||
from app.schemas import DashboardLayoutIn
|
||||
from app.schemas import DashboardCreate
|
||||
|
||||
vorgabe = [{"i": "status", "x": 0, "y": 0, "w": 12, "h": 1}]
|
||||
dashboard.put_default_layout(DashboardLayoutIn(layout=vorgabe), db=db, admin=user)
|
||||
dashboard.put_layout(DashboardLayoutIn(layout=[{"i": "shopping", "x": 0, "y": 0, "w": 4, "h": 3}]),
|
||||
db=db, user=user)
|
||||
assert dashboard.get_layout(db=db, user=user).source == "user"
|
||||
dashboard.create_dashboard(DashboardCreate(name="Vorgabe", layout=[]), db=db, user=user)
|
||||
dashboard.put_default_dashboards(db=db, admin=user)
|
||||
dashboard.create_dashboard(DashboardCreate(name="Eigenes", layout=[]), db=db, user=user)
|
||||
assert dashboard.list_dashboards(db=db, user=user).source == "user"
|
||||
|
||||
dashboard.reset_layout(db=db, user=user)
|
||||
antwort = dashboard.get_layout(db=db, user=user)
|
||||
dashboard.reset_dashboards(db=db, user=user)
|
||||
antwort = dashboard.list_dashboards(db=db, user=user)
|
||||
assert antwort.source == "default"
|
||||
assert antwort.layout == vorgabe
|
||||
assert _erstes(antwort).name == "Vorgabe"
|
||||
|
||||
|
||||
def test_erstes_eigenes_uebernimmt_die_vorgabe(db, user):
|
||||
"""Sonst verschwaende das Anlegen eines zweiten Dashboards die Vorgabe:
|
||||
Sobald eigene Zeilen existieren, zaehlen nur noch die."""
|
||||
from app.schemas import DashboardCreate
|
||||
|
||||
dashboard.create_dashboard(DashboardCreate(name="Vorgabe", layout=[]), db=db, user=user)
|
||||
dashboard.put_default_dashboards(db=db, admin=user)
|
||||
dashboard.reset_dashboards(db=db, user=user)
|
||||
assert dashboard.list_dashboards(db=db, user=user).source == "default"
|
||||
|
||||
dashboard.create_dashboard(DashboardCreate(name="Getränke", layout=[]), db=db, user=user)
|
||||
antwort = dashboard.list_dashboards(db=db, user=user)
|
||||
assert [d.name for d in antwort.dashboards] == ["Vorgabe", "Getränke"]
|
||||
|
||||
|
||||
def test_fremdes_dashboard_ist_unsichtbar(db, user):
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.models import Role, User
|
||||
from app.schemas import DashboardCreate, DashboardUpdate
|
||||
|
||||
fremd = User(username="andere", password_hash="x", role=Role.user)
|
||||
db.add(fremd)
|
||||
db.commit()
|
||||
db.refresh(fremd)
|
||||
|
||||
meins = dashboard.create_dashboard(DashboardCreate(name="Meins", layout=[]), db=db, user=user)
|
||||
with pytest.raises(HTTPException) as fehler:
|
||||
dashboard.update_dashboard(meins.id, DashboardUpdate(name="Geklaut"), db=db, user=fremd)
|
||||
assert fehler.value.status_code == 404
|
||||
|
||||
Reference in New Issue
Block a user