Pipeline: parse HTML presets, compare modlists, download from Caddy file server, create junctions/symlinks to Arma 3 Server directory. Includes update/sync flows, missing-mod reporting, OS compat layer, shared config, dep checker, comprehensive test suite (71 tests). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
96 lines
3.1 KiB
Python
96 lines
3.1 KiB
Python
"""
|
|
arma_modlist_tools.reporter
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
Build and persist a report of mods that are required by the modlists but
|
|
absent from the file server.
|
|
|
|
The report includes a ``group`` field per missing mod so downstream tools
|
|
(``sync_missing.py``) know exactly where to place it when it becomes
|
|
available on the server, without needing to re-read ``comparison.json``.
|
|
|
|
Typical usage::
|
|
|
|
from arma_modlist_tools.reporter import build_missing_report, save_missing_report
|
|
|
|
report = build_missing_report(comparison, server_index)
|
|
save_missing_report(report, cfg.missing_report)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
|
|
def build_missing_report(comparison: dict, server_index: dict) -> dict:
|
|
"""
|
|
Cross-reference every mod in *comparison* against *server_index* and
|
|
return a report of mods that are not on the server.
|
|
|
|
:param comparison: Dict as returned by :func:`~arma_modlist_tools.compare.compare_presets`.
|
|
:param server_index: Dict as returned by :func:`~arma_modlist_tools.fetcher.build_server_index`.
|
|
:returns: Report dict::
|
|
|
|
{
|
|
"generated_at": "2026-04-07T12:00:00+00:00",
|
|
"total_mods": 80,
|
|
"on_server": 2,
|
|
"missing": 78,
|
|
"missing_mods": [
|
|
{
|
|
"name": "CBA_A3",
|
|
"steam_id": "450814997",
|
|
"url": "https://steamcommunity.com/...",
|
|
"group": "shared"
|
|
},
|
|
...
|
|
]
|
|
}
|
|
"""
|
|
by_steam_id: dict = server_index.get("by_steam_id", {})
|
|
by_name: dict = server_index.get("by_name", {})
|
|
|
|
from .fetcher import _normalize_name # reuse existing helper
|
|
|
|
def _on_server(mod: dict) -> bool:
|
|
if mod.get("steam_id") and mod["steam_id"] in by_steam_id:
|
|
return True
|
|
return _normalize_name(mod.get("name", "")) in by_name
|
|
|
|
# Flatten all mods with their group label
|
|
all_mods: list[tuple[dict, str]] = []
|
|
for mod in comparison["shared"]["mods"]:
|
|
all_mods.append((mod, "shared"))
|
|
for preset_name, data in comparison["unique"].items():
|
|
for mod in data["mods"]:
|
|
all_mods.append((mod, preset_name))
|
|
|
|
missing_mods = []
|
|
on_server_count = 0
|
|
|
|
for mod, group in all_mods:
|
|
if _on_server(mod):
|
|
on_server_count += 1
|
|
else:
|
|
missing_mods.append({
|
|
"name": mod["name"],
|
|
"steam_id": mod.get("steam_id"),
|
|
"url": mod.get("url"),
|
|
"group": group,
|
|
})
|
|
|
|
return {
|
|
"generated_at": datetime.now(timezone.utc).isoformat(),
|
|
"total_mods": len(all_mods),
|
|
"on_server": on_server_count,
|
|
"missing": len(missing_mods),
|
|
"missing_mods": missing_mods,
|
|
}
|
|
|
|
|
|
def save_missing_report(report: dict, path: Path) -> None:
|
|
"""Write *report* as indented JSON to *path*, creating parent dirs as needed."""
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8")
|