Преглед на файлове

API configurazione GUI

master
Lorenzo Pollutri преди 1 ден
родител
ревизия
52cb4b88ea
променени са 5 файла, в които са добавени 149 реда и са изтрити 1 реда
  1. +1
    -0
      app.py
  2. +1
    -0
      logica_reslevis/config.py
  3. +88
    -0
      logica_reslevis/user_preferences.py
  4. +39
    -0
      routes/reslevis.py
  5. +20
    -1
      schemas/reslevis.py

+ 1
- 0
app.py Целия файл

@@ -154,6 +154,7 @@ async def local_then_core(request: Request, call_next):
internal_core_proxy_paths = {
"/reslevis/updateAlarm",
"/reslevis/updateCoreSettings",
"/reslevis/updateUserPreferences",
}
# only proxy CRUD for Reslevis (change prefix or methods if needed)
if (


+ 1
- 0
logica_reslevis/config.py Целия файл

@@ -27,4 +27,5 @@ TRACK_JSON_PATH = DATA_DIR / "tracks.json"
TRACKER_ZONE_JSON_PATH = DATA_DIR / "tracker_zone.json"
SETTING_JSON_PATH = DATA_DIR / "settings.json"
GUI_CONFIG_JSON_PATH = DATA_DIR / "gui_config.json"
USER_PREFERENCES_JSON_PATH = DATA_DIR / "user_preferences.json"


+ 88
- 0
logica_reslevis/user_preferences.py Целия файл

@@ -0,0 +1,88 @@
import json
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional
from uuid import NAMESPACE_URL, uuid5

from fastapi.encoders import jsonable_encoder

from schemas.reslevis import UserPreferencesUpdateItem
from .config import USER_PREFERENCES_JSON_PATH
from .gateway import _LockedFile, _atomic_write, _norm_str


def _model_to_dict(model: Any) -> Dict[str, Any]:
if hasattr(model, "model_dump"):
return model.model_dump(exclude_unset=True, exclude_none=True)
return model.dict(exclude_unset=True, exclude_none=True)


class UserPreferencesJsonRepository:
def __init__(self, json_path: str = USER_PREFERENCES_JSON_PATH):
self.path = json_path

def _read_all(self) -> List[Dict[str, Any]]:
with _LockedFile(self.path, "r") as fp:
try:
fp.seek(0)
data = fp.read().strip()
return json.loads(data) if data else []
except json.JSONDecodeError:
return []

def _write_all(self, rows: List[Dict[str, Any]]) -> None:
payload = json.dumps(rows, ensure_ascii=False, indent=2)
_atomic_write(self.path, payload)

def _index_by_uid(self, rows: List[Dict[str, Any]], uid: str) -> Optional[int]:
target = _norm_str(uid)
for i, row in enumerate(rows):
if _norm_str(row.get("uid")) == target:
return i
return None

def _default_for_uid(self, uid: str) -> Dict[str, Any]:
return {
"id": str(uuid5(NAMESPACE_URL, f"reslevis:user-preferences:{uid}")),
"uid": uid,
"language": "it",
"tables": {},
"updated_at": None,
}

def get(self, uid: str) -> Dict[str, Any]:
rows = self._read_all()
idx = self._index_by_uid(rows, uid)
if idx is None:
return self._default_for_uid(uid)

obj = self._default_for_uid(uid)
obj.update(rows[idx])
obj["uid"] = uid
obj["tables"] = obj.get("tables") or {}
obj["language"] = obj.get("language") or "it"
return obj

def update(self, uid: str, item: UserPreferencesUpdateItem) -> Dict[str, Any]:
rows = self._read_all()
idx = self._index_by_uid(rows, uid)
current = self._default_for_uid(uid) if idx is None else self.get(uid)
payload = jsonable_encoder(_model_to_dict(item))

if "language" in payload and payload["language"] is not None:
current["language"] = payload["language"]

if "tables" in payload and payload["tables"] is not None:
tables = current.get("tables") or {}
for table_name, table_preferences in payload["tables"].items():
tables[table_name] = table_preferences or {"hiddenColumns": {}}
current["tables"] = tables

current["uid"] = uid
current["updated_at"] = datetime.now(timezone.utc).isoformat()

if idx is None:
rows.append(current)
else:
rows[idx] = current
self._write_all(rows)
return current

+ 39
- 0
routes/reslevis.py Целия файл

@@ -24,6 +24,8 @@ from schemas.reslevis import (
TrackerZoneItem,
SettingItem,
GuiConfigItem,
UserPreferencesItem,
UserPreferencesUpdateItem,
CoreSettingsItem,
CoreSettingsUpdateItem,
)
@@ -37,6 +39,7 @@ from logica_reslevis.tracker import TrackerJsonRepository
from logica_reslevis.operator import OperatorJsonRepository
from logica_reslevis.setting import SettingJsonRepository
from logica_reslevis.gui_config import GuiConfigJsonRepository
from logica_reslevis.user_preferences import UserPreferencesJsonRepository
from logica_reslevis.subject import SubjectJsonRepository
from logica_reslevis.alarm import AlarmJsonRepository
from logica_reslevis.track import TrackJsonRepository
@@ -94,6 +97,7 @@ track_repo = TrackJsonRepository()
tracker_zone_repo = TrackerZoneJsonRepository()
setting_repo = SettingJsonRepository()
gui_config_repo = GuiConfigJsonRepository()
user_preferences_repo = UserPreferencesJsonRepository()

def _none_if_empty(v):
return None if v in ("", None, 0, "0") else v
@@ -155,6 +159,18 @@ def _normalize_zone(row: dict) -> dict:
row["groups"] = _uuid_list(row.get("groups"))
return row


def _uid_from_claims(claims: dict) -> str:
uid = (
claims.get("preferred_username")
or claims.get("username")
or claims.get("email")
or claims.get("sub")
)
if not uid:
raise HTTPException(status_code=401, detail="User identity not found in token")
return str(uid)

CORE_GET_SYNC = {
"/reslevis/getGateways": (gateway_repo, _normalize_gateway),
"/reslevis/getZones": (zone_repo, _normalize_zone),
@@ -604,6 +620,29 @@ def getGuiConfigs():
return gui_config_repo.list()


@router.get(
"/getUserPreferences",
response_model=UserPreferencesItem,
tags=["Reslevis"],
)
def getUserPreferences(current_user: dict = Depends(get_current_user)):
uid = _uid_from_claims(current_user)
return user_preferences_repo.get(uid)


@router.put(
"/updateUserPreferences",
response_model=UserPreferencesItem,
tags=["Reslevis"],
)
def updateUserPreferences(
item: UserPreferencesUpdateItem,
current_user: dict = Depends(get_current_user),
):
uid = _uid_from_claims(current_user)
return user_preferences_repo.update(uid, item)


@router.post("/postGuiConfig", tags=["Reslevis"], dependencies=[Depends(get_current_user)])
def postGuiConfig(item: GuiConfigItem):
gui_config_repo.add(item)


+ 20
- 1
schemas/reslevis.py Целия файл

@@ -1,9 +1,10 @@
from pydantic import BaseModel
from pydantic import BaseModel, Field
from typing import Optional
from uuid import UUID
from typing import Optional, Union, Literal
from typing import List
from typing import Tuple
from typing import Dict

class BuildingItem(BaseModel):
id: UUID
@@ -113,6 +114,24 @@ class GuiConfigItem(BaseModel):
role: Optional[Literal["developer", "administrator", "user"]] = None
debug: Optional[bool] = None


class TablePreferenceItem(BaseModel):
hiddenColumns: Dict[str, bool] = Field(default_factory=dict)


class UserPreferencesItem(BaseModel):
id: Optional[UUID] = None
uid: str
language: Literal["it", "en"] = "it"
tables: Dict[str, TablePreferenceItem] = Field(default_factory=dict)
updated_at: Optional[str] = None


class UserPreferencesUpdateItem(BaseModel):
language: Optional[Literal["it", "en"]] = None
tables: Optional[Dict[str, TablePreferenceItem]] = None


class OperatorItem(BaseModel):
id: UUID
name: str


Зареждане…
Отказ
Запис