feat: add date-based OHIP data reporting alongside XML
This commit is contained in:
1 parent
5ecd571e3b
commit
ad2c59256f
259 files changed
+37326
-126
No files matched your search
@@ -0,0 +1,92 @@
|
||||
"""Explicit row values for ARR processing XML, not an OHIP mapping policy.
|
||||
|
||||
Callers must already have resolved every value, note/trace selection and row
|
||||
order from independently accepted source rules. No fields have defaults.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
import re
|
||||
|
||||
from .collect_arr_source import require
|
||||
|
||||
VERSION = "arr-processing-xml/v1"
|
||||
# Match the existing frozen handoff/upload boundary, not the larger diagnostic
|
||||
# native-XML comparator limit. A serialization result must fit that entrypoint.
|
||||
MAX_BYTES = 25 * 1024 * 1024
|
||||
MAX_ROWS = 10000
|
||||
MAX_ELEMENTS = 500000
|
||||
|
||||
# Declarative correspondence only; shared by serializer and independent parser.
|
||||
SCALARS = (
|
||||
("block_code", "BLOCK_CODE"), ("adults", "ADULTS"), ("children", "CF_CHILDREN"),
|
||||
("company_name", "COMPANY_NAME"), ("confirmation_no", "CONFIRMATION_NO"),
|
||||
("room_no", "DISP_ROOM_NO"), ("effective_rate", "EFFECTIVE_RATE_AMOUNT"),
|
||||
("full_name", "FULL_NAME"), ("no_of_rooms", "NO_OF_ROOMS"), ("products", "PRODUCTS"),
|
||||
("rate_code", "RATE_CODE"), ("room_category_label", "ROOM_CATEGORY_LABEL"),
|
||||
("arrival", "TRUNC_BEGIN"), ("departure", "TRUNC_END"),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SourceRow:
|
||||
reservation_id: str
|
||||
block_code: str
|
||||
adults: str
|
||||
children: str
|
||||
company_name: str
|
||||
confirmation_no: str
|
||||
room_no: str
|
||||
effective_rate: str
|
||||
full_name: str
|
||||
no_of_rooms: str
|
||||
products: str
|
||||
rate_code: str
|
||||
room_category_label: str
|
||||
arrival: str
|
||||
departure: str
|
||||
notes: tuple[str, ...]
|
||||
traces: tuple[str, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SourceDocument:
|
||||
hotel_id: str
|
||||
report_date: date
|
||||
rows: tuple[SourceRow, ...]
|
||||
|
||||
|
||||
def _text(value):
|
||||
require(type(value) is str, "arr_xml_explicit_text_required")
|
||||
require(all(char in "\t\n\r" or 0x20 <= ord(char) <= 0xD7FF
|
||||
or 0xE000 <= ord(char) <= 0xFFFD or 0x10000 <= ord(char) <= 0x10FFFF
|
||||
for char in value), "arr_xml_invalid_character")
|
||||
return len(value.encode("utf-8"))
|
||||
|
||||
|
||||
def validate_document(document: SourceDocument) -> None:
|
||||
"""Validate transport shape only; existing processor owns business rules.
|
||||
|
||||
An explicit blank is retained even on a required whitelist field. A missing
|
||||
value/None is rejected. This preserves invalid and non-whitelist source rows so
|
||||
serialization cannot silently remove records to make processing succeed.
|
||||
"""
|
||||
require(type(document) is SourceDocument, "arr_xml_document_required")
|
||||
require(type(document.hotel_id) is str and bool(re.fullmatch(r"[A-Za-z0-9_-]{1,64}", document.hotel_id)),
|
||||
"arr_xml_hotel_required")
|
||||
require(type(document.report_date) is date, "arr_xml_explicit_date_required")
|
||||
require(type(document.rows) is tuple and 1 <= len(document.rows) <= MAX_ROWS, "arr_xml_record_count")
|
||||
size, elements = 0, 6
|
||||
for row in document.rows:
|
||||
require(type(row) is SourceRow, "arr_xml_row_required")
|
||||
size += _text(row.reservation_id)
|
||||
require(bool(row.reservation_id.strip()), "arr_xml_reservation_identity_required")
|
||||
for attribute, _ in SCALARS:
|
||||
size += _text(getattr(row, attribute))
|
||||
for collection in (row.notes, row.traces):
|
||||
require(type(collection) is tuple, "arr_xml_explicit_collection_required")
|
||||
for value in collection:
|
||||
size += _text(value)
|
||||
elements += 19 + 2 * (len(row.notes) + len(row.traces))
|
||||
require(size <= MAX_BYTES and elements <= MAX_ELEMENTS, "arr_xml_input_budget_exceeded")
|
||||
Reference in new issue
Block a user