feat: enhance compatibility table with file name handling and UI updates
This commit is contained in:
@@ -219,7 +219,9 @@ która gra wiezie którą bibliotekę. Haczyk: ta sama nazwa może wystąpić w
|
|||||||
nie być tym samym silnikiem. Dlatego w obrębie jednej nazwy klastrujemy **warianty po powierzchni
|
nie być tym samym silnikiem. Dlatego w obrębie jednej nazwy klastrujemy **warianty po powierzchni
|
||||||
silnika** (te same zbiory tożsamości co `diff`/`similarity` — kompilator-agnostyczne), a każdy
|
silnika** (te same zbiory tożsamości co `diff`/`similarity` — kompilator-agnostyczne), a każdy
|
||||||
wariant inny niż referencyjny (`A`) dostaje **gwiazdkę** `*` z deltą *czym* się różni. Byte-różnice
|
wariant inny niż referencyjny (`A`) dostaje **gwiazdkę** `*` z deltą *czym* się różni. Byte-różnice
|
||||||
z rekompilacji nie generują gwiazdki — liczy się API skryptowe.
|
z rekompilacji nie generują gwiazdki — liczy się API skryptowe. Prefiks `<sha8>_` doklejany do
|
||||||
|
nazwy pliku przy składowaniu (np. `333ab0c4_PIKLIB8.dll`) jest **obcinany przed grupowaniem**, więc
|
||||||
|
wszystkie kopie `PIKLIB8.dll` lądują w jednym wierszu (pełna nazwa pliku zostaje w polu `file` JSON-a).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# z plików snapshot (bez DB/serwera); Label= ustawia kolumnę-grę
|
# z plików snapshot (bez DB/serwera); Label= ustawia kolumnę-grę
|
||||||
@@ -236,7 +238,7 @@ curl -OJ "http://127.0.0.1:8000/compat?format=md" # → compat.md
|
|||||||
curl -OJ "http://127.0.0.1:8000/compat?format=json" # → compat.json (grupy + warianty + diffy)
|
curl -OJ "http://127.0.0.1:8000/compat?format=json" # → compat.json (grupy + warianty + diffy)
|
||||||
```
|
```
|
||||||
|
|
||||||
W UI: przycisk **⬇ kompat** w panelu *Gry / wersje*. Markdown to macierz `biblioteka × gra`
|
W UI: przyciski **⬇ md** / **⬇ json** w panelu *Gry / wersje*. Markdown to macierz `biblioteka × gra`
|
||||||
(`✓` = obecna; `A`/`B*`/… gdy nazwa ma kilka wariantów powierzchni) plus sekcja *Warianty* z
|
(`✓` = obecna; `A`/`B*`/… gdy nazwa ma kilka wariantów powierzchni) plus sekcja *Warianty* z
|
||||||
przypisem: dla każdego rozbieżnego wariantu overlap % i `types/methods/events/fields +N/-N/~N`
|
przypisem: dla każdego rozbieżnego wariantu overlap % i `types/methods/events/fields +N/-N/~N`
|
||||||
względem referencyjnego.
|
względem referencyjnego.
|
||||||
|
|||||||
@@ -17,8 +17,10 @@
|
|||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
Gry / wersje
|
Gry / wersje
|
||||||
<span class="panel-actions">
|
<span class="panel-actions">
|
||||||
<a id="dl-compat" class="mini-btn" href="/compat?format=md" download="compat.md"
|
<a id="dl-compat-md" class="mini-btn" href="/compat?format=md" download="compat.md"
|
||||||
title="Pobierz tablicę kompatybilności (Markdown) — grupowaną po nazwie biblioteki">⬇ kompat</a>
|
title="Tablica kompatybilności — Markdown (grupowana po nazwie biblioteki)">⬇ md</a>
|
||||||
|
<a id="dl-compat-json" class="mini-btn" href="/compat?format=json" download="compat.json"
|
||||||
|
title="Tablica kompatybilności — JSON (grupy + warianty + diffy)">⬇ json</a>
|
||||||
<button id="upload-toggle" class="mini-btn" title="Wgraj grę (ISO / ZIP / DLL)">+ wgraj</button>
|
<button id="upload-toggle" class="mini-btn" title="Wgraj grę (ISO / ZIP / DLL)">+ wgraj</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@@ -77,8 +78,19 @@ def _vs_reference(reference: Snapshot, variant: Snapshot) -> dict[str, Any]:
|
|||||||
|
|
||||||
|
|
||||||
# --- table builder -----------------------------------------------------------------------------
|
# --- table builder -----------------------------------------------------------------------------
|
||||||
|
# Stored binaries are prefixed with the first 8 hex of their sha256 to keep filenames unique on
|
||||||
|
# disk (e.g. ``333ab0c4_PIKLIB8.dll``). That prefix is per-file, so it must be stripped before
|
||||||
|
# grouping — otherwise every copy of PIKLIB8.dll lands in its own row and nothing ever clusters.
|
||||||
|
_HASH_PREFIX = re.compile(r"^[0-9a-fA-F]{8}_")
|
||||||
|
|
||||||
|
|
||||||
|
def library_name(raw: str) -> str:
|
||||||
|
"""The display/grouping name: the stored binary name with any ``<sha8>_`` prefix removed."""
|
||||||
|
return _HASH_PREFIX.sub("", raw)
|
||||||
|
|
||||||
|
|
||||||
def _library_name(snap: Snapshot) -> str:
|
def _library_name(snap: Snapshot) -> str:
|
||||||
return snap.binary.get("name") or "?"
|
return library_name(snap.binary.get("name") or "?")
|
||||||
|
|
||||||
|
|
||||||
def _variant_id(index: int) -> str:
|
def _variant_id(index: int) -> str:
|
||||||
@@ -115,6 +127,7 @@ def _build_library(name: str, entries: list[Entry]) -> dict[str, Any]:
|
|||||||
members = [
|
members = [
|
||||||
{
|
{
|
||||||
"game": e.game,
|
"game": e.game,
|
||||||
|
"file": e.snapshot.binary.get("name"), # stored name, incl. the <sha8>_ prefix
|
||||||
"engine": e.snapshot.binary.get("engine"),
|
"engine": e.snapshot.binary.get("engine"),
|
||||||
"compiler": e.snapshot.binary.get("compiler"),
|
"compiler": e.snapshot.binary.get("compiler"),
|
||||||
"sha256": e.snapshot.binary.get("sha256"),
|
"sha256": e.snapshot.binary.get("sha256"),
|
||||||
|
|||||||
@@ -77,6 +77,23 @@ def test_divergent_surface_raises_asterisk():
|
|||||||
assert lib["cells"]["Game C"]["diverges"] is True
|
assert lib["cells"]["Game C"]["diverges"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_hash_prefix_is_stripped_for_grouping():
|
||||||
|
# stored names carry a per-file <sha8>_ prefix; copies must still group under one library row
|
||||||
|
t = compatibility_table([
|
||||||
|
Entry("Game A", _snap("333ab0c4_PIKLIB8.dll", ["SHOW", "HIDE"])),
|
||||||
|
Entry("Game B", _snap("8dc0165d_PIKLIB8.dll", ["SHOW", "HIDE"])),
|
||||||
|
Entry("Game C", _snap("bba62d83_PIKLIB8.dll", ["SHOW", "HIDE", "PAUSE"])),
|
||||||
|
])
|
||||||
|
assert len(t["libraries"]) == 1
|
||||||
|
lib = t["libraries"][0]
|
||||||
|
assert lib["name"] == "PIKLIB8.dll" # prefix gone from the display name
|
||||||
|
assert lib["variant_count"] == 2 # A,B identical → reference; C diverges → asterisk
|
||||||
|
assert lib["cells"]["Game C"]["diverges"] is True
|
||||||
|
# the original stored filename is preserved per member for correlation
|
||||||
|
files = {m["file"] for v in lib["variants"] for m in v["members"]}
|
||||||
|
assert "333ab0c4_PIKLIB8.dll" in files
|
||||||
|
|
||||||
|
|
||||||
def test_grouping_is_case_insensitive_keeps_common_spelling():
|
def test_grouping_is_case_insensitive_keeps_common_spelling():
|
||||||
t = compatibility_table([
|
t = compatibility_table([
|
||||||
Entry("Game A", _snap("Piklib8.dll", ["SHOW"])),
|
Entry("Game A", _snap("Piklib8.dll", ["SHOW"])),
|
||||||
|
|||||||
Reference in New Issue
Block a user