Initial release: full Arma 3 mod management toolchain
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>
This commit is contained in:
95
arma_modlist_tools/reporter.py
Normal file
95
arma_modlist_tools/reporter.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""
|
||||
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")
|
||||
Reference in New Issue
Block a user