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:
79
arma_modlist_tools/compare.py
Normal file
79
arma_modlist_tools/compare.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
arma_modlist_tools.compare
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Compare two or more Arma 3 mod presets (parsed by :mod:`arma_modlist_tools.parser`)
|
||||
and produce a breakdown of shared and preset-unique mods.
|
||||
|
||||
Typical usage::
|
||||
|
||||
from arma_modlist_tools.parser import parse_modlist_dir
|
||||
from arma_modlist_tools.compare import compare_presets
|
||||
|
||||
presets = parse_modlist_dir("modlist_html")
|
||||
result = compare_presets(*presets)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def _mod_key(mod: dict) -> str:
|
||||
"""Return the identity key for a mod.
|
||||
|
||||
Uses ``steam_id`` when available (canonical Workshop identifier),
|
||||
falls back to ``name`` for local mods that have no Workshop ID.
|
||||
"""
|
||||
return mod["steam_id"] or mod["name"]
|
||||
|
||||
|
||||
def compare_presets(*presets: dict) -> dict:
|
||||
"""
|
||||
Compare two or more preset dicts and return a comparison dict.
|
||||
|
||||
:param presets: Two or more preset dicts as returned by
|
||||
:func:`~arma_modlist_tools.parser.parse_modlist_html`.
|
||||
:returns: Dict with keys:
|
||||
|
||||
- ``compared_presets`` — list of preset names that were compared
|
||||
- ``shared`` — mods present in **every** preset
|
||||
- ``mod_count`` — number of shared mods
|
||||
- ``mods`` — list of mod entry dicts
|
||||
- ``unique`` — per-preset mods not present in any other preset
|
||||
- keyed by ``preset_name``
|
||||
- each value has ``mod_count`` and ``mods``
|
||||
|
||||
:raises ValueError: If fewer than two presets are provided.
|
||||
"""
|
||||
if len(presets) < 2:
|
||||
raise ValueError("compare_presets requires at least two presets")
|
||||
|
||||
# Build per-preset {identity_key -> mod_entry} mappings
|
||||
preset_maps: list[dict[str, dict]] = [
|
||||
{_mod_key(mod): mod for mod in preset["mods"]}
|
||||
for preset in presets
|
||||
]
|
||||
|
||||
# Shared keys = intersection across ALL presets
|
||||
shared_keys: set[str] = set(preset_maps[0].keys())
|
||||
for pm in preset_maps[1:]:
|
||||
shared_keys &= pm.keys()
|
||||
|
||||
# Shared mods: take entries from the first preset (identical across all)
|
||||
shared_mods = [preset_maps[0][k] for k in preset_maps[0] if k in shared_keys]
|
||||
|
||||
# Unique mods per preset: entries whose key is not in the shared set
|
||||
unique: dict[str, dict] = {}
|
||||
for preset, pm in zip(presets, preset_maps):
|
||||
unique_mods = [mod for k, mod in pm.items() if k not in shared_keys]
|
||||
unique[preset["preset_name"]] = {
|
||||
"mod_count": len(unique_mods),
|
||||
"mods": unique_mods,
|
||||
}
|
||||
|
||||
return {
|
||||
"compared_presets": [p["preset_name"] for p in presets],
|
||||
"shared": {
|
||||
"mod_count": len(shared_mods),
|
||||
"mods": shared_mods,
|
||||
},
|
||||
"unique": unique,
|
||||
}
|
||||
Reference in New Issue
Block a user