Files

112 lines
4.6 KiB
Python
Raw Permalink Normal View History

2026-10-17 23:49:14 -06:00
"""Phase 7 ITP PDF: the builder on an open and a complete ITP (the number, a section title, the approvals, the
stamp, three page groups), and the sign-off through the API that now files the signed PDF (the asset on the ITP,
the automatic Q.C. folder document, the log note), superseded by a second sign-off after a reopen."""
from __future__ import annotations
from collections.abc import Iterator
from datetime import UTC, datetime
from typing import Any
import pytest
from closehelpers import builder_conn, flat, page_texts, pdf_reader, q1, qall, who
from pshelpers import needs_pg
from psycopg.rows import dict_row
from qchelpers import (
SECTION_TITLE_5,
approve_all,
itp_log,
open_qc_shop,
qc_directory,
reopen_itp,
sign_all_blocks,
sign_line,
sign_off,
start_itp,
)
from shophelpers import Shop, make_unit, ok
from packetstation.pdf import itp as itp_pdf
from packetstation.settings import Settings
pytestmark = needs_pg
NOW = datetime(2026, 10, 17, 21, 4, tzinfo=UTC)
PAGE_HEADS = ("1 OF 3: WP", "2 OF 3: C", "3 OF 3: FA") # the header sub is drawn in capitals
@pytest.fixture
def qc_shop(pg_settings: Settings, dev_visual: None) -> Iterator[Shop]:
yield from open_qc_shop(pg_settings, qc_directory())
def build(shop: Shop, itp_id: int) -> Any:
with builder_conn(shop.settings) as conn:
with conn.cursor(row_factory=dict_row) as cur:
row = cur.execute("SELECT * FROM itps WHERE id = %s", (itp_id,)).fetchone()
return itp_pdf.build(conn, shop.settings, row, who=who(shop, "qc.quinn"), now=NOW)
def test_open_itp_pdf(qc_shop: Shop) -> None:
shop = qc_shop
_, unit_id = make_unit(shop.settings)
itp = start_itp(shop, unit_id)
itp = ok(sign_line(shop("qc.quinn"), itp, "1.1"))
built = build(shop, itp["id"])
assert built.bytes[:5] == b"%PDF-"
assert len(pdf_reader(built.bytes).pages) == built.pages >= 3
texts = [flat(t) for t in page_texts(built.bytes)]
text = " ".join(texts)
assert itp["no"] in text and SECTION_TITLE_5 in text
assert "Approved" in text and "INSPECTION & TEST PLAN" in text
# three page groups, in order WP, C, FA
firsts = [next(i for i, t in enumerate(texts) if head in t) for head in PAGE_HEADS]
assert firsts == sorted(firsts) and firsts[0] == 0, firsts
for t in texts:
assert "OPEN, not signed off" in t
assert "Controlled copy - by Quinn QC (5001) 2026-10-17 21:04 UTC - OPEN, not signed off - stamp " in t
assert pdf_reader(built.bytes).metadata.title == f"ITP {itp['no']}"
def test_signoff_files_the_signed_pdf(qc_shop: Shop) -> None:
shop = qc_shop
_, unit_id = make_unit(shop.settings)
q = shop("qc.quinn")
itp = sign_all_blocks(shop, approve_all(shop, start_itp(shop, unit_id)))
itp = ok(sign_off(q, itp))
assert itp["status"] == "complete"
asset_id = itp["signoff"]["asset_id"]
assert asset_id is not None
assert itp_log(itp)[0] == ("ITP signed off", "PDF filed")
row = q1(shop.settings, "SELECT signoff_asset_id FROM itps WHERE id = %s", (itp["id"],))
assert str(row[0]) == asset_id
a = q1(shop.settings, "SELECT original_name, mime, source FROM assets WHERE id = %s", (asset_id,))
assert a[0] == f"{itp['no']}_signed.pdf" and a[1] == "application/pdf" and a[2] == "itp_signoff"
docs = qall(
shop.settings,
"SELECT id, type, title, auto, superseded_by, asset_id FROM qc_docs WHERE unit_id = %s ORDER BY id",
(unit_id,),
)
assert len(docs) == 1
assert tuple(docs[0][1:5]) == ("ITP", f"{itp['no']} — signed off", True, None)
assert str(docs[0][5]) == asset_id
r = q.get(f"/_blob/{asset_id}")
assert r.status_code == 200 and r.content[:5] == b"%PDF-"
text = flat(" ".join(page_texts(r.content)))
assert itp["no"] in text and "signed off" in text and "OPEN, not signed off" not in text
assert "ITP signed off" in text and "Quinn QC (5001)" in text
# the builder on the complete ITP says signed off on every page
for t in page_texts(build(shop, itp["id"]).bytes):
assert "signed off" in flat(t) and "OPEN, not signed off" not in flat(t)
# reopen and sign off again: a second PDF supersedes the first automatic document
itp = ok(reopen_itp(shop("admin.ann"), itp, "Customer asked for 4.2 again"))
itp = ok(sign_off(q, itp))
second = itp["signoff"]["asset_id"]
assert second is not None and second != asset_id
docs = qall(
shop.settings, "SELECT id, superseded_by, asset_id FROM qc_docs WHERE unit_id = %s ORDER BY id", (unit_id,)
)
assert len(docs) == 2 and docs[0][1] == docs[1][0] and docs[1][1] is None and str(docs[1][2]) == second