shaiwatcher/data_manager.py
Franz Rolfsvaag 4e86eb43fc 0.4.1.0.a2
- Docs site changes
  - Details brief no longer opens automatically on narrower devices
  - Implemented a counter that displays the number of executions for each command
2025-08-16 02:26:49 +02:00

104 lines
3.6 KiB
Python

# data_manager.py
import json
import threading
import shutil
import os
from typing import Callable, Any
class DataManager:
def __init__(self, json_path: str):
self.json_path = json_path
self.lock = threading.Lock()
self._data = self._load()
def _load(self):
try:
with open(self.json_path, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
default = {
'agreed_rules': [],
'agreed_engagement': [],
'agreed_nickname': [],
'nick_same_confirmed': [],
'nick_nudged': [],
'nick_dm_map': [],
'pirates': [],
'modlog': [],
'reports': [],
'encounters': [],
'vc_channels': [],
'user_cards': [],
'pirates_list_posts': [],
'spicepay_prefs': [],
'nick_verified': [],
'nick_claim_pending': [],
'nick_reviews': [],
'rr_msg_channels': [],
'_counters': {},
}
self._save(default)
return default
def _safe_write(self, data: dict):
os.makedirs(os.path.dirname(self.json_path) or ".", exist_ok=True)
tmp = self.json_path + ".tmp"
with open(tmp, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=4)
if os.path.exists(self.json_path):
try:
shutil.copy2(self.json_path, self.json_path + ".bak")
except Exception:
pass
os.replace(tmp, self.json_path)
def _save(self, data: dict):
self._safe_write(data)
# ------------- existing list helpers -------------
def get(self, category: str):
with self.lock:
return list(self._data.get(category, []))
def add(self, category: str, item: Any):
with self.lock:
self._data.setdefault(category, []).append(item)
self._save(self._data)
def remove(self, category: str, predicate: Callable[[Any], bool]):
with self.lock:
arr = self._data.get(category, [])
self._data[category] = [i for i in arr if not predicate(i)]
self._save(self._data)
def update(self, category: str, predicate: Callable[[Any], bool], updater: Callable[[dict], dict]) -> bool:
"""Atomically find one item in `category` and update it with `updater`."""
with self.lock:
arr = self._data.get(category, [])
for idx, item in enumerate(arr):
if predicate(item):
new_item = dict(item)
new_item = updater(new_item) or new_item
arr[idx] = new_item
self._data[category] = arr
self._save(self._data)
return True
return False
# ------------- NEW: tiny key→counter helpers -------------
def incr_counter(self, key: str, by: int = 1) -> int:
with self.lock:
c = self._data.setdefault('_counters', {})
c[key] = int(c.get(key, 0)) + int(by)
self._save(self._data)
return c[key]
def get_counter(self, key: str) -> int:
with self.lock:
return int(self._data.get('_counters', {}).get(key, 0))
def get_all_counters(self, prefix: str = "") -> dict[str, int]:
with self.lock:
c = dict(self._data.get('_counters', {}))
return {k: v for k, v in c.items() if (not prefix or k.startswith(prefix))}