Commit graph

7909 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
b6746cc24a Terraform + CI for bulk_document_download: exports bucket, 10GB /tmp, SES SMTP, wiring 🟩
New lambda_with_sqs consumer (timeout 900, memory 3008, ephemeral_storage 10240
for multi-GB ZIPs); dedicated retrofit-document-exports bucket (no lifecycle on
DATA_BUCKET); IAM to read source buckets + write/presign-read the exports
bucket; SES SMTP creds baked from Secrets Manager (no ses:* on the role). Adds
ephemeral_storage_size knob to the shared lambda modules (default 512, backward
compatible). Wires the queue url+arn into fast-api and orders the CI jobs
(ADR-0055).

NOTE: terraform is drafted, not validated (no AWS/terraform in the dev env).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 10:03:50 +00:00
Khalim Conn-Kowlessar
3c00e0ef00 FastAPI trigger route: POST /v1/documents/bulk-download 🟩
Resolves the authenticated requester's email (ADR-0059), resolves + caps the
property selection, pins the recipe (landlord_property_ids, recipient_email,
package_name) onto one pre-created sub_task (raw SQL, dodging the mirror
double-registration), and enqueues one message to the worker. Refuses
double-submits (409) and oversized selections (400).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:59:02 +00:00
Khalim Conn-Kowlessar
6db6bbd98f ADR-0060: dedicated exports bucket + multi-GB streaming (drop DATA_BUCKET lifecycle) 🟪
Per review: packages go to a separate DOCUMENT_EXPORTS_BUCKET (not DATA_BUCKET),
no 7-day lifecycle imposed on the shared data bucket; packages can be several GB
so they stream via /tmp + multipart upload with raised ephemeral storage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:52:43 +00:00
Khalim Conn-Kowlessar
8c0b015923 bulk_document_download Lambda: attach-mode handler, trigger body, packaging 🟩
Reads the recipe from sub_task.inputs, assembles the Download Package via the
orchestrator (source docs from each row's bucket -> ZIP -> the dedicated
DOCUMENT_EXPORTS_BUCKET), returns the presigned URL + skip summary for
sub_task.outputs. Dockerfile/requirements mirror bulk_upload_finaliser.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:51:51 +00:00
Khalim Conn-Kowlessar
a36aa49c2b Stream the Download Package ZIP via /tmp and multipart upload 🟪
Multi-GB packages are no longer held in memory: each source file is read,
written into the on-disk ZIP, and released; the archive is streamed up from
disk. Behaviour unchanged (moto tests green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:51:26 +00:00
Khalim Conn-Kowlessar
879581bb32 Stream a local file to S3 with managed multipart upload 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:50:34 +00:00
Khalim Conn-Kowlessar
79c8890d07 Stream a local file to S3 with managed multipart upload 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:50:10 +00:00
Khalim Conn-Kowlessar
0387f4a897 Resolve a property's display address from its landlord_property_id 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:45:13 +00:00
Khalim Conn-Kowlessar
456d4dffe7 Resolve a property's display address from its landlord_property_id 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:44:36 +00:00
Khalim Conn-Kowlessar
79fa46d97c Read document bytes from an arbitrary source bucket 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:42:37 +00:00
Khalim Conn-Kowlessar
0a09b56425 Read document bytes from an arbitrary source bucket 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:42:05 +00:00
Khalim Conn-Kowlessar
77e7b0fda9 Package the good documents and report the skipped ones 🟩
Pins the best-effort contract (ADR-0060) delivered with the tracer — no red
phase; discriminating: a strict impl would fail the run or omit the report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:33:58 +00:00
Khalim Conn-Kowlessar
59ef8a12ab Treat a selection with no documents as a recorded failure 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:33:25 +00:00
Khalim Conn-Kowlessar
73b21e8d64 Treat a selection with no documents as a recorded failure 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:32:37 +00:00
Khalim Conn-Kowlessar
e7181b7d1f Build, upload and email a Download Package for a property set 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:31:49 +00:00
Khalim Conn-Kowlessar
115f506353 Build, upload and email a Download Package for a property set 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:30:38 +00:00
Khalim Conn-Kowlessar
5151fea9b2 STARTTLS and authenticate before sending the email 🟩
Pins the auth handshake delivered with the tracer green (no red phase);
discriminating: an unauthenticated/cleartext send would be rejected by SES.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:27:31 +00:00
Khalim Conn-Kowlessar
74732c6e5a Send a document-download email to the requesting user over SES SMTP 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:27:09 +00:00
Khalim Conn-Kowlessar
7466b78e54 Send a document-download email to the requesting user over SES SMTP 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:25:56 +00:00
Khalim Conn-Kowlessar
135dd9abda Fetch all uploaded files for a set of properties by landlord_property_id 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:23:47 +00:00
Khalim Conn-Kowlessar
910f7cc06c Fetch all uploaded files for a set of properties by landlord_property_id 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:23:12 +00:00
Khalim Conn-Kowlessar
da3e0cd4b4 Generate a presigned GET URL for an S3 object 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:18:54 +00:00
Khalim Conn-Kowlessar
ba13300ee1 Generate a presigned GET URL for an S3 object 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:18:10 +00:00
Khalim Conn-Kowlessar
e906c8fc98 Scope latest-per-Document-Type per property, each in its own folder 🟩
Pins behavior delivered with the tracer slice (the group key is
(landlord_property_id, document_type)) — no red phase; discriminating: a
global-by-type dedupe would drop one property's file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:15:44 +00:00
Khalim Conn-Kowlessar
b89b9fe944 Name the packaged file by Document Type with the original extension 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:07:25 +00:00
Khalim Conn-Kowlessar
63933db529 Name the packaged file by Document Type with the original extension 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:06:51 +00:00
Khalim Conn-Kowlessar
bd928828e3 Skip and report a document with no Document Type 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:06:26 +00:00
Khalim Conn-Kowlessar
34c48d4adc Skip and report a document with no Document Type 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:05:44 +00:00
Khalim Conn-Kowlessar
810e910961 Keep only the latest file of a Document Type in each property's folder 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:04:58 +00:00
Khalim Conn-Kowlessar
28b7b43372 Keep only the latest file of a Document Type in each property's folder 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:04:19 +00:00
Khalim Conn-Kowlessar
6d001d650e ADR-0059/0060: Bulk Document Download design — SES-SMTP email sender + capped best-effort Download Package 🟪
Grilled 2026-07-08. Locks: app-owned-task + attach-mode lane (ADR-0055);
FastAPI route resolves the requesting user (dbId -> UserModel.email) and pins
it + the resolved property_id set into tasks.inputs; one capped Download
Package = one sub_task; landlord_property_id matching (property_id a future
gap); address-named folders (hubspot-enriched), latest per Document Type,
null-type skipped+reported; best-effort (fail only on infra / wholly-empty);
URL to sub_task.outputs + email; DATA_BUCKET/bulk-downloads/ ~7-day lifecycle.
Backend-sent email via a repositories/email port + infrastructure/email SES-SMTP
adapter. Glossary: Bulk Document Download, Download Package, Document Type.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 08:54:43 +00:00
KhalimCK
bd01f403c9
Merge pull request #1490 from Hestia-Homes/fix/first-plan-is-default
A property's first plan becomes its default whatever the scenario says
2026-07-07 17:59:36 +01:00
Khalim Conn-Kowlessar
8dd125f199 Review nitpicks: pids_with_default naming, empty-batch guard, truthful fake 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:44:23 +00:00
Khalim Conn-Kowlessar
5c982beba4 A new default plan demotes the prior default across scenarios 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:42:47 +00:00
Khalim Conn-Kowlessar
aa94e76d3d A new default plan demotes the prior default across scenarios 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:41:48 +00:00
Khalim Conn-Kowlessar
0eaf6fa99c A property's first plan becomes its default whatever the scenario says 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:13:47 +00:00
Khalim Conn-Kowlessar
101c1f1f21 A property's first plan becomes its default whatever the scenario says 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:12:34 +00:00
Jun-te Kim
c9b5fc095b
Merge pull request #1487 from Hestia-Homes/feature/uprn-confirmation-before-finalise
Confirm UPRNs before finalise: withhold ambiguous address2uprn matches (ADR-0057)
2026-07-07 17:12:14 +01:00
Khalim Conn-Kowlessar
371240c885 The plan repository reports which properties already have a default plan 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:11:32 +00:00
Khalim Conn-Kowlessar
015db4275b The plan repository reports which properties already have a default plan 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:08:30 +00:00
Jun-te Kim
dbcdf29bd9 refactor(address2uprn): name the match/decision return types; rename helper
Address PR review (dancafc):
- introduce UprnMatch NamedTuple (datatypes/address_match.py) for the
  (uprn, address, lexiscore, certificate_number) return, replacing the bare
  4-tuple in get_uprn_from_epc_df / get_uprn_from_historic_epc /
  HistoricEpcResolver.resolve_uprn. Tuple-compatible, so unpacking is unchanged.
- rename get_uprn_with_epc_df -> get_uprn_from_epc_df (+ callers).
- type resolve_group_ambiguity via a GroupDecision NamedTuple and trim its
  docstring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 16:00:00 +00:00
Daniel Roth
39beb303ef
Merge pull request #1488 from Hestia-Homes/deployment-fix
Abri API deployment correction
2026-07-07 16:57:08 +01:00
Daniel Roth
2e0cc7432c terraform correction 2026-07-07 15:23:44 +00:00
Jun-te Kim
269aade481 test(address2uprn): cover ambiguity withholding + override dedup; add ADR-0057
- unit tests for resolve_group_ambiguity (distinct addresses withheld,
  same-address re-listing kept, order preserved)
- Postgres integration tests for upsert_all's backstop dedup
- ADR-0057 recording the "confirm UPRNs before finalise" decision

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 15:21:04 +00:00
Jun-te Kim
4e0134dd27 Merge branch 'feat/address2uprn-certificate-number' into feature/uprn-confirmation-before-finalise
# Conflicts:
#	backend/address2UPRN/main.py
2026-07-07 15:17:47 +00:00
Jun-te Kim
daa1cd7967 feat(address2uprn): withhold ambiguous cross-row UPRN matches (ADR-0057)
Phase 1 of confirming UPRNs before finalise. address2uprn matched each
row independently, so one UPRN could be the best match for two distinct
addresses (a coarse EPC record absorbing several real addresses, e.g.
flats in a block). Those distinct addresses were then silently merged by
the property identity insert, and collided in property_overrides.

resolve_group_ambiguity() withholds a UPRN claimed by >=2 distinct
normalised addresses within a postcode group (keeps genuine same-address
re-listings), and the handler now emits an address2uprn_status column
(matched | ambiguous_duplicate | unmatched | invalid_postcode | error).
Withheld rows drop to a null UPRN but keep their lexiscore for triage on
the (upcoming) confirmation page.

Also adds the ADR-0057 backstop dedup in property_overrides upsert_all so
the ON CONFLICT statement can never double-touch a row.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 15:15:59 +00:00
Daniel Roth
66b2df99e9
Merge pull request #1482 from Hestia-Homes/feature/abri-api-integration
Abri API Integration: lambda handler and plumb everything together
2026-07-07 15:46:50 +01:00
Daniel Roth
c0e444fccb abri_api not abri 2026-07-07 14:40:33 +00:00
Jun-te Kim
7abc71ed95 Drop property-table wiring for certificate_number
On reflection the certificate number only needs to travel through the
address2uprn pipeline (result CSV, S3 output) as an internal value —
not persisted to property. Reverts the PropertyIdentityInsert /
property_table.py / property_postgres_repository.py changes; keeps
certificate_number flowing through get_epc_data_with_postcode,
get_uprn_with_epc_df, get_uprn_from_historic_epc, and the
address2uprn_certificate_number result column.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 14:16:55 +00:00
Daniel Roth
1085384f21 Merge branch 'main' into feature/abri-api-integration 2026-07-07 14:08:52 +00:00