147 lines
4.8 KiB
Python
147 lines
4.8 KiB
Python
import os
|
|
import shutil
|
|
|
|
from models.model import Model
|
|
|
|
|
|
def _maybe_migrate_tab_json_to_zone():
|
|
"""One-time copy ``db/tab.json`` → ``db/zone.json`` when upgrading."""
|
|
try:
|
|
base = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
db_dir = os.path.join(base, "db")
|
|
zone_path = os.path.join(db_dir, "zone.json")
|
|
tab_path = os.path.join(db_dir, "tab.json")
|
|
if not os.path.exists(zone_path) and os.path.exists(tab_path):
|
|
shutil.copy2(tab_path, zone_path)
|
|
print("Migrated db/tab.json -> db/zone.json")
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
class Zone(Model):
|
|
"""Preset layout row (stored in ``db/zone.json``); legacy storage was ``tab.json`` / Tab.
|
|
|
|
Optional legacy ``content_kind`` (``\"presets\"`` / ``\"sequences\"``) is kept for older data;
|
|
zones may hold both preset tiles and ``sequence_ids``.
|
|
"""
|
|
|
|
def __init__(self):
|
|
if not getattr(Zone, "_migration_checked", False):
|
|
_maybe_migrate_tab_json_to_zone()
|
|
Zone._migration_checked = True
|
|
super().__init__()
|
|
|
|
def load(self):
|
|
super().load()
|
|
changed = False
|
|
for zid, doc in list(self.items()):
|
|
if not isinstance(doc, dict):
|
|
continue
|
|
if "group_ids" not in doc:
|
|
doc["group_ids"] = []
|
|
changed = True
|
|
if "preset_group_ids" not in doc or not isinstance(doc.get("preset_group_ids"), dict):
|
|
doc["preset_group_ids"] = {}
|
|
changed = True
|
|
if "sequence_ids" not in doc or not isinstance(doc.get("sequence_ids"), list):
|
|
doc["sequence_ids"] = []
|
|
changed = True
|
|
if not self._normalized_content_kind(doc):
|
|
doc["content_kind"] = self._infer_content_kind(doc)
|
|
changed = True
|
|
if changed:
|
|
self.save()
|
|
|
|
@staticmethod
|
|
def _normalized_content_kind(doc):
|
|
if not isinstance(doc, dict):
|
|
return None
|
|
kind = doc.get("content_kind")
|
|
return kind if kind in ("presets", "sequences") else None
|
|
|
|
@staticmethod
|
|
def _preset_ids_in_doc(doc):
|
|
if not isinstance(doc, dict):
|
|
return []
|
|
flat = doc.get("presets_flat")
|
|
if isinstance(flat, list):
|
|
return [str(x) for x in flat if x is not None and str(x).strip()]
|
|
presets = doc.get("presets")
|
|
if not isinstance(presets, list) or not presets:
|
|
return []
|
|
if isinstance(presets[0], str):
|
|
return [str(x) for x in presets if x is not None and str(x).strip()]
|
|
if isinstance(presets[0], list):
|
|
out = []
|
|
for row in presets:
|
|
if isinstance(row, list):
|
|
out.extend(str(x) for x in row if x is not None and str(x).strip())
|
|
return out
|
|
return []
|
|
|
|
@classmethod
|
|
def _infer_content_kind(cls, doc):
|
|
kind = cls._normalized_content_kind(doc)
|
|
if kind:
|
|
return kind
|
|
seq_ids = [
|
|
str(x).strip()
|
|
for x in (doc.get("sequence_ids") or [])
|
|
if x is not None and str(x).strip()
|
|
]
|
|
preset_ids = cls._preset_ids_in_doc(doc)
|
|
if seq_ids and not preset_ids:
|
|
return "sequences"
|
|
return "presets"
|
|
|
|
def _enforce_content_kind_invariants(self, doc):
|
|
"""No-op: presets and sequences may coexist on one zone."""
|
|
_ = doc
|
|
|
|
def create(self, name="", names=None, presets=None, group_ids=None, content_kind=None):
|
|
next_id = self.get_next_id()
|
|
gid_list = []
|
|
if isinstance(group_ids, list):
|
|
gid_list = [str(x).strip() for x in group_ids if x is not None and str(x).strip()]
|
|
doc = {
|
|
"name": name,
|
|
"names": names if names else [],
|
|
"group_ids": gid_list,
|
|
"preset_group_ids": {},
|
|
"presets": presets if presets else [],
|
|
"default_preset": None,
|
|
"brightness": 255,
|
|
}
|
|
if content_kind in ("presets", "sequences"):
|
|
doc["content_kind"] = content_kind
|
|
if "sequence_ids" not in doc:
|
|
doc["sequence_ids"] = []
|
|
self._enforce_content_kind_invariants(doc)
|
|
self[next_id] = doc
|
|
self.save()
|
|
return next_id
|
|
|
|
def read(self, id):
|
|
id_str = str(id)
|
|
return self.get(id_str, None)
|
|
|
|
def update(self, id, data):
|
|
id_str = str(id)
|
|
if id_str not in self:
|
|
return False
|
|
patch = dict(data) if isinstance(data, dict) else {}
|
|
self[id_str].update(patch)
|
|
self.save()
|
|
return True
|
|
|
|
def delete(self, id):
|
|
id_str = str(id)
|
|
if id_str not in self:
|
|
return False
|
|
self.pop(id_str)
|
|
self.save()
|
|
return True
|
|
|
|
def list(self):
|
|
return list(self.keys())
|