Compare commits

...

2 Commits

Author SHA1 Message Date
revernomad17
80ecd3a919 docs: document _find_folder three-level name matching in CLAUDE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 15:57:11 +07:00
revernomad17
6197659568 Fix mods view showing wrong download status for name-mismatched mods
_find_folder() used a plain lowercase compare which failed when the
server canonical folder name differs from the comparison.json mod name
in more than just case (e.g. spaces vs underscores: "NIArms All in One"
vs "@NIArms_All_In_One"). These mods showed ✗ even though the pipeline
found and linked them correctly.

Add a normalized-name fallback (strips non-alphanumeric, same logic as
fetcher._normalize_name) so the lookup matches the same way the fetcher
resolves mods from the server index.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 15:54:30 +07:00
2 changed files with 20 additions and 3 deletions

View File

@@ -101,6 +101,11 @@ Pass 2 builds `ok_disk_names` — the set of disk names that already match the s
- `logs.py` — real-time log viewer fed from the stdout/stderr queue - `logs.py` — real-time log viewer fed from the stdout/stderr queue
- `settings.py` — in-app editor for `config.json` (server URL, paths, credentials) - `settings.py` — in-app editor for `config.json` (server URL, paths, credentials)
**`_find_folder` (mods.py) — three-level name matching:** The mods view resolves a mod's local folder by mod name from `comparison.json`, which may differ from the server-canonical folder name used by the fetcher. Lookup order:
1. Exact: `@{mod_name}`
2. Case-insensitive: `@CBA_A3` matches `CBA_A3`
3. Normalized (`_normalize_name`): strips all non-alphanumeric — handles punctuation/spacing differences, e.g. `@US GEAr- Units (IFA3)` matches `US GEAr: Units (IFA3)` (both → `usgearunitsifa3`)
**`selection.json`** — GUI selection state file, tracked in git. Persists which mods/groups are selected between GUI sessions. Written by the GUI; safe to delete (GUI recreates it on next save). **`selection.json`** — GUI selection state file, tracked in git. Persists which mods/groups are selected between GUI sessions. Written by the GUI; safe to delete (GUI recreates it on next save).
## Python Version Compatibility ## Python Version Compatibility

View File

@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Optional
import customtkinter as ctk import customtkinter as ctk
from arma_modlist_tools.fetcher import _normalize_name
from gui._constants import COLOR_OK, COLOR_ERROR, COLOR_WARN, COLOR_RUNNING from gui._constants import COLOR_OK, COLOR_ERROR, COLOR_WARN, COLOR_RUNNING
from gui.views.base import BaseView from gui.views.base import BaseView
@@ -15,15 +16,26 @@ if TYPE_CHECKING:
def _find_folder(group_dir: Path, mod_name: str) -> Optional[Path]: def _find_folder(group_dir: Path, mod_name: str) -> Optional[Path]:
"""Return the local mod folder path, or None if not downloaded.""" """Return the local mod folder path, or None if not downloaded.
Matches in priority order:
1. Exact folder name ``@{mod_name}``
2. Case-insensitive name (handles ``@CBA_A3`` vs ``CBA_A3``)
3. Normalized name — strips non-alphanumeric (handles ``@cba_a3`` vs ``CBA A3``)
"""
if not group_dir.is_dir(): if not group_dir.is_dir():
return None return None
candidate = group_dir / f"@{mod_name}" candidate = group_dir / f"@{mod_name}"
if candidate.is_dir(): if candidate.is_dir():
return candidate return candidate
target = mod_name.lower() target_lower = mod_name.lower()
target_norm = _normalize_name(mod_name)
for p in group_dir.iterdir(): for p in group_dir.iterdir():
if p.is_dir() and p.name.lstrip("@").lower() == target: if not p.is_dir():
continue
if p.name.lstrip("@").lower() == target_lower:
return p
if _normalize_name(p.name) == target_norm:
return p return p
return None return None