feat(consume): a table fragment is read under the heading it stands under
C3, the remaining half. The proposer names a table block with no heading of its own after the line it starts on (`Tabell linje <n>`). A position, not a name: searched, it matched no question, and shown, it told a reader nothing about what the table is. `consume.inherit_table_titles` reads such a concept under the title of the nearest concept ABOVE it in the same source document -- in reading order, the heading the table stands under -- ordered by `source_offset`, else `source_lines`, one key per document and never mixed. Applied where the bundle is loaded, so the ranking, the excerpt and the near misses all see the same title; the excerpt keeps the file's own as `own_title`, so the name shown is never mistaken for the one in the file. A table with nothing named above it, or a document whose concepts do not all carry one position key, is left as it is. Chose to do it in the reading and not in the build: no bundle byte moves, no collection has to be rebuilt, and the proposer's goldens stay pinned. `consume.MECHANICAL_TITLE` is held against the proposer's own output by a test. Suite on a clean tree after `git add`: 2418 passed, 2 skipped, 4 xfailed. ruff, ruff format, mypy --strict clean. The search gate's table for the previous commit is kept in local state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
80aac93b8f
commit
f7cd84c5e6
4 changed files with 235 additions and 9 deletions
|
|
@ -354,6 +354,73 @@ class Concept:
|
|||
#: `True` when the concept carries a `parent` key that resolves to no
|
||||
#: concept of its own document -- the third state, as for `sources`.
|
||||
parent_unresolved: bool = False
|
||||
#: The title the FILE carries, set only when `title` was inherited from
|
||||
#: the heading the concept stands under (:func:`inherit_table_titles`).
|
||||
own_title: str | None = None
|
||||
|
||||
|
||||
#: The name the proposer gives a table block that has no heading of its own
|
||||
#: (`propose`, `rule:table-block`): the line the block starts on. A position,
|
||||
#: not a name -- held against the proposer's output by a test, so the two
|
||||
#: cannot drift apart.
|
||||
MECHANICAL_TITLE = re.compile(r"Tabell linje \d+")
|
||||
|
||||
_FIRST_NUMBER = re.compile(r"\s*\[\s*(\d+)")
|
||||
|
||||
#: The locators a concept's place in its document is read off, in order of
|
||||
#: preference. One key per DOCUMENT, never mixed: an offset and a line number
|
||||
#: are not on one scale.
|
||||
_POSITION_KEYS = ("source_offset", "source_lines")
|
||||
|
||||
|
||||
def _position(concept: Concept, key: str) -> int | None:
|
||||
match = _FIRST_NUMBER.match(concept.locators.get(key, ""))
|
||||
return int(match.group(1)) if match else None
|
||||
|
||||
|
||||
def inherit_table_titles(concepts: Sequence[Concept]) -> list[Concept]:
|
||||
"""Every concept, a table fragment with no name of its own read under the
|
||||
heading it stands under (v1.1 C3).
|
||||
|
||||
`Tabell linje <n>` is a line number: searched, it matches no question, and
|
||||
shown, it tells a reader nothing about what the table is. So such a concept
|
||||
takes the title of the nearest concept ABOVE it in the same source
|
||||
document -- in reading order, the heading the table stands under -- and
|
||||
keeps its own in `own_title`, so the name shown is never mistaken for the
|
||||
one in the file. A table with nothing named above it keeps its own title,
|
||||
and a document whose concepts do not all carry one position key is left
|
||||
alone rather than ordered by a guess.
|
||||
|
||||
A READING, never a write: no bundle byte moves, and the ranking, the near
|
||||
misses and the excerpt all see the same title because they all read it
|
||||
from here.
|
||||
"""
|
||||
by_source: dict[str, list[int]] = {}
|
||||
for index, concept in enumerate(concepts):
|
||||
if concept.source_file:
|
||||
by_source.setdefault(concept.source_file, []).append(index)
|
||||
out = list(concepts)
|
||||
for indices in by_source.values():
|
||||
if not any(MECHANICAL_TITLE.fullmatch(concepts[index].title) for index in indices):
|
||||
continue
|
||||
key = next(
|
||||
(
|
||||
key
|
||||
for key in _POSITION_KEYS
|
||||
if all(_position(concepts[index], key) is not None for index in indices)
|
||||
),
|
||||
None,
|
||||
)
|
||||
if key is None:
|
||||
continue
|
||||
heading: str | None = None
|
||||
for index in sorted(indices, key=lambda each: (_position(concepts[each], key), each)):
|
||||
concept = concepts[index]
|
||||
if not MECHANICAL_TITLE.fullmatch(concept.title):
|
||||
heading = concept.title
|
||||
elif heading is not None:
|
||||
out[index] = replace(concept, title=heading, own_title=concept.title)
|
||||
return out
|
||||
|
||||
|
||||
#: The key a concept names its enclosing section under, and the key that
|
||||
|
|
@ -1828,6 +1895,8 @@ def excerpt_for(concept: Concept) -> dict[str, object] | None:
|
|||
"bundle_id_inherited": concept.bundle_id_inherited,
|
||||
"title": concept.title,
|
||||
}
|
||||
if concept.own_title is not None:
|
||||
excerpt["own_title"] = concept.own_title
|
||||
if concept.req_number:
|
||||
excerpt["req_number"] = concept.req_number
|
||||
if concept.sources:
|
||||
|
|
@ -2429,15 +2498,17 @@ def _load(
|
|||
)
|
||||
root_bundle_id = root_bundle_id_of(bundle_root, profile=profile)
|
||||
concept_ids = enumerate_concepts(bundle_root, profile=profile)
|
||||
concepts = link_parents(
|
||||
[
|
||||
read_concept(
|
||||
read_path_in_bundle(bundle_root, f"{concept_id}{profile.paths.concept_suffix}"),
|
||||
bundle_root=bundle_root,
|
||||
root_bundle_id=root_bundle_id,
|
||||
)
|
||||
for concept_id in concept_ids
|
||||
]
|
||||
concepts = inherit_table_titles(
|
||||
link_parents(
|
||||
[
|
||||
read_concept(
|
||||
read_path_in_bundle(bundle_root, f"{concept_id}{profile.paths.concept_suffix}"),
|
||||
bundle_root=bundle_root,
|
||||
root_bundle_id=root_bundle_id,
|
||||
)
|
||||
for concept_id in concept_ids
|
||||
]
|
||||
)
|
||||
)
|
||||
# The text the two lexical signals read, tokenised ONCE: the stem
|
||||
# vocabulary, the rarity `df` and the coverage report below all count over
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue