import uuid import time from typing import Iterable class InterventionDraftManager: SESSION_KEY = 'intervention_drafts' EXPIRATION_SECONDS = 3600 # 1h def __init__(self, session): self.session = session if self.SESSION_KEY not in self.session: self.session[self.SESSION_KEY] = {} def _touch(self, draft): draft['timestamp'] = time.time() self.session.modified = True def get_or_create(self, draft_id=None): self.cleanup() drafts = self.session[self.SESSION_KEY] if not draft_id or draft_id not in drafts: draft_id = str(uuid.uuid4()) drafts[draft_id] = { 'form_data': {}, 'assets': [], # liste d'objets {"model","object_id"} 'locations': [], # si besoin, même principe {"id": ...} ou {"model","object_id"} 'timestamp': time.time() } self.session.modified = True return draft_id def get(self, draft_id): self.cleanup() return self.session[self.SESSION_KEY].get(draft_id, {}) def update_form_data(self, draft_id, form_data: dict): draft = self.get(draft_id) if draft: draft['form_data'] = form_data or {} self._touch(draft) # --- Helpers de normalisation --- @staticmethod def _norm_assets(items: Iterable): """Normalise en [{'model': str, 'object_id': int}, ...] + dédup.""" norm = [] seen = set() for i, a in enumerate(items or []): if isinstance(a, dict) and 'model' in a and 'object_id' in a: model = str(a['model']).lower().strip() oid = int(a['object_id']) key = (model, oid) if key not in seen: seen.add(key) norm.append({'model': model, 'object_id': oid, 'asset_type': model}) else: # Compat : si ancien format [id, id, ...], on ignore (impossible d’inférer le model) # => à migrer côté appelant continue return norm def update_assets(self, draft_id, assets_list): draft = self.get(draft_id) if draft is not None: draft['assets'] = self._norm_assets(assets_list) self._touch(draft) def update_locations(self, draft_id, locations_ids): draft = self.get(draft_id) if draft is not None: ids = [] for x in (locations_ids or []): try: v = int(str(x).strip()) # gère '', ' 12 ', etc. except (ValueError, TypeError): continue # ignore les invalides if v > 0: # optionnel: n'autoriser que des ids positifs ids.append(v) draft['locations'] = ids self._touch(draft) def remove_location(self, draft_id, location_id): """Supprime une localisation spécifique du draft""" draft = self.get(draft_id) if draft is not None: try: location_id = int(location_id) draft['locations'] = [loc_id for loc_id in draft.get('locations', []) if loc_id != location_id] self._touch(draft) return True except (ValueError, TypeError): return False return False def remove_asset(self, draft_id, asset_model, asset_id): """Supprime un asset spécifique du draft""" draft = self.get(draft_id) if draft is not None: try: asset_id = int(asset_id) asset_model = str(asset_model).lower().strip() draft['assets'] = [ a for a in draft.get('assets', []) if not (a.get('model') == asset_model and a.get('object_id') == asset_id) ] self._touch(draft) return True except (ValueError, TypeError): return False return False def delete(self, draft_id): self.session[self.SESSION_KEY].pop(draft_id, None) self.session.modified = True def cleanup(self): now = time.time() expired = [k for k, v in self.session[self.SESSION_KEY].items() if v.get('timestamp', 0) < now - self.EXPIRATION_SECONDS] for k in expired: self.session[self.SESSION_KEY].pop(k, None) if expired: self.session.modified = True