mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 04:39:06 -05:00
fix: address a hashed file card by its hash
An emulator card's anchor changed when a second entry with the same path was added, so cited links landed at the page top. A card whose entry declares a hash is now addressed by it, and the first card of a path keeps the plain file-<path> id as an alias.
This commit is contained in:
1 parent
73ff57468f
commit
2239df669f
2 files changed
+71
-11
No files matched your search
+46
-11
@@ -1615,11 +1615,14 @@ def dataset_structured_data(exports: list[dict], db: dict) -> dict:
|
||||
def emulator_file_anchors(files: list[dict]) -> list[str]:
|
||||
"""The id of each file card on an emulator page, in profile order.
|
||||
|
||||
A path declared once keeps its plain anchor. One declared for several
|
||||
versions (raze's nine DUKE3D.GRP) takes what tells the version apart:
|
||||
its hash, else its size, else, when even the size is shared, its
|
||||
description. The page and its JSON-LD both read this, so a structured
|
||||
part links to its card.
|
||||
An entry that declares a hash is addressed by it: its anchor depends on
|
||||
its own content, never on the entries beside it, and a second version
|
||||
added under the same path moves nothing. An entry without one keeps
|
||||
its plain anchor when its path is declared once, and otherwise takes
|
||||
its size, else, when even the size is shared, its description. The
|
||||
page and its JSON-LD both read this, so a structured part links to its
|
||||
card. The plain anchor of a hashed card stays reachable through
|
||||
emulator_file_aliases.
|
||||
"""
|
||||
bases = [
|
||||
"file-" + (
|
||||
@@ -1638,12 +1641,15 @@ def emulator_file_anchors(files: list[dict]) -> list[str]:
|
||||
return digest[:8]
|
||||
return f"{f['size']}b" if f.get("size") else ""
|
||||
|
||||
keys = [(b, _version_key(f)) if b in repeated else (b, "") for f, b in zip(files, bases)]
|
||||
keys = [
|
||||
(b, _version_key(f)) if b in repeated or _first_hash_value(f) else (b, "")
|
||||
for f, b in zip(files, bases)
|
||||
]
|
||||
used: dict[str, int] = {}
|
||||
anchors = []
|
||||
for f, (base, key) in zip(files, keys):
|
||||
anchor = f"{base}-{key}" if key else base
|
||||
if base in repeated and keys.count((base, key)) > 1 and f.get("description"):
|
||||
if keys.count((base, key)) > 1 and f.get("description"):
|
||||
described = re.sub(r"[^a-z0-9]+", "-", str(f["description"]).lower()).strip("-")
|
||||
anchor = f"{anchor}-{described[:40].strip('-')}"
|
||||
used[anchor] = used.get(anchor, 0) + 1
|
||||
@@ -1653,6 +1659,30 @@ def emulator_file_anchors(files: list[dict]) -> list[str]:
|
||||
return anchors
|
||||
|
||||
|
||||
def emulator_file_aliases(files: list[dict], anchors: list[str]) -> list[str]:
|
||||
"""The plain file-<path> id each card also answers to, or "".
|
||||
|
||||
A link cited as emulators/raze/#file-duke3d-grp must still land once the
|
||||
card is addressed by its hash: the first card of that path keeps the
|
||||
plain id, unless another card already is it.
|
||||
"""
|
||||
taken = set(anchors)
|
||||
aliases = []
|
||||
for f, anchor in zip(files, anchors):
|
||||
base = "file-" + (
|
||||
re.sub(
|
||||
r"[^a-z0-9]+", "-",
|
||||
str(f.get("path") or f.get("standalone_path") or f.get("name", "")).lower(),
|
||||
).strip("-") or "entry"
|
||||
)
|
||||
if base in taken or anchor == base:
|
||||
aliases.append("")
|
||||
else:
|
||||
taken.add(base)
|
||||
aliases.append(base)
|
||||
return aliases
|
||||
|
||||
|
||||
def emulator_structured_data(profile: dict) -> dict:
|
||||
"""What an emulator page states, for a reader of its JSON-LD.
|
||||
|
||||
@@ -3087,10 +3117,12 @@ def _render_emulator_file(
|
||||
_file_available: Callable[[dict], bool],
|
||||
anchor: str = "",
|
||||
owner: str = "",
|
||||
alias: str = "",
|
||||
) -> list[str]:
|
||||
"""Render one row of an emulator profile's file table.
|
||||
|
||||
`anchor` is the card's id on the page, so one file can be cited by URL.
|
||||
`anchor` is the card's id on the page, so one file can be cited by URL,
|
||||
and `alias` a second id it answers to.
|
||||
|
||||
Split out of generate_emulator_page, where it was a 228-line loop body
|
||||
carrying most of that function's branching: thirty-odd optional fields,
|
||||
@@ -3128,7 +3160,8 @@ def _render_emulator_file(
|
||||
badge_str = " ".join(badges)
|
||||
lines.append('<div class="rb-file-entry" markdown>')
|
||||
lines.append("")
|
||||
lines.append(_card_title(f"{_code(fname)} {badge_str}", anchor))
|
||||
marker = f'<span id="{alias}"></span>' if alias else ""
|
||||
lines.append(_card_title(f"{marker}{_code(fname)} {badge_str}", anchor))
|
||||
lines.append("")
|
||||
if desc:
|
||||
lines.extend([_escape_tags(str(desc)), ""])
|
||||
@@ -3782,10 +3815,12 @@ def generate_emulator_page(
|
||||
|
||||
lines.extend(_emulator_file_summary(files, _file_available))
|
||||
|
||||
for f, anchor in zip(files, emulator_file_anchors(files)):
|
||||
anchors = emulator_file_anchors(files)
|
||||
for f, anchor, alias in zip(files, anchors, emulator_file_aliases(files, anchors)):
|
||||
lines.extend(
|
||||
_render_emulator_file(
|
||||
f, profile, consumers, files, _file_available, anchor, name
|
||||
f, profile, consumers, files, _file_available, anchor, name,
|
||||
alias,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -69,6 +69,31 @@ class WhoShipsTheBuildAndWhy(unittest.TestCase):
|
||||
"found only as rebuilt copies) |", page)
|
||||
|
||||
|
||||
class ACardKeepsItsAddress(unittest.TestCase):
|
||||
"""emulators/raze/#file-duke3d-grp stopped pointing at its card once a
|
||||
second version of the path was declared: the anchor of the first one
|
||||
became file-duke3d-grp-<hash>."""
|
||||
|
||||
def test_a_second_version_moves_nothing(self):
|
||||
one = {"name": "DUKE3D.GRP", "md5": "aaaa1111bbbb"}
|
||||
two = {"name": "DUKE3D.GRP", "md5": "cccc2222dddd"}
|
||||
before = gs.emulator_file_anchors([one])
|
||||
after = gs.emulator_file_anchors([two, one])
|
||||
self.assertEqual(before, ["file-duke3d-grp-aaaa1111"])
|
||||
self.assertEqual(after[1], before[0])
|
||||
|
||||
def test_the_plain_address_still_lands(self):
|
||||
files = [{"name": "DUKE3D.GRP", "md5": "aaaa1111bbbb"}]
|
||||
page = _page("raze", {"type": "standalone", "files": files})
|
||||
self.assertIn('<span id="file-duke3d-grp"></span>', page)
|
||||
self.assertIn("{ #file-duke3d-grp-aaaa1111 .rb-file-title }", page)
|
||||
# A card already holding the plain id gives it to no one else.
|
||||
files = [{"name": "x.bin"}, {"name": "x.bin", "sha1": "ab" * 20}]
|
||||
anchors = gs.emulator_file_anchors(files)
|
||||
self.assertEqual(anchors, ["file-x-bin", "file-x-bin-abababab"])
|
||||
self.assertEqual(gs.emulator_file_aliases(files, anchors), ["", ""])
|
||||
|
||||
|
||||
class WhatASearchSnippetSays(unittest.TestCase):
|
||||
def test_a_core_loading_no_file_promises_no_hash(self):
|
||||
"""152 pages of cores that load no file announced paths and hashes."""
|
||||
|
||||
Reference in new issue
Block a user