fix: inherit person data via request_number for individually uploaded pages
- Detect page 2+ from page_info field (e.g. صفحة ٢ من 5) with Arabic digit support - Find sibling documents by matching request_number, not just pdf_group_id - Works for both PDF splits AND individually photographed pages - Inherit all person fields (name, father, family, registry, etc.) from confirmed sibling - Update review banner to show for all subsequent pages
This commit is contained in:
+105
-27
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import asyncio
|
||||
import re
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Request, status
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
@@ -15,6 +16,25 @@ templates = Jinja2Templates(directory="templates")
|
||||
|
||||
REVIEWABLE_DOCUMENT_STATUSES = {"extracted", "confirmed", "error"}
|
||||
|
||||
# Map Arabic-Indic digits to Western
|
||||
_ARABIC_DIGIT_MAP = str.maketrans("٠١٢٣٤٥٦٧٨٩", "0123456789")
|
||||
|
||||
|
||||
def _is_subsequent_page(page_info: str) -> bool:
|
||||
"""Return True if page_info indicates this is page 2 or later.
|
||||
Handles formats like: 'صفحة ٢ من 5', '2 / 3', 'صفحة 2 من 3', '٢ من ٣'
|
||||
"""
|
||||
if not page_info:
|
||||
return False
|
||||
# Normalize Arabic-Indic digits to Western
|
||||
normalized = page_info.translate(_ARABIC_DIGIT_MAP)
|
||||
# Find the first number (the page number)
|
||||
m = re.search(r"(\d+)", normalized)
|
||||
if m:
|
||||
page_num = int(m.group(1))
|
||||
return page_num > 1
|
||||
return False
|
||||
|
||||
|
||||
def _get_document(doc_id: int) -> dict | None:
|
||||
with get_db() as conn:
|
||||
@@ -42,33 +62,62 @@ def _get_document(doc_id: int) -> dict | None:
|
||||
else:
|
||||
doc["person"] = {}
|
||||
|
||||
# For multi-page PDFs (page 2+), inherit person info & doc fields from page 1
|
||||
# For multi-page documents (page 2+), inherit person info & doc fields from page 1.
|
||||
# Works for both PDF splits (pdf_group_id) and individually uploaded images (via request_number + page_info).
|
||||
doc["inherited_from_page1"] = False
|
||||
doc["page1_person_id"] = None
|
||||
|
||||
current_first = (doc["person"].get("first_name") or "").strip()
|
||||
is_subsequent_page = False
|
||||
sibling_doc = None
|
||||
|
||||
# Method 1: PDF group link
|
||||
if doc.get("pdf_group_id") and (doc.get("page_number") or 0) > 1:
|
||||
page1 = conn.execute(
|
||||
is_subsequent_page = True
|
||||
sibling_doc = conn.execute(
|
||||
"""SELECT * FROM documents
|
||||
WHERE pdf_group_id=? AND page_number=1""",
|
||||
(doc["pdf_group_id"],),
|
||||
).fetchone()
|
||||
if page1:
|
||||
page1 = dict(page1)
|
||||
# Inherit person data if current page has no name
|
||||
current_first = (doc["person"].get("first_name") or "").strip()
|
||||
if not current_first:
|
||||
if page1.get("person_id"):
|
||||
|
||||
# Method 2: Same request_number + page_info indicates page 2+
|
||||
if not sibling_doc and not current_first:
|
||||
page_info_str = doc.get("page_info") or ""
|
||||
req_num = doc.get("request_number") or ""
|
||||
if req_num and _is_subsequent_page(page_info_str):
|
||||
is_subsequent_page = True
|
||||
# Find another document with same request_number that has person data
|
||||
sibling_doc = conn.execute(
|
||||
"""SELECT * FROM documents
|
||||
WHERE request_number=? AND id != ? AND person_id IS NOT NULL
|
||||
ORDER BY id LIMIT 1""",
|
||||
(req_num, doc["id"]),
|
||||
).fetchone()
|
||||
if not sibling_doc:
|
||||
# Page 1 may not be confirmed yet; look for one with raw extraction
|
||||
sibling_doc = conn.execute(
|
||||
"""SELECT * FROM documents
|
||||
WHERE request_number=? AND id != ? AND raw_extraction_json IS NOT NULL
|
||||
ORDER BY id LIMIT 1""",
|
||||
(req_num, doc["id"]),
|
||||
).fetchone()
|
||||
|
||||
if sibling_doc and not current_first:
|
||||
sibling_doc = dict(sibling_doc)
|
||||
if sibling_doc.get("person_id"):
|
||||
person = conn.execute(
|
||||
"SELECT * FROM persons WHERE id=?", (page1["person_id"],)
|
||||
"SELECT * FROM persons WHERE id=?", (sibling_doc["person_id"],)
|
||||
).fetchone()
|
||||
if person:
|
||||
doc["person"] = dict(person)
|
||||
doc["inherited_from_page1"] = True
|
||||
doc["page1_person_id"] = page1["person_id"]
|
||||
elif page1.get("raw_extraction_json"):
|
||||
doc["page1_person_id"] = sibling_doc["person_id"]
|
||||
elif sibling_doc.get("raw_extraction_json"):
|
||||
try:
|
||||
p1_extracted = json.loads(page1["raw_extraction_json"])
|
||||
p1_person = p1_extracted.get("person", {})
|
||||
if (p1_person.get("first_name") or "").strip():
|
||||
doc["person"] = p1_person
|
||||
sib_extracted = json.loads(sibling_doc["raw_extraction_json"])
|
||||
sib_person = sib_extracted.get("person", {})
|
||||
if (sib_person.get("first_name") or "").strip():
|
||||
doc["person"] = sib_person
|
||||
doc["inherited_from_page1"] = True
|
||||
except Exception:
|
||||
pass
|
||||
@@ -80,8 +129,10 @@ def _get_document(doc_id: int) -> dict | None:
|
||||
"applicant_name_raw",
|
||||
]
|
||||
for field in inherit_fields:
|
||||
if not (doc.get(field) or "").strip() and (page1.get(field) or "").strip():
|
||||
doc[field] = page1[field]
|
||||
if not (doc.get(field) or "").strip() and (sibling_doc.get(field) or "").strip():
|
||||
doc[field] = sibling_doc[field]
|
||||
|
||||
doc["is_subsequent_page"] = is_subsequent_page
|
||||
|
||||
return doc
|
||||
|
||||
@@ -294,13 +345,16 @@ async def confirm_document(doc_id: int, request: Request):
|
||||
|
||||
current_doc = dict(current_doc)
|
||||
|
||||
# For multi-page PDFs (page 2+), resolve page 1's person and inherit name if needed
|
||||
# For multi-page documents (page 2+), resolve sibling document's person
|
||||
# Works via pdf_group_id OR request_number + page_info
|
||||
page1_person_id = None
|
||||
is_subsequent_page = (
|
||||
is_subsequent = (
|
||||
current_doc.get("pdf_group_id")
|
||||
and (current_doc.get("page_number") or 0) > 1
|
||||
)
|
||||
if is_subsequent_page:
|
||||
|
||||
# Method 1: PDF group link
|
||||
if is_subsequent:
|
||||
page1_doc = conn.execute(
|
||||
"""SELECT d.person_id, p.first_name, p.father_name, p.family_name,
|
||||
p.registry_number
|
||||
@@ -311,14 +365,38 @@ async def confirm_document(doc_id: int, request: Request):
|
||||
).fetchone()
|
||||
if page1_doc and page1_doc["person_id"]:
|
||||
page1_person_id = page1_doc["person_id"]
|
||||
# Inherit name from page 1's person if not provided
|
||||
if not first_name and page1_doc["first_name"]:
|
||||
first_name = page1_doc["first_name"]
|
||||
|
||||
# Method 2: Same request_number + page_info indicates page 2+
|
||||
if not page1_person_id and not first_name:
|
||||
req_num = current_doc.get("request_number") or ""
|
||||
page_info_val = current_doc.get("page_info") or ""
|
||||
if req_num and _is_subsequent_page(page_info_val):
|
||||
is_subsequent = True
|
||||
sibling = conn.execute(
|
||||
"""SELECT d.person_id, p.first_name, p.father_name, p.family_name,
|
||||
p.registry_number
|
||||
FROM documents d
|
||||
LEFT JOIN persons p ON p.id = d.person_id
|
||||
WHERE d.request_number=? AND d.id != ? AND d.person_id IS NOT NULL
|
||||
ORDER BY d.id LIMIT 1""",
|
||||
(req_num, doc_id),
|
||||
).fetchone()
|
||||
if sibling and sibling["person_id"]:
|
||||
page1_person_id = sibling["person_id"]
|
||||
|
||||
# Inherit person fields from sibling if not provided
|
||||
if page1_person_id and not first_name:
|
||||
sibling_person = conn.execute(
|
||||
"SELECT * FROM persons WHERE id=?", (page1_person_id,)
|
||||
).fetchone()
|
||||
if sibling_person:
|
||||
first_name = sibling_person["first_name"] or ""
|
||||
person_data["first_name"] = first_name
|
||||
# Also inherit other person fields if empty
|
||||
for field in ("father_name", "family_name", "registry_number"):
|
||||
if not (person_data.get(field) or "").strip() and page1_doc[field]:
|
||||
person_data[field] = page1_doc[field]
|
||||
for field in ("father_name", "mother_name", "family_name",
|
||||
"family_origin", "nationality", "birth_date",
|
||||
"registry_number", "registry_place"):
|
||||
if not (person_data.get(field) or "").strip() and sibling_person[field]:
|
||||
person_data[field] = sibling_person[field]
|
||||
registry_number = (person_data.get("registry_number") or "").strip() or None
|
||||
|
||||
if not first_name:
|
||||
|
||||
@@ -54,14 +54,14 @@
|
||||
<div class="form-panel">
|
||||
<form id="reviewForm">
|
||||
|
||||
{% if doc.get('pdf_group_id') and (doc.get('page_number') or 0) > 1 %}
|
||||
{% if doc.get('is_subsequent_page') %}
|
||||
<div class="info-banner page-group-banner">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
|
||||
هذه الصفحة {{ doc.page_number }} من مستند متعدد الصفحات.
|
||||
هذه صفحة تكميلية ({{ doc.get('page_info') or doc.get('page_number') or '' }}) من مستند متعدد الصفحات.
|
||||
{% if doc.get('inherited_from_page1') %}
|
||||
بيانات الشخص مستوردة تلقائياً من الصفحة الأولى.
|
||||
{% else %}
|
||||
يرجى التأكد من بيانات الشخص يدوياً.
|
||||
يرجى التأكد من بيانات الشخص يدوياً — لم يتم العثور على صفحة أولى مؤكدة بعد.
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user