fbc2deec45
Prepare the UI for English (default/fallback) and German with auto or manual locale selection, and report import issues with client-translated warnings instead of failing on minor save format changes. Co-authored-by: Cursor <cursoragent@cursor.com>
158 lines
4.6 KiB
Python
158 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
||
"""Idle Fantasy Save Viewer – local Flask server."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import json
|
||
import sys
|
||
import webbrowser
|
||
from pathlib import Path
|
||
|
||
from flask import Flask, jsonify, render_template, request
|
||
|
||
from db import (
|
||
DEFAULT_DB,
|
||
diff_snapshots,
|
||
get_latest_snapshot,
|
||
get_snapshot,
|
||
import_save,
|
||
init_db,
|
||
list_snapshots,
|
||
get_connection,
|
||
timeline,
|
||
)
|
||
app = Flask(__name__)
|
||
DB_PATH = DEFAULT_DB
|
||
|
||
|
||
@app.route("/")
|
||
def index():
|
||
return render_template("index.html")
|
||
|
||
|
||
@app.route("/api/snapshot/latest")
|
||
def api_latest():
|
||
data = get_latest_snapshot(db_path=DB_PATH)
|
||
if not data:
|
||
return jsonify({"error": "No snapshots imported yet"}), 404
|
||
return jsonify(data)
|
||
|
||
|
||
@app.route("/api/snapshot/<int:snapshot_id>")
|
||
def api_snapshot(snapshot_id: int):
|
||
data = get_snapshot(snapshot_id, db_path=DB_PATH)
|
||
if not data:
|
||
return jsonify({"error": "Snapshot not found"}), 404
|
||
return jsonify(data)
|
||
|
||
|
||
@app.route("/api/snapshots")
|
||
def api_snapshots():
|
||
return jsonify(list_snapshots(db_path=DB_PATH))
|
||
|
||
|
||
@app.route("/api/snapshots/<int:older_id>/diff/<int:newer_id>")
|
||
def api_diff(older_id: int, newer_id: int):
|
||
try:
|
||
return jsonify(diff_snapshots(older_id, newer_id, db_path=DB_PATH))
|
||
except ValueError as e:
|
||
return jsonify({"error": str(e)}), 404
|
||
|
||
|
||
@app.route("/api/timeline")
|
||
def api_timeline():
|
||
return jsonify(timeline(db_path=DB_PATH))
|
||
|
||
|
||
@app.route("/api/import", methods=["POST"])
|
||
def api_import():
|
||
if "file" in request.files:
|
||
f = request.files["file"]
|
||
if not f.filename:
|
||
return jsonify({"error": "No file selected"}), 400
|
||
tmp = Path(DB_PATH.parent) / f"_upload_{f.filename}"
|
||
f.save(tmp)
|
||
try:
|
||
result = import_save(tmp, db_path=DB_PATH)
|
||
finally:
|
||
tmp.unlink(missing_ok=True)
|
||
if result.get("error"):
|
||
return jsonify(result), 422
|
||
return jsonify(result)
|
||
|
||
body = request.get_json(silent=True) or {}
|
||
path = body.get("path")
|
||
if not path:
|
||
return jsonify({"error": "Provide file upload or JSON body with path"}), 400
|
||
path = Path(path)
|
||
if not path.exists():
|
||
return jsonify({"error": f"File not found: {path}"}), 404
|
||
result = import_save(path, db_path=DB_PATH)
|
||
if result.get("error"):
|
||
return jsonify(result), 422
|
||
return jsonify(result)
|
||
|
||
|
||
def _print_import_report(result: dict) -> None:
|
||
report = result.get("import_report") or []
|
||
if not report:
|
||
return
|
||
for item in report:
|
||
level = item.get("level", "info").upper()
|
||
print(f" [{level}] {item.get('message')}", file=sys.stderr)
|
||
|
||
|
||
def main() -> int:
|
||
parser = argparse.ArgumentParser(description="Idle Fantasy Save Viewer")
|
||
parser.add_argument("save_file", nargs="?", help="Save JSON to import on start")
|
||
parser.add_argument("--import", dest="import_file", metavar="FILE", help="Import save without starting server")
|
||
parser.add_argument("--port", type=int, default=5000)
|
||
parser.add_argument("--no-browser", action="store_true")
|
||
parser.add_argument("--db", type=Path, default=DEFAULT_DB, help="SQLite database path")
|
||
args = parser.parse_args()
|
||
|
||
global DB_PATH
|
||
DB_PATH = args.db
|
||
|
||
conn = get_connection(DB_PATH)
|
||
init_db(conn)
|
||
conn.close()
|
||
|
||
import_path = args.import_file or args.save_file
|
||
if import_path:
|
||
path = Path(import_path)
|
||
if not path.exists():
|
||
print(f"Error: file not found: {path}", file=sys.stderr)
|
||
return 1
|
||
result = import_save(path, db_path=DB_PATH)
|
||
if result.get("error"):
|
||
print(f"Import failed: {result['error']}", file=sys.stderr)
|
||
_print_import_report(result)
|
||
return 1
|
||
if result.get("imported"):
|
||
print(f"Imported snapshot #{result['snapshot_id']} from {path.name}")
|
||
summary = result.get("import_summary") or {}
|
||
if summary.get("warnings") or summary.get("infos"):
|
||
print(
|
||
f"Notes: {summary.get('warnings', 0)} warning(s), "
|
||
f"{summary.get('infos', 0)} info(s)",
|
||
file=sys.stderr,
|
||
)
|
||
_print_import_report(result)
|
||
else:
|
||
print(f"Skipped duplicate: {path.name} (snapshot #{result['snapshot_id']})")
|
||
|
||
if args.import_file and not args.save_file:
|
||
return 0
|
||
|
||
url = f"http://127.0.0.1:{args.port}"
|
||
print(f"Starting server at {url}")
|
||
if not args.no_browser:
|
||
webbrowser.open(url)
|
||
app.run(host="127.0.0.1", port=args.port, debug=False)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|