diff --git a/logica_reslevis/__pycache__/config.cpython-310.pyc b/logica_reslevis/__pycache__/config.cpython-310.pyc index d68112b..7cb0816 100644 Binary files a/logica_reslevis/__pycache__/config.cpython-310.pyc and b/logica_reslevis/__pycache__/config.cpython-310.pyc differ diff --git a/logica_reslevis/__pycache__/floor.cpython-310.pyc b/logica_reslevis/__pycache__/floor.cpython-310.pyc new file mode 100644 index 0000000..2d7013f Binary files /dev/null and b/logica_reslevis/__pycache__/floor.cpython-310.pyc differ diff --git a/logica_reslevis/__pycache__/tracker_zone.cpython-310.pyc b/logica_reslevis/__pycache__/tracker_zone.cpython-310.pyc new file mode 100644 index 0000000..2b38b64 Binary files /dev/null and b/logica_reslevis/__pycache__/tracker_zone.cpython-310.pyc differ diff --git a/logica_reslevis/__pycache__/zone.cpython-310.pyc b/logica_reslevis/__pycache__/zone.cpython-310.pyc index 302001a..0b11b5d 100644 Binary files a/logica_reslevis/__pycache__/zone.cpython-310.pyc and b/logica_reslevis/__pycache__/zone.cpython-310.pyc differ diff --git a/logica_reslevis/config.py b/logica_reslevis/config.py index 33714e3..a26ff10 100644 --- a/logica_reslevis/config.py +++ b/logica_reslevis/config.py @@ -14,13 +14,14 @@ DATA_DIR = Path( ) # Percorsi dei file JSON -GATEWAY_JSON_PATH = DATA_DIR / "Gateway.json" -BUILDING_JSON_PATH = DATA_DIR / "Building.json" -PLAN_JSON_PATH = DATA_DIR / "Plan.json" -ZONE_JSON_PATH = DATA_DIR / "Zone.json" -TRACKER_JSON_PATH = DATA_DIR / "Tracker.json" -OPERATOR_JSON_PATH = DATA_DIR / "Operator.json" -SUBJECT_JSON_PATH = DATA_DIR / "Subject.json" -ALARM_JSON_PATH = DATA_DIR / "Alarm.json" -TRACK_JSON_PATH = DATA_DIR / "Track.json" +GATEWAY_JSON_PATH = DATA_DIR / "gateway_list.json" +BUILDING_JSON_PATH = DATA_DIR / "building.json" +FLOOR_JSON_PATH = DATA_DIR / "floors.json" +ZONE_JSON_PATH = DATA_DIR / "zone_list.json" +TRACKER_JSON_PATH = DATA_DIR / "tracker_conf.json" +OPERATOR_JSON_PATH = DATA_DIR / "oper_conf.json" +SUBJECT_JSON_PATH = DATA_DIR / "subject.json" +ALARM_JSON_PATH = DATA_DIR / "active_alarm.json" +TRACK_JSON_PATH = DATA_DIR / "tracks.json" +TRACKER_ZONE_JSON_PATH = DATA_DIR / "tracker_zone.json" diff --git a/logica_reslevis/plan.py b/logica_reslevis/floor.py similarity index 75% rename from logica_reslevis/plan.py rename to logica_reslevis/floor.py index 32a8181..5bcf120 100644 --- a/logica_reslevis/plan.py +++ b/logica_reslevis/floor.py @@ -3,13 +3,13 @@ from typing import List, Dict, Any, Optional from fastapi.encoders import jsonable_encoder -from schemas.reslevis import PlanItem -from .config import PLAN_JSON_PATH +from schemas.reslevis import FloorItem +from .config import FLOOR_JSON_PATH from .gateway import _LockedFile, _atomic_write, _norm_str, _index_by_id -class PlanJsonRepository: - def __init__(self, json_path: str = PLAN_JSON_PATH): +class FloorJsonRepository: + def __init__(self, json_path: str = FLOOR_JSON_PATH): self.path = json_path def _read_all(self) -> List[Dict[str, Any]]: @@ -39,34 +39,34 @@ class PlanJsonRepository: ] return rows - def add(self, item: PlanItem) -> None: + def add(self, item: FloorItem) -> None: rows = self._read_all() obj = jsonable_encoder(item) obj_id = _norm_str(obj.get("id")) if any(_norm_str(r.get("id")) == obj_id for r in rows): - raise ValueError(f"Plan con id '{obj_id}' già presente") + raise ValueError(f"Floor con id '{obj_id}' già presente") rows.append(obj) self._write_all(rows) - def update(self, item: PlanItem) -> None: + def update(self, item: FloorItem) -> None: rows = self._read_all() obj = jsonable_encoder(item) obj_id = _norm_str(obj.get("id")) idx = _index_by_id(rows, obj_id) if idx is None: - raise ValueError(f"Plan con id '{obj_id}' non trovato") + raise ValueError(f"Floor con id '{obj_id}' non trovato") rows[idx] = obj self._write_all(rows) - def remove(self, plan_id: str) -> None: + def remove(self, floor_id: str) -> None: rows = self._read_all() - idx = _index_by_id(rows, plan_id) + idx = _index_by_id(rows, floor_id) if idx is None: - raise ValueError(f"Plan con id '{plan_id}' non trovato") + raise ValueError(f"Floor con id '{floor_id}' non trovato") del rows[idx] self._write_all(rows) diff --git a/logica_reslevis/tracker_zone.py b/logica_reslevis/tracker_zone.py new file mode 100644 index 0000000..a943409 --- /dev/null +++ b/logica_reslevis/tracker_zone.py @@ -0,0 +1,76 @@ +import json +from typing import List, Dict, Any, Optional +from fastapi.encoders import jsonable_encoder + +from schemas.reslevis import TrackerZoneItem +from .config import TRACKER_ZONE_JSON_PATH +from .gateway import _LockedFile, _atomic_write, _norm_str, _index_by_id + + +class TrackerZoneJsonRepository: + def __init__(self, json_path: str = TRACKER_ZONE_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 list(self, search_string: Optional[str] = None) -> List[Dict[str, Any]]: + rows = self._read_all() + if search_string: + s = search_string.lower() + rows = [ + r + for r in rows + if any( + isinstance(r.get(k), str) and s in (r.get(k) or "").lower() + for k in ("days", "time", "id", "tracker") + ) + or any( + s in str(z).lower() + for z in (r.get("zoneList") or []) + ) + ] + return rows + + def add(self, item: TrackerZoneItem) -> None: + rows = self._read_all() + obj = jsonable_encoder(item) + obj_id = _norm_str(obj.get("id")) + + if any(_norm_str(r.get("id")) == obj_id for r in rows): + raise ValueError(f"TrackerZone con id '{obj_id}' già presente") + + rows.append(obj) + self._write_all(rows) + + def update(self, item: TrackerZoneItem) -> None: + rows = self._read_all() + obj = jsonable_encoder(item) + obj_id = _norm_str(obj.get("id")) + + idx = _index_by_id(rows, obj_id) + if idx is None: + raise ValueError(f"TrackerZone con id '{obj_id}' non trovato") + + rows[idx] = obj + self._write_all(rows) + + def remove(self, tracker_zone_id: str) -> None: + rows = self._read_all() + idx = _index_by_id(rows, tracker_zone_id) + if idx is None: + raise ValueError(f"TrackerZone con id '{tracker_zone_id}' non trovato") + + del rows[idx] + self._write_all(rows) + diff --git a/models/__pycache__/gateway_item.cpython-310.pyc b/models/__pycache__/gateway_item.cpython-310.pyc index d21116b..dff9d86 100644 Binary files a/models/__pycache__/gateway_item.cpython-310.pyc and b/models/__pycache__/gateway_item.cpython-310.pyc differ diff --git a/models/plan_item.py b/models/floor_item.py similarity index 93% rename from models/plan_item.py rename to models/floor_item.py index 228d7ba..2714522 100644 --- a/models/plan_item.py +++ b/models/floor_item.py @@ -7,12 +7,12 @@ from attrs import field as _attrs_field from app_types import UNSET, Unset -T = TypeVar("T", bound="PlanItem") +T = TypeVar("T", bound="FloorItem") @_attrs_define -class PlanItem: - """A plan is floor or a space of a building +class FloorItem: + """A floor is space of a building Attributes: id (UUID): ID @@ -77,7 +77,7 @@ class PlanItem: else: building = UUID(_building) - plan_item = cls( + floor_item = cls( id=id, name=name, image=image, @@ -85,8 +85,8 @@ class PlanItem: building=building, ) - plan_item.additional_properties = d - return plan_item + floor_item.additional_properties = d + return floor_item @property def additional_keys(self) -> list[str]: diff --git a/models/gateway_item.py b/models/gateway_item.py index 26308ae..a05535a 100644 --- a/models/gateway_item.py +++ b/models/gateway_item.py @@ -24,7 +24,7 @@ class GatewayItem: position (Union[Unset, str]): Position x (Union[Unset, float]): X y (Union[Unset, float]): Y - zone (Union[Unset, UUID]): Zone + floor (Union[Unset, UUID]): Zone building (Union[Unset, UUID]): Building notes (Union[Unset, str]): Notes """ @@ -38,7 +38,7 @@ class GatewayItem: position: Union[Unset, str] = UNSET x: Union[Unset, float] = UNSET y: Union[Unset, float] = UNSET - zone: Union[Unset, UUID] = UNSET + floor: UUID = UNSET building: Union[Unset, UUID] = UNSET notes: Union[Unset, str] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -62,9 +62,7 @@ class GatewayItem: y = self.y - zone: Union[Unset, str] = UNSET - if not isinstance(self.zone, Unset): - zone = str(self.zone) + floor = self.floor building: Union[Unset, str] = UNSET if not isinstance(self.building, Unset): @@ -94,8 +92,8 @@ class GatewayItem: field_dict["x"] = x if y is not UNSET: field_dict["y"] = y - if zone is not UNSET: - field_dict["zone"] = zone + if floor is not UNSET: + field_dict["floor"] = floor if building is not UNSET: field_dict["building"] = building if notes is not UNSET: @@ -124,12 +122,7 @@ class GatewayItem: y = d.pop("y", UNSET) - _zone = d.pop("zone", UNSET) - zone: Union[Unset, UUID] - if isinstance(_zone, Unset): - zone = UNSET - else: - zone = UUID(_zone) + floor = d.pop("floor", UNSET) _building = d.pop("building", UNSET) building: Union[Unset, UUID] @@ -150,7 +143,7 @@ class GatewayItem: position=position, x=x, y=y, - zone=zone, + floor=floor, building=building, notes=notes, ) diff --git a/routes/__pycache__/reslevis.cpython-310.pyc b/routes/__pycache__/reslevis.cpython-310.pyc index f90c75d..494e849 100644 Binary files a/routes/__pycache__/reslevis.cpython-310.pyc and b/routes/__pycache__/reslevis.cpython-310.pyc differ diff --git a/routes/reslevis.py b/routes/reslevis.py index e264681..6c9e6a6 100644 --- a/routes/reslevis.py +++ b/routes/reslevis.py @@ -3,7 +3,7 @@ from typing import List from schemas.reslevis import ( BuildingItem, - PlanItem, + FloorItem, ZoneItem, GatewayItem, TrackerItem, @@ -11,17 +11,19 @@ from schemas.reslevis import ( SubjectItem, AlarmItem, TrackItem, + TrackerZoneItem, ) from logica_reslevis.gateway import GatewayJsonRepository from logica_reslevis.building import BuildingJsonRepository -from logica_reslevis.plan import PlanJsonRepository +from logica_reslevis.floor import FloorJsonRepository from logica_reslevis.zone import ZoneJsonRepository from logica_reslevis.tracker import TrackerJsonRepository from logica_reslevis.operator import OperatorJsonRepository from logica_reslevis.subject import SubjectJsonRepository from logica_reslevis.alarm import AlarmJsonRepository from logica_reslevis.track import TrackJsonRepository +from logica_reslevis.tracker_zone import TrackerZoneJsonRepository from security import get_current_user @@ -29,13 +31,14 @@ router = APIRouter() gateway_repo = GatewayJsonRepository() building_repo = BuildingJsonRepository() -plan_repo = PlanJsonRepository() +floor_repo = FloorJsonRepository() zone_repo = ZoneJsonRepository() tracker_repo = TrackerJsonRepository() operator_repo = OperatorJsonRepository() subject_repo = SubjectJsonRepository() alarm_repo = AlarmJsonRepository() track_repo = TrackJsonRepository() +tracker_zone_repo = TrackerZoneJsonRepository() @router.get( @@ -43,61 +46,27 @@ track_repo = TrackJsonRepository() response_model=List[GatewayItem], tags=["Reslevis"], dependencies=[Depends(get_current_user)], - summary="getGateway", ) def getGateways(): return gateway_repo.list() -@router.post( - "/postGateway", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postGateway", -) +@router.post("/postGateway", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def postGateway(item: GatewayItem): - try: - gateway_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") + gateway_repo.add(item) + return {"message": "OK"} -@router.put( - "/updateGateway", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putGateway", -) +@router.put("/updateGateway", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def updateGateway(item: GatewayItem): - try: - gateway_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removeGateway/{gateway_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removeGateway", -) + gateway_repo.update(item) + return {"message": "OK"} + + +@router.delete("/removeGateway/{gateway_id}", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def removeGateway(gateway_id: str): - try: - gateway_repo.remove(gateway_id) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") + gateway_repo.remove(gateway_id) + return {"message": "OK"} @router.get( @@ -105,123 +74,55 @@ def removeGateway(gateway_id: str): response_model=List[BuildingItem], tags=["Reslevis"], dependencies=[Depends(get_current_user)], - summary="getBuilding", ) def getBuildings(): return building_repo.list() -@router.post( - "/postBuilding", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postBuilding", -) +@router.post("/postBuilding", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def postBuilding(item: BuildingItem): - try: - building_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") + building_repo.add(item) + return {"message": "OK"} -@router.put( - "/updateBuilding", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putBuilding", -) +@router.put("/updateBuilding", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def updateBuilding(item: BuildingItem): - try: - building_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removeBuilding/{building_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removeBuilding", -) + building_repo.update(item) + return {"message": "OK"} + + +@router.delete("/removeBuilding/{building_id}", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def removeBuilding(building_id: str): - try: - building_repo.remove(building_id) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") + building_repo.remove(building_id) + return {"message": "OK"} @router.get( - "/getPlans", - response_model=List[PlanItem], + "/getFloors", + response_model=List[FloorItem], tags=["Reslevis"], dependencies=[Depends(get_current_user)], - summary="getPlan", ) -def getPlans(): - return plan_repo.list() +def getFloors(): + return floor_repo.list() -@router.post( - "/postPlan", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postPlan", -) -def postPlan(item: PlanItem): - try: - plan_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.put( - "/updatePlan", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putPlan", -) -def updatePlan(item: PlanItem): - try: - plan_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removePlan/{plan_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removePlan", -) -def removePlan(plan_id: str): - try: - plan_repo.remove(plan_id) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") +@router.post("/postFloor", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) +def postFloor(item: FloorItem): + floor_repo.add(item) + return {"message": "OK"} + + +@router.put("/updateFloor", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) +def updateFloor(item: FloorItem): + floor_repo.update(item) + return {"message": "OK"} + + +@router.delete("/removeFloor/{floor_id}", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) +def removeFloor(floor_id: str): + floor_repo.remove(floor_id) + return {"message": "OK"} @router.get( @@ -229,61 +130,27 @@ def removePlan(plan_id: str): response_model=List[ZoneItem], tags=["Reslevis"], dependencies=[Depends(get_current_user)], - summary="getZone", ) def getZones(): return zone_repo.list() -@router.post( - "/postZone", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postZone", -) +@router.post("/postZone", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def postZone(item: ZoneItem): - try: - zone_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") + zone_repo.add(item) + return {"message": "OK"} -@router.put( - "/updateZone", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putZone", -) +@router.put("/updateZone", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def updateZone(item: ZoneItem): - try: - zone_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removeZone/{zone_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removeZone", -) + zone_repo.update(item) + return {"message": "OK"} + + +@router.delete("/removeZone/{zone_id}", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def removeZone(zone_id: str): - try: - zone_repo.remove(zone_id) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") + zone_repo.remove(zone_id) + return {"message": "OK"} @router.get( @@ -291,307 +158,53 @@ def removeZone(zone_id: str): response_model=List[TrackerItem], tags=["Reslevis"], dependencies=[Depends(get_current_user)], - summary="getTracker", ) def getTrackers(): return tracker_repo.list() -@router.post( - "/postTracker", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postTracker", -) +@router.post("/postTracker", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def postTracker(item: TrackerItem): - try: - tracker_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") + tracker_repo.add(item) + return {"message": "OK"} -@router.put( - "/updateTracker", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putTracker", -) +@router.put("/updateTracker", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def updateTracker(item: TrackerItem): - try: - tracker_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removeTracker/{tracker_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removeTracker", -) -def removeTracker(tracker_id: str): - try: - tracker_repo.remove(tracker_id) - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") + tracker_repo.update(item) return {"message": "OK"} -@router.get( - "/getOperators", - response_model=List[OperatorItem], - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="getOperator", -) -def getOperators(): - return operator_repo.list() - - -@router.post( - "/postOperator", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postOperator", -) -def postOperator(item: OperatorItem): - try: - operator_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.put( - "/updateOperator", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putOperator", -) -def updateOperator(item: OperatorItem): - try: - operator_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removeOperator/{operator_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removeOperator", -) -def removeOperator(operator_id: str): - try: - operator_repo.remove(operator_id) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.get( - "/getSubjects", - response_model=List[SubjectItem], - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="getSubject", -) -def getSubjects(): - return subject_repo.list() - - -@router.post( - "/postSubject", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postSubject", -) -def postSubject(item: SubjectItem): - try: - subject_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.put( - "/updateSubject", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putSubject", -) -def updateSubject(item: SubjectItem): - try: - subject_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removeSubject/{subject_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removeSubject", -) -def removeSubject(subject_id: str): - try: - subject_repo.remove(subject_id) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") +@router.delete("/removeTracker/{tracker_id}", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) +def removeTracker(tracker_id: str): + tracker_repo.remove(tracker_id) + return {"message": "OK"} @router.get( - "/getAlarms", - response_model=List[AlarmItem], + "/getTrackerZones", + response_model=List[TrackerZoneItem], tags=["Reslevis"], dependencies=[Depends(get_current_user)], - summary="getAlarm", ) -def getAlarms(): - return alarm_repo.list() +def getTrackerZones(): + return tracker_zone_repo.list() -@router.post( - "/postAlarm", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postAlarm", -) -def postAlarm(item: AlarmItem): - try: - alarm_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.put( - "/updateAlarm", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putAlarm", -) -def updateAlarm(item: AlarmItem): - try: - alarm_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removeAlarm/{alarm_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removeAlarm", -) -def removeAlarm(alarm_id: str): - try: - alarm_repo.remove(alarm_id) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") +@router.post("/postTrackerZone", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) +def postTrackerZone(item: TrackerZoneItem): + tracker_zone_repo.add(item) + return {"message": "OK"} -@router.get( - "/getTracks", - response_model=List[TrackItem], - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="getTrack", -) -def getTracks(): - return track_repo.list() +@router.put("/updateTrackerZone", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) +def updateTrackerZone(item: TrackerZoneItem): + tracker_zone_repo.update(item) + return {"message": "OK"} -@router.post( - "/postTrack", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="postTrack", -) -def postTrack(item: TrackItem): - try: - track_repo.add(item) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=409, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.put( - "/updateTrack", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="putTrack", -) -def updateTrack(item: TrackItem): - try: - track_repo.update(item) - return {"message": "OK"} - except ValueError as e: - msg = str(e) - if "non trovato" in msg: - raise HTTPException(status_code=404, detail=msg) - raise HTTPException(status_code=409, detail=msg) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") - - -@router.delete( - "/removeTrack/{track_id}", - tags=["Reslevis"], - dependencies=[Depends(get_current_user)], - summary="removeTrack", -) -def removeTrack(track_id: str): - try: - track_repo.remove(track_id) - return {"message": "OK"} - except ValueError as e: - raise HTTPException(status_code=404, detail=str(e)) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Errore interno: {e}") +@router.delete("/removeTrackerZone/{tracker_zone_id}", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) +def removeTrackerZone(tracker_zone_id: str): + tracker_zone_repo.remove(tracker_zone_id) + return {"message": "OK"} diff --git a/schemas/__pycache__/reslevis.cpython-310.pyc b/schemas/__pycache__/reslevis.cpython-310.pyc index ce97c91..242228c 100644 Binary files a/schemas/__pycache__/reslevis.cpython-310.pyc and b/schemas/__pycache__/reslevis.cpython-310.pyc differ diff --git a/schemas/reslevis.py b/schemas/reslevis.py index dda2db5..f0ef195 100644 --- a/schemas/reslevis.py +++ b/schemas/reslevis.py @@ -2,6 +2,7 @@ from pydantic import BaseModel from typing import Optional from uuid import UUID from typing import Optional, Union +from typing import List class BuildingItem(BaseModel): id: UUID @@ -11,7 +12,7 @@ class BuildingItem(BaseModel): latitude: Optional[float] = None longitude: Optional[float] = None -class PlanItem(BaseModel): +class FloorItem(BaseModel): id: UUID name: str image: Optional[str] = None @@ -21,12 +22,12 @@ class PlanItem(BaseModel): class ZoneItem(BaseModel): id: UUID name: str - groups: Optional[str] = None - plan: Optional[UUID] = None + groups: List[UUID] + floor: Optional[UUID] = None building: Optional[UUID] = None class GatewayItem(BaseModel): - id: str + id: UUID name: str mac: Optional[str] = None status: Optional[Union[str, bool]] = None @@ -36,8 +37,8 @@ class GatewayItem(BaseModel): x: Optional[float] = None y: Optional[float] = None notes: Optional[str] = None - zone: Optional[str] = None - building: Optional[str] = None + floor: Optional[UUID] = None + building: Optional[UUID] = None class TrackerItem(BaseModel): id: UUID @@ -49,11 +50,11 @@ class TrackerItem(BaseModel): notes: Optional[str] = None x: Optional[float] = None y: Optional[float] = None - zone: Optional[UUID] = None + floor: Optional[UUID] = None building: Optional[UUID] = None class OperatorItem(BaseModel): - id: str + id: UUID name: str phone: Optional[str] = None zones: Optional[str] = None @@ -62,7 +63,7 @@ class OperatorItem(BaseModel): building: Optional[UUID] = None class SubjectItem(BaseModel): - id: str + id: UUID name: str role: Optional[str] = None phone: Optional[str] = None @@ -72,7 +73,7 @@ class SubjectItem(BaseModel): building: Optional[UUID] = None class AlarmItem(BaseModel): - id: str + id: UUID timestamp: Optional[str] = None type: Optional[str] = None status: Optional[str] = None @@ -89,7 +90,7 @@ class AlarmItem(BaseModel): building: Optional[UUID] = None class TrackItem(BaseModel): - id: str + id: UUID timestamp: Optional[str] = None type: Optional[str] = None status: Optional[str] = None @@ -99,8 +100,13 @@ class TrackItem(BaseModel): trackerMac: Optional[str] = None subject: Optional[str] = None subjectName: Optional[str] = None - zone: Optional[str] = None - zoneName: Optional[str] = None + floor: Optional[str] = None signal: Optional[float] = None building: Optional[UUID] = None +class TrackerZoneItem(BaseModel): + id: UUID + zoneList: List[UUID] + tracker: UUID + days: Optional[str] = None + time: Optional[str] = None diff --git a/security.py.ok b/security.py.ok deleted file mode 100644 index 04e4543..0000000 --- a/security.py.ok +++ /dev/null @@ -1,105 +0,0 @@ -# security.py -from typing import Dict, Any, List, Optional -import os -import logging -import httpx -import config_env -from jose import jwt, JWTError -from fastapi import HTTPException, status, Depends -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials - -logger = logging.getLogger("security") - -# === CONFIG === -#KEYCLOAK_ISSUER = os.getenv( -# "KEYCLOAK_ISSUER", -# "https://10.251.0.30:10002/realms/API.Server.local", - #"https://192.168.1.3:10002/realms/API.Server.local", -#) -#KEYCLOAK_JWKS_URL = os.getenv( -# "KEYCLOAK_JWKS_URL", -# "https://10.251.0.30:10002/realms/API.Server.local/protocol/openid-connect/certs", - #"https://192.168.1.3:10002/realms/API.Server.local/protocol/openid-connect/certs", -#) - -KEYCLOAK_ISSUER = config_env.KEYCLOAK_ISSUER -KEYCLOAK_JWKS_URL = config_env.KEYCLOAK_JWKS_URL - -KEYCLOAK_AUDIENCE = os.getenv("KEYCLOAK_AUDIENCE", "Fastapi") - -ALGORITHMS = ["RS256", "RS384", "RS512", "PS256", "PS384", "PS512"] - -# Per test con certificato self-signed. In prod: metti verify="/path/CA.crt" -_http = httpx.AsyncClient(timeout=5.0, verify=False) - -_cached_jwks: Optional[Dict[str, Any]] = None - -# NON chiamarla 'security' per evitare conflitti col nome del modulo. -http_bearer = HTTPBearer(auto_error=True) - - -async def _get_jwks() -> Dict[str, Any]: - global _cached_jwks - if _cached_jwks is None: - logger.info(f"Fetching JWKS from: {KEYCLOAK_JWKS_URL}") - resp = await _http.get(KEYCLOAK_JWKS_URL) - resp.raise_for_status() - _cached_jwks = resp.json() - return _cached_jwks - - -async def _get_key(token: str) -> Dict[str, Any]: - headers = jwt.get_unverified_header(token) - kid = headers.get("kid") - jwks = await _get_jwks() - for key in jwks.get("keys", []): - if key.get("kid") == kid: - return key - # chiave ruotata? invalida la cache e riprova - global _cached_jwks - _cached_jwks = None - jwks = await _get_jwks() - for key in jwks.get("keys", []): - if key.get("kid") == kid: - return key - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Signing key not found") - - -async def verify_token(token: str) -> Dict[str, Any]: - try: - key = await _get_key(token) - claims = jwt.decode( - token, - key, - algorithms=ALGORITHMS, - audience=KEYCLOAK_AUDIENCE, - issuer=KEYCLOAK_ISSUER, - options={"verify_aud": True, "verify_iss": True}, - ) - return claims - except JWTError as e: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e)) - - -async def get_current_user( - credentials: HTTPAuthorizationCredentials = Depends(http_bearer), -) -> Dict[str, Any]: - token = credentials.credentials - return await verify_token(token) - - -def require_roles(*roles: str): - async def checker(claims: Dict[str, Any] = Depends(get_current_user)) -> Dict[str, Any]: - # ruoli realm - realm_roles: List[str] = (claims.get("realm_access") or {}).get("roles", []) or [] - # ruoli client - client_roles: List[str] = [] - for v in (claims.get("resource_access") or {}).values(): - client_roles += v.get("roles", []) - have = set(realm_roles + client_roles) - missing = [r for r in roles if r not in have] - if missing: - raise HTTPException(status_code=403, detail=f"Missing roles: {missing}") - return claims - return checker -