Skip to content

Commit 0bf7054

Browse files
committed
Address review: section-heading fallback, N+1, alias-registry footgun
- resolver.py: backward-heading fallback now matches the nearest *preceding* occurrence (rfind from cand.start) instead of the first in the document, so a duplicate heading in an earlier exhibit no longer mis-resolves. - annotation_queries.py: select_related the FK targets CorpusReferenceType resolves, removing the N+1 on resolve_corpus_references. - authorities.py: authority_alias_registry(user=None) now contributes only static defaults (Document.objects.none()) rather than aliases from every document — closes a cross-tenant footgun for future callers. - authorities.py: narrow the read_field_file_text except to (OSError, ValueError, AttributeError) so genuine bugs surface. - extractor.py/constants.py: hoist trailing-punctuation literal to C.TRAILING_PUNCT.
1 parent 667d5d0 commit 0bf7054

5 files changed

Lines changed: 27 additions & 5 deletions

File tree

config/graphql/annotation_queries.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,15 @@ def resolve_corpus_references(self, info, corpus_id, **kwargs) -> Any:
8080
qs = qs.filter(reference_type=kwargs["reference_type"])
8181
if kwargs.get("canonical_key"):
8282
qs = qs.filter(canonical_key=kwargs["canonical_key"])
83-
return qs
83+
# Pull the FK targets the type resolves in one pass — without this each
84+
# CorpusReferenceType row fires a separate query per FK (N+1).
85+
return qs.select_related(
86+
"source_annotation",
87+
"corpus",
88+
"target_document",
89+
"target_annotation",
90+
"target_corpus",
91+
)
8492

8593
governance_graph = graphene.Field(
8694
GovernanceGraphType,

opencontractserver/enrichment/authorities.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,12 @@ def authority_alias_registry(user=None) -> dict[str, str]:
6767
documents visible to that user are included.
6868
"""
6969
mapping: dict[str, str] = dict(C.AUTHORITY_PREFIX)
70+
# Fail closed: without a user we contribute only the static defaults rather
71+
# than aliases from EVERY document (including private corpora). A future
72+
# caller that forgets to thread ``creator`` gets no DB-declared aliases, not
73+
# a cross-tenant metadata leak.
7074
qs = (
71-
Document.objects.all()
75+
Document.objects.none()
7276
if user is None
7377
else Document.objects.visible_to_user(user)
7478
)
@@ -157,7 +161,10 @@ def bootstrap(
157161
if existing is not None:
158162
try:
159163
current = read_field_file_text(existing.txt_extract_file)
160-
except Exception: # unreadable -> rewrite below
164+
except (OSError, ValueError, AttributeError):
165+
# Unreadable (missing file / no file associated / decode
166+
# error) -> treat as needs-rewrite below. Narrow on purpose
167+
# so genuine bugs surface instead of being swallowed.
161168
current = None
162169
if current == sec.text:
163170
meta = existing.custom_meta or {}

opencontractserver/enrichment/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@
7777
# Defaults / thresholds.
7878
DEFAULT_SAMPLE_N = 10
7979
MAX_DEFINED_TERMS = 50 # cap to control precision/volume in v1
80+
# Punctuation stripped from the tail of a captured defined term
81+
# (e.g. (the "Notes," ...) -> "Notes").
82+
TRAILING_PUNCT = ",.;:"
8083
ALL_REFERENCE_TYPES = (REF_LAW, REF_DOCUMENT, REF_SECTION, REF_DEFINED_TERM)
8184
# Defined-terms are opt-in (precision risk); not scanned/applied by default.
8285
DEFAULT_REFERENCE_TYPES = (REF_LAW, REF_DOCUMENT, REF_SECTION)

opencontractserver/enrichment/extractor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def _terms(self, text: str) -> Iterator[Candidate]:
229229
return
230230
# Trim trailing punctuation captured inside the quotes
231231
# (e.g. (the "Notes," ...) -> "Notes").
232-
term = m.group("term").strip().rstrip(",.;:")
232+
term = m.group("term").strip().rstrip(C.TRAILING_PUNCT)
233233
slug = _slugify_term(term)
234234
if not slug or slug in seen:
235235
continue

opencontractserver/enrichment/resolver.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ def resolve_section(
111111
if heading:
112112
idx = doc_text.lower().find(heading.lower(), cand.end)
113113
if idx == -1:
114-
idx = doc_text.lower().find(heading.lower())
114+
# Backward reference (e.g. "as defined in 'Heading' above"):
115+
# match the nearest *preceding* occurrence, not the first in
116+
# the document — a duplicate heading in an earlier exhibit
117+
# would otherwise mis-resolve to an unrelated section.
118+
idx = doc_text.lower().rfind(heading.lower(), 0, cand.start)
115119
if idx != -1:
116120
res.target_offset = idx
117121
res.resolution_status = C.STATUS_RESOLVED

0 commit comments

Comments
 (0)