159 lines
5.7 KiB
Python
159 lines
5.7 KiB
Python
"""Tests for splitting comma-separated device identifiers into separate rows."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import unittest
|
|
|
|
from sfda_parser.sfda_scraper.detail_pdf import (
|
|
DeviceRow,
|
|
_expand_device_row,
|
|
_expand_device_rows,
|
|
_split_multi_values,
|
|
parse_affected_device_list,
|
|
)
|
|
|
|
|
|
class TestSplitMultiValues(unittest.TestCase):
|
|
def test_splits_comma_separated_values(self):
|
|
self.assertEqual(
|
|
_split_multi_values("24B0729, 25B0770, 25H0171, 26B0542"),
|
|
["24B0729", "25B0770", "25H0171", "26B0542"],
|
|
)
|
|
|
|
def test_single_value_unchanged(self):
|
|
self.assertEqual(_split_multi_values("24B0729"), ["24B0729"])
|
|
|
|
def test_splits_semicolon_separated_values(self):
|
|
self.assertEqual(_split_multi_values("A1; A2; A3"), ["A1", "A2", "A3"])
|
|
|
|
def test_splits_space_separated_numeric_ids(self):
|
|
value = (
|
|
"0231547054 0231591827 0231758880 0231758898 0231758932 "
|
|
"0231792414 0231792421 0231912903 0232670901 0232709219 "
|
|
"0232709254 0232744928 0232784925 0232784989 0232805458 0232837110"
|
|
)
|
|
parts = _split_multi_values(value)
|
|
self.assertEqual(len(parts), 16)
|
|
self.assertEqual(parts[0], "0231547054")
|
|
self.assertEqual(parts[-1], "0232837110")
|
|
|
|
def test_does_not_split_prose_with_spaces(self):
|
|
self.assertEqual(
|
|
_split_multi_values("Surgical Instrument Kit"),
|
|
["Surgical Instrument Kit"],
|
|
)
|
|
|
|
|
|
class TestExpandDeviceRow(unittest.TestCase):
|
|
def _row(self, **kwargs) -> DeviceRow:
|
|
base = {
|
|
"material": "Test Device",
|
|
"material_description": "Description",
|
|
"catalog_number": "",
|
|
"udi": "",
|
|
"serial_no": "",
|
|
"gstn": "",
|
|
"batch": "",
|
|
}
|
|
base.update(kwargs)
|
|
return DeviceRow(**base)
|
|
|
|
def test_expands_serial_numbers_into_separate_rows(self):
|
|
rows = _expand_device_row(
|
|
self._row(serial_no="24B0729, 25B0770, 25H0171, 26B0542")
|
|
)
|
|
self.assertEqual(len(rows), 4)
|
|
self.assertEqual([row.serial_no for row in rows], [
|
|
"24B0729",
|
|
"25B0770",
|
|
"25H0171",
|
|
"26B0542",
|
|
])
|
|
for row in rows:
|
|
self.assertEqual(row.material, "Test Device")
|
|
self.assertEqual(row.material_description, "Description")
|
|
|
|
def test_expands_catalog_numbers_into_separate_rows(self):
|
|
rows = _expand_device_row(self._row(catalog_number="CAT-1, CAT-2, CAT-3"))
|
|
self.assertEqual(len(rows), 3)
|
|
self.assertEqual([row.catalog_number for row in rows], ["CAT-1", "CAT-2", "CAT-3"])
|
|
|
|
def test_expands_batch_values_into_separate_rows(self):
|
|
rows = _expand_device_row(self._row(batch="LOT1, LOT2"))
|
|
self.assertEqual(len(rows), 2)
|
|
self.assertEqual([row.batch for row in rows], ["LOT1", "LOT2"])
|
|
|
|
def test_zips_serial_and_catalog_when_counts_match(self):
|
|
rows = _expand_device_row(
|
|
self._row(serial_no="S1, S2", catalog_number="C1, C2")
|
|
)
|
|
self.assertEqual(len(rows), 2)
|
|
self.assertEqual(rows[0].serial_no, "S1")
|
|
self.assertEqual(rows[0].catalog_number, "C1")
|
|
self.assertEqual(rows[1].serial_no, "S2")
|
|
self.assertEqual(rows[1].catalog_number, "C2")
|
|
|
|
def test_single_value_row_unchanged(self):
|
|
row = self._row(serial_no="ONLY-ONE", catalog_number="CAT-9")
|
|
self.assertEqual(_expand_device_row(row), [row])
|
|
|
|
def test_expand_device_rows_flattens_list(self):
|
|
devices = [
|
|
self._row(serial_no="A, B"),
|
|
self._row(catalog_number="X, Y, Z"),
|
|
]
|
|
expanded = _expand_device_rows(devices)
|
|
self.assertEqual(len(expanded), 5)
|
|
|
|
def test_expands_space_separated_udi_values(self):
|
|
udi_value = "0231547054 0231591827 0231758880"
|
|
rows = _expand_device_row(self._row(udi=udi_value, material="Monitor"))
|
|
self.assertEqual(len(rows), 3)
|
|
self.assertEqual(rows[1].udi, "0231591827")
|
|
self.assertEqual(rows[2].material, "Monitor")
|
|
|
|
|
|
class TestParseAffectedDeviceListExpansion(unittest.TestCase):
|
|
def test_parse_expands_comma_separated_serials_from_table_like_input(self):
|
|
# Minimal synthetic check via direct expansion path used by parser output.
|
|
devices = _expand_device_rows([
|
|
DeviceRow(
|
|
material="Pump",
|
|
material_description="",
|
|
catalog_number="REF-100",
|
|
udi="",
|
|
serial_no="24B0729, 25B0770, 25H0171, 26B0542",
|
|
gstn="",
|
|
batch="",
|
|
)
|
|
])
|
|
self.assertEqual(len(devices), 4)
|
|
self.assertEqual(devices[0].catalog_number, "REF-100")
|
|
self.assertEqual(devices[-1].serial_no, "26B0542")
|
|
|
|
|
|
class TestSparseTablePdfExpansion(unittest.TestCase):
|
|
def test_medtronic_pdf_expands_sparse_table_lot_lists(self):
|
|
pdf_path = os.path.abspath(
|
|
os.path.join(
|
|
os.path.dirname(__file__),
|
|
"../../../../sites/erp.seeraunified.com/public/files/"
|
|
"SA-28-05-26-1408-ade-detailad786ead786e.pdf",
|
|
)
|
|
)
|
|
if not os.path.isfile(pdf_path):
|
|
self.skipTest(f"fixture PDF not found: {pdf_path}")
|
|
|
|
with open(pdf_path, "rb") as pdf_file:
|
|
devices = parse_affected_device_list(pdf_file.read())
|
|
|
|
self.assertGreaterEqual(len(devices), 40)
|
|
serial_numbers = [device.serial_no for device in devices if device.serial_no]
|
|
self.assertIn("0012508285", serial_numbers)
|
|
self.assertIn("0013272796", serial_numbers)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|