first commit
This commit is contained in:
293
webapp/routes/cookbook.py
Normal file
293
webapp/routes/cookbook.py
Normal file
@@ -0,0 +1,293 @@
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Tuple
|
||||
from flask import Blueprint, request, jsonify
|
||||
import time
|
||||
|
||||
# Services
|
||||
from webapp.services.ccookbook import (
|
||||
call_cookbook_build_cost,
|
||||
call_eve_ref_materials,
|
||||
)
|
||||
# Storage
|
||||
from webapp.storage.orders import (
|
||||
load_orders, update_order,
|
||||
cache_costs, cache_materials,
|
||||
)
|
||||
|
||||
bp = Blueprint("cookbook", __name__, url_prefix="/api")
|
||||
|
||||
def _now() -> str:
|
||||
return time.strftime("%Y-%m-%dT%H:%M:%S%z", time.gmtime())
|
||||
|
||||
# ----------------------------- Helpers ----------------------------------------
|
||||
|
||||
def _arg(name: str, default: str = "") -> str:
|
||||
v = request.args.get(name)
|
||||
return v if v is not None else default
|
||||
|
||||
def _to_int(s: str, default: int = 0) -> int:
|
||||
try:
|
||||
return int(float(s))
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
def _order_by_id(order_id: str) -> Dict[str, Any] | None:
|
||||
for o in load_orders():
|
||||
if o.get("id") == order_id:
|
||||
return o
|
||||
return None
|
||||
|
||||
# ----------------------------- Endpoints – Raw --------------------------------
|
||||
|
||||
@bp.get("/cookbook/build_cost")
|
||||
def api_build_cost():
|
||||
"""Kosten via evecookbook (Preise, Gesamt etc.)."""
|
||||
bps = request.args.getlist("blueprintTypeId")
|
||||
if not bps and request.args.get("blueprintTypeId"):
|
||||
bps = [request.args.get("blueprintTypeId")]
|
||||
|
||||
if not bps:
|
||||
return jsonify({"error": "missing blueprintTypeId"}), 400
|
||||
|
||||
try:
|
||||
data = call_cookbook_build_cost(
|
||||
blueprint_type_ids=bps,
|
||||
quantity=_to_int(_arg("quantity", "1")),
|
||||
price_mode=_arg("priceMode", "buy"),
|
||||
additional_costs=_to_int(_arg("additionalCosts", "0")),
|
||||
base_me=_to_int(_arg("baseMe", "0")),
|
||||
components_me=_to_int(_arg("componentsMe", "0")),
|
||||
system=_arg("system", "Jita"),
|
||||
facility_tax=_to_int(_arg("facilityTax", "0")),
|
||||
industry_structure_type=_arg("industryStructureType", "Station"),
|
||||
industry_rig=_arg("industryRig", "None"),
|
||||
reaction_structure_type=_arg("reactionStructureType", "Athanor"),
|
||||
reaction_rig=_arg("reactionRig", "None"),
|
||||
reaction_flag=_arg("reactionFlag", ""),
|
||||
blueprint_version=_arg("blueprintVersion", "tq"),
|
||||
)
|
||||
return jsonify(data)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 502
|
||||
|
||||
|
||||
@bp.get("/cookbook/materials")
|
||||
def api_materials():
|
||||
"""
|
||||
Materialliste via EVE Ref.
|
||||
Erwartet: blueprintTypeId, optional quantity/baseMe/industryStructureType.
|
||||
"""
|
||||
if not request.args.get("blueprintTypeId"):
|
||||
return jsonify({"error": "missing blueprintTypeId"}), 400
|
||||
try:
|
||||
data = call_eve_ref_materials(dict(request.args))
|
||||
# Sicherstellen, dass immer eine Array-Liste unter "materials" steht
|
||||
mats = data.get("materials") or []
|
||||
if isinstance(mats, dict):
|
||||
mats = list(mats.values())
|
||||
data["materials"] = mats
|
||||
return jsonify(data)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 502
|
||||
|
||||
# ----------------------------- Endpoints – Cache/Order ------------------------
|
||||
|
||||
@bp.post("/order/<order_id>/bp")
|
||||
def api_set_bp(order_id: str):
|
||||
payload = request.get_json(silent=True) or {}
|
||||
bp_id = payload.get("blueprint_type_id")
|
||||
if not bp_id:
|
||||
return jsonify({"error": "missing blueprint_type_id"}), 400
|
||||
ok = update_order(order_id, {"blueprint_type_id": int(bp_id), "last_updated": _now()})
|
||||
return jsonify({"ok": bool(ok)})
|
||||
|
||||
@bp.post("/order/<order_id>/cache")
|
||||
def api_cache(order_id: str):
|
||||
payload = request.get_json(silent=True) or {}
|
||||
ok = False
|
||||
if "costs" in payload:
|
||||
ok = cache_costs(order_id, payload["costs"])
|
||||
if "materials" in payload:
|
||||
ok = cache_materials(order_id, payload["materials"])
|
||||
return jsonify({"ok": bool(ok)})
|
||||
|
||||
@bp.post("/order/<order_id>/refresh")
|
||||
def api_refresh_one(order_id: str):
|
||||
o = _order_by_id(order_id)
|
||||
if not o:
|
||||
return jsonify({"error": "order not found"}), 404
|
||||
bp_id = o.get("blueprint_type_id")
|
||||
if not bp_id:
|
||||
return jsonify({"error": "blueprint_type_id missing"}), 400
|
||||
|
||||
# Basis-Parameter aus Order
|
||||
p = {
|
||||
"blueprintTypeId": str(bp_id),
|
||||
"quantity": str(o.get("quantity", 1)),
|
||||
"priceMode": "buy",
|
||||
"additionalCosts": "0",
|
||||
"baseMe": str(o.get("me", 0)),
|
||||
"componentsMe": str(o.get("me", 0)),
|
||||
"system": o.get("system", "Jita"),
|
||||
"facilityTax": str(o.get("facility_tax", 0)),
|
||||
"industryStructureType": o.get("industry_structure", "Station"),
|
||||
"industryRig": o.get("industry_rig", "None"),
|
||||
"reactionStructureType": o.get("reaction_structure", "Athanor"),
|
||||
"reactionRig": o.get("reaction_rig", "None"),
|
||||
"reactionFlag": "",
|
||||
"blueprintVersion": "tq",
|
||||
}
|
||||
|
||||
# Kosten
|
||||
try:
|
||||
costs = call_cookbook_build_cost([bp_id],
|
||||
quantity=int(p["quantity"]),
|
||||
price_mode=p["priceMode"],
|
||||
additional_costs=int(p["additionalCosts"]),
|
||||
base_me=int(p["baseMe"]),
|
||||
components_me=int(p["componentsMe"]),
|
||||
system=p["system"],
|
||||
facility_tax=int(float(p["facilityTax"])),
|
||||
industry_structure_type=p["industryStructureType"],
|
||||
industry_rig=p["industryRig"],
|
||||
reaction_structure_type=p["reactionStructureType"],
|
||||
reaction_rig=p["reactionRig"],
|
||||
reaction_flag=p["reactionFlag"],
|
||||
blueprint_version=p["blueprintVersion"],
|
||||
)
|
||||
cache_costs(order_id, costs)
|
||||
except Exception as e:
|
||||
costs = {"error": str(e)}
|
||||
|
||||
# Materialien
|
||||
try:
|
||||
mats = call_eve_ref_materials(p)
|
||||
if isinstance(mats.get("materials"), dict):
|
||||
mats["materials"] = list(mats["materials"].values())
|
||||
cache_materials(order_id, mats)
|
||||
except Exception as e:
|
||||
mats = {"error": str(e)}
|
||||
|
||||
return jsonify({"ok": True, "costs": costs, "materials": mats})
|
||||
|
||||
@bp.post("/orders/refresh_all")
|
||||
def api_refresh_all():
|
||||
orders = [o for o in load_orders() if o.get("status") == "open" and o.get("blueprint_type_id")]
|
||||
done = 0
|
||||
for o in orders:
|
||||
try:
|
||||
request.environ["werkzeug.server.shutdown"] # no-op
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
_ = api_refresh_one(o["id"])
|
||||
done += 1
|
||||
except Exception:
|
||||
continue
|
||||
return jsonify({"ok": True, "refreshed": done})
|
||||
|
||||
# ----------------------------- Endpoints – Aggregation ------------------------
|
||||
|
||||
@bp.get("/cookbook/open_costs")
|
||||
def api_open_costs():
|
||||
"""
|
||||
Aggregiert Kosten je Order (nutzt Cache; fehlt er, wird berechnet).
|
||||
"""
|
||||
items: List[Dict[str, Any]] = []
|
||||
total = 0.0
|
||||
for o in load_orders():
|
||||
if o.get("status") != "open" or not o.get("blueprint_type_id"):
|
||||
continue
|
||||
|
||||
entry: Dict[str, Any] = {
|
||||
"structure": o.get("structure"),
|
||||
"quantity": o.get("quantity"),
|
||||
"me": o.get("me"),
|
||||
"system": o.get("system"),
|
||||
"industry_structure": o.get("industry_structure"),
|
||||
"industry_rig": o.get("industry_rig"),
|
||||
"reaction_structure": o.get("reaction_structure"),
|
||||
"reaction_rig": o.get("reaction_rig"),
|
||||
}
|
||||
|
||||
costs = o.get("cookbook")
|
||||
if not costs:
|
||||
try:
|
||||
costs = call_cookbook_build_cost([o["blueprint_type_id"]],
|
||||
quantity=int(o.get("quantity", 1)),
|
||||
price_mode="buy",
|
||||
additional_costs=0,
|
||||
base_me=int(o.get("me", 0)),
|
||||
components_me=int(o.get("me", 0)),
|
||||
system=o.get("system", "Jita"),
|
||||
facility_tax=int(float(o.get("facility_tax", 0))),
|
||||
industry_structure_type=o.get("industry_structure", "Station"),
|
||||
industry_rig=o.get("industry_rig", "None"),
|
||||
reaction_structure_type=o.get("reaction_structure", "Athanor"),
|
||||
reaction_rig=o.get("reaction_rig", "None"),
|
||||
reaction_flag="",
|
||||
blueprint_version="tq",
|
||||
)
|
||||
cache_costs(o["id"], costs)
|
||||
except Exception as e:
|
||||
entry["error"] = str(e)
|
||||
items.append(entry)
|
||||
continue
|
||||
|
||||
msg = costs.get("message") or costs
|
||||
unit = msg.get("buildCostPerUnit") or msg.get("unitCost")
|
||||
tot = msg.get("totalCost") or msg.get("total") or 0
|
||||
entry["unit_cost"] = unit
|
||||
entry["total_cost"] = tot
|
||||
total += float(tot or 0)
|
||||
items.append(entry)
|
||||
|
||||
return jsonify({"items": items, "total_cost": total, "refreshed_at": _now()})
|
||||
|
||||
@bp.get("/cookbook/open_materials")
|
||||
def api_open_materials():
|
||||
"""
|
||||
Aggregiert Materialien über alle offenen Orders.
|
||||
"""
|
||||
agg: Dict[int, Dict[str, Any]] = {}
|
||||
total_cost = 0.0
|
||||
|
||||
def _add(tid: int, name: str, qty: float, cost: float | None):
|
||||
row = agg.setdefault(tid, {"type_id": tid, "name": name, "quantity": 0, "cost": 0})
|
||||
row["quantity"] += float(qty or 0)
|
||||
if cost is not None:
|
||||
row["cost"] += float(cost or 0)
|
||||
|
||||
for o in load_orders():
|
||||
if o.get("status") != "open" or not o.get("blueprint_type_id"):
|
||||
continue
|
||||
|
||||
mats = o.get("materials")
|
||||
if not mats:
|
||||
try:
|
||||
mats = call_eve_ref_materials({
|
||||
"blueprintTypeId": str(o["blueprint_type_id"]),
|
||||
"quantity": str(o.get("quantity", 1)),
|
||||
"baseMe": str(o.get("me", 0)),
|
||||
"industryStructureType": o.get("industry_structure", "Station"),
|
||||
"facilityTax": str(o.get("facility_tax", 0)),
|
||||
})
|
||||
if isinstance(mats.get("materials"), dict):
|
||||
mats["materials"] = list(mats["materials"].values())
|
||||
cache_materials(o["id"], mats)
|
||||
except Exception:
|
||||
mats = {"materials": []}
|
||||
|
||||
for m in mats.get("materials") or []:
|
||||
tid = int(m.get("type_id") or m.get("typeId") or 0)
|
||||
if not tid:
|
||||
continue
|
||||
name = m.get("name") or f"Type {tid}"
|
||||
qty = m.get("quantity") or m.get("qty") or m.get("amount") or 0
|
||||
cost = m.get("cost") or m.get("cost_per_unit") or m.get("unit_cost")
|
||||
_add(tid, name, qty, cost)
|
||||
if cost:
|
||||
total_cost += float(cost or 0)
|
||||
|
||||
items = sorted(agg.values(), key=lambda r: (r.get("cost") or 0), reverse=True)
|
||||
return jsonify({"items": items, "total_cost": total_cost, "refreshed_at": _now()})
|
||||
Reference in New Issue
Block a user