Files
encounterflow/encounterflow/storage.py

123 lines
4.3 KiB
Python

from __future__ import annotations
import json, os, uuid
from typing import List, Dict
from flask import current_app
from .models import Actor, Preset, PresetGroup, CONDITION_ICON_FILES, CURATED_ICON_SLUGS
PLACEHOLDER_BG = '#334155'
PLACEHOLDER_FG = '#e2e8f0'
GAME_ICONS_BASE = 'https://raw.githubusercontent.com/game-icons/icons/master'
def paths():
return dict(
STATE=current_app.config["STATE_PATH"],
PRESETS=current_app.config["PRESETS_PATH"],
GROUPS=current_app.config["PRESET_GROUPS_PATH"],
AVATARS=current_app.config["AVATAR_DIR"],
ICONS=current_app.config["ICON_DIR"],
DATA=current_app.config["DATA_DIR"],
)
def save_state(state) -> None:
p = paths()
tmp = {
'actors': [a.__dict__ for a in state.actors],
'turn_idx': state.turn_idx,
'round': state.round,
'visible': state.visible,
'dead_mode': state.dead_mode,
}
with open(p["STATE"], 'w', encoding='utf-8') as f:
json.dump(tmp, f, ensure_ascii=False, indent=2)
def load_state(state) -> None:
p = paths()
if not os.path.exists(p["STATE"]):
return
try:
with open(p["STATE"], 'r', encoding='utf-8') as f:
data = json.load(f)
state.actors = [Actor(**a) for a in data.get('actors', [])]
state.turn_idx = int(data.get('turn_idx', 0))
state.round = int(data.get('round', 1))
state.visible = bool(data.get('visible', True))
state.dead_mode = str(data.get('dead_mode', 'normal'))
state.normalize()
state.touch()
except Exception:
pass
def save_presets(items: List[Preset]) -> None:
p = paths()
with open(p["PRESETS"], 'w', encoding='utf-8') as f:
json.dump([x.__dict__ for x in items], f, ensure_ascii=False, indent=2)
def load_presets() -> List[Preset]:
p = paths()
if not os.path.exists(p["PRESETS"]):
return []
try:
with open(p["PRESETS"], 'r', encoding='utf-8') as f:
arr = json.load(f)
return [Preset(**x) for x in arr]
except Exception:
return []
def save_groups(items: List[PresetGroup]) -> None:
p = paths()
with open(p["GROUPS"], 'w', encoding='utf-8') as f:
json.dump([x.__dict__ for x in items], f, ensure_ascii=False, indent=2)
def load_groups() -> List[PresetGroup]:
p = paths()
if not os.path.exists(p["GROUPS"]):
return []
try:
with open(p["GROUPS"], 'r', encoding='utf-8') as f:
arr = json.load(f)
return [PresetGroup(**x) for x in arr]
except Exception:
return []
def ensure_default_icons() -> None:
p = paths()
os.makedirs(p["ICONS"], exist_ok=True)
for c, fn in CONDITION_ICON_FILES.items():
dest = os.path.join(p["ICONS"], fn)
if os.path.exists(dest):
continue
letter = (c[:1] or '?').upper()
svg = f"""<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'>
<rect rx='6' width='32' height='32' fill='{PLACEHOLDER_BG}' />
<text x='16' y='21' font-family='sans-serif' font-size='16' text-anchor='middle' fill='{PLACEHOLDER_FG}'>{letter}</text>
</svg>"""
try:
with open(dest, 'w', encoding='utf-8') as f:
f.write(svg)
except Exception:
pass
def seed_curated_icons(overwrite: bool = True) -> list[dict]:
ensure_default_icons()
out = []
try:
from urllib.request import urlopen
from urllib.error import URLError, HTTPError
p = paths()
for cond, (author, slug) in CURATED_ICON_SLUGS.items():
dest = os.path.join(p["ICONS"], f'{cond}.svg')
url = f"https://raw.githubusercontent.com/game-icons/icons/master/{author}/{slug}.svg"
try:
with urlopen(url, timeout=10) as r:
svg = r.read()
if (not os.path.exists(dest)) or overwrite:
with open(dest, 'wb') as f:
f.write(svg)
out.append({'condition': cond, 'status': 'downloaded'})
else:
out.append({'condition': cond, 'status': 'exists'})
except (URLError, HTTPError) as e:
out.append({'condition': cond, 'status': f'fallback ({e.__class__.__name__})'})
finally:
return out