141 lines
4.4 KiB
Python

"""Re-parse device_list child rows from stored ADE detail PDF attachments."""
from __future__ import annotations
import os
import frappe
from frappe.utils.file_manager import get_file_path
from sfda_parser.sfda_scraper.detail_pdf import parse_affected_device_list
def _read_detail_pdf_bytes(file_url: str) -> bytes:
file_name = file_url.split("/files/")[-1]
path = get_file_path(file_name)
if not path or not os.path.isfile(path):
site_path = frappe.get_site_path("public", "files", file_name)
if os.path.isfile(site_path):
path = site_path
else:
raise FileNotFoundError(f"PDF not found for {file_url}")
with open(path, "rb") as f:
return f.read()
def _normalize_ncmdr_ref(ncmdr_ref: str) -> str:
return (ncmdr_ref or "").strip().upper()
def _group_child_rows_by_ncmdr_ref(doc) -> dict[str, dict[str, str]]:
groups: dict[str, dict[str, str]] = {}
for row in doc.device_list or []:
ref = (row.ncmdr_ref or "").strip()
if not ref:
continue
key = _normalize_ncmdr_ref(ref)
if key not in groups:
groups[key] = {
"ncmdr_ref": ref,
"manufacturer": row.manufacturer or "",
"detail_pdf": row.detail_pdf or "",
"ade_detail_url": row.ade_detail_url or "",
}
return groups
def reparse_sfda_entry_device_list(doc_name: str) -> dict:
"""Rebuild device_list child rows from stored detail_pdf on each NCMDR group."""
doc = frappe.get_doc("SFDA Entries", doc_name)
groups = _group_child_rows_by_ncmdr_ref(doc)
if not groups:
return {"name": doc_name, "device_rows": 0, "skipped": "no_child_rows"}
rebuilt_rows: list[dict] = []
total_device_rows = 0
for group in groups.values():
detail_pdf = group.get("detail_pdf") or ""
if not detail_pdf:
rebuilt_rows.append(
{
"doctype": "SFDA Device Entries",
"ncmdr_ref": group["ncmdr_ref"],
"manufacturer": group["manufacturer"],
"ade_detail_url": group["ade_detail_url"],
}
)
continue
pdf_bytes = _read_detail_pdf_bytes(detail_pdf)
devices = parse_affected_device_list(pdf_bytes)
if devices:
for device in devices:
rebuilt_rows.append(
{
"doctype": "SFDA Device Entries",
"ncmdr_ref": group["ncmdr_ref"],
"manufacturer": group["manufacturer"],
"detail_pdf": detail_pdf,
"ade_detail_url": group["ade_detail_url"],
**device.as_dict(),
}
)
total_device_rows += len(devices)
else:
rebuilt_rows.append(
{
"doctype": "SFDA Device Entries",
"ncmdr_ref": group["ncmdr_ref"],
"manufacturer": group["manufacturer"],
"detail_pdf": detail_pdf,
"ade_detail_url": group["ade_detail_url"],
}
)
doc.set("device_list", [])
for row in rebuilt_rows:
doc.append("device_list", row)
doc.save(ignore_permissions=True)
frappe.db.commit()
return {
"name": doc_name,
"ncmdr_groups": len(groups),
"device_rows": total_device_rows or len(rebuilt_rows),
}
def reparse_all_stored_pdf_device_lists(sfda_entries_name: str | None = None) -> dict:
"""Re-parse device_list from child-row detail_pdf for one or all SFDA Entries."""
if sfda_entries_name:
names = [sfda_entries_name]
else:
names = frappe.db.sql_list(
"""
select distinct parent
from `tabSFDA Device Entries`
where ifnull(detail_pdf, '') != ''
"""
)
summary = {
"processed": 0,
"updated": 0,
"total_device_rows": 0,
"errors": [],
}
for name in names:
summary["processed"] += 1
try:
result = reparse_sfda_entry_device_list(name)
summary["updated"] += 1
summary["total_device_rows"] += result.get("device_rows", 0)
except Exception as exc:
frappe.db.rollback()
summary["errors"].append(f"{name}: {exc}")
return summary