Source code for psc_sim.user_presets

"""Named user-defined parameter presets persisted beside settings.json."""

from __future__ import annotations

import json
import re
import uuid
from dataclasses import dataclass
from pathlib import Path

from psc_sim.parameters import CellParameters
from psc_sim.settings import settings_path
from psc_sim.validation import params_from_dict, params_to_dict


[docs] @dataclass class UserPreset: id: str label: str params: CellParameters note: str = ""
def user_presets_path() -> Path: return settings_path().parent / "user_presets.json" def _slug_id(label: str) -> str: base = re.sub(r"[^a-z0-9]+", "_", label.strip().lower()).strip("_") return base or f"preset_{uuid.uuid4().hex[:8]}" def load_user_presets() -> list[UserPreset]: path = user_presets_path() if not path.is_file(): return [] try: raw = json.loads(path.read_text(encoding="utf-8")) except (json.JSONDecodeError, OSError): return [] if not isinstance(raw, list): return [] out: list[UserPreset] = [] for item in raw: if not isinstance(item, dict): continue pid = str(item.get("id", "")).strip() label = str(item.get("label", "")).strip() if not pid or not label: continue try: params = params_from_dict(item["params"]) except (KeyError, TypeError, ValueError): continue note = str(item.get("note", "")) out.append(UserPreset(id=pid, label=label, params=params, note=note)) return out def save_user_presets(presets: list[UserPreset]) -> None: path = user_presets_path() path.parent.mkdir(parents=True, exist_ok=True) payload = [ { "id": p.id, "label": p.label, "note": p.note, "params": params_to_dict(p.params), } for p in presets ] path.write_text(json.dumps(payload, indent=2), encoding="utf-8") def add_user_preset( label: str, params: CellParameters, *, note: str = "", preset_id: str | None = None, ) -> UserPreset: presets = load_user_presets() pid = preset_id or _slug_id(label) existing = {p.id for p in presets} if pid in existing: pid = f"{pid}_{uuid.uuid4().hex[:6]}" entry = UserPreset(id=pid, label=label.strip(), params=params, note=note) presets.append(entry) save_user_presets(presets) return entry def rename_user_preset(preset_id: str, new_label: str) -> bool: presets = load_user_presets() label = new_label.strip() if not label: return False for p in presets: if p.id == preset_id: p.label = label save_user_presets(presets) return True return False def delete_user_preset(preset_id: str) -> bool: presets = load_user_presets() new_list = [p for p in presets if p.id != preset_id] if len(new_list) == len(presets): return False save_user_presets(new_list) return True def user_preset_by_id(preset_id: str) -> UserPreset: for p in load_user_presets(): if p.id == preset_id: return p raise KeyError(preset_id) def user_preset_item_data(preset_id: str) -> str: return f"user:{preset_id}"