From e3c91073131b7ddbcc36ee500d3c40bd6dc55244 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 26 Jun 2026 19:49:48 +0000 Subject: [PATCH] =?UTF-8?q?feat(audit):=20--scenario=20filter=20+=20audit-?= =?UTF-8?q?ara-portfolio=20skill=20=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Script takes an optional --scenario to restrict to one scenario's plans. New skill drives the full loop: run the deterministic scan, review groups, deep-dive samples via run_modelling_e2e, characterise sub-classes, and cross-reference open PRs/ADRs — then proposes new checks to codify. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/skills/audit-ara-portfolio/SKILL.md | 92 ++++ modelling_audit.md | 462 +++++++++++++++++++- scripts/audit/anomalies.py | 28 +- 3 files changed, 571 insertions(+), 11 deletions(-) create mode 100644 .claude/skills/audit-ara-portfolio/SKILL.md diff --git a/.claude/skills/audit-ara-portfolio/SKILL.md b/.claude/skills/audit-ara-portfolio/SKILL.md new file mode 100644 index 00000000..dd6c1824 --- /dev/null +++ b/.claude/skills/audit-ara-portfolio/SKILL.md @@ -0,0 +1,92 @@ +--- +name: audit-ara-portfolio +description: Audit a modelled Ara portfolio for odd results — run the deterministic anomaly checks over the DB, review the groups, deep-dive a sample of each, characterise sub-classes, and cross-reference open PRs/ADRs. Use when the user wants to audit or review a portfolio's modelling output, hunt for dodgy plans / baselines / SAP scores / recommendations, or triage modelling anomalies. Asks for portfolio id and scenario id. +--- + +# Audit Ara portfolio + +Turn the deterministic anomaly scan into a triaged, root-caused review. The +deterministic checks (`scripts/audit/anomalies.py`) find KNOWN anti-patterns +exhaustively and cheaply; this skill adds the judgement — confirm, characterise, +cross-reference existing work, and feed novel patterns back as new checks. + +## Input + +Ask for **portfolio_id** and **scenario_id** if the user didn't give them. +`scenario_id` is optional — without it, each Property's *default* plan (the one +shown in the FE) is audited. + +## Phase 1 — Build the dataset + +``` +python -m scripts.audit.anomalies --portfolio --scenario +``` + +Writes `modelling_audit.md` (grouped, ranked by severity) and +`modelling_audit.csv` (every flagged row: property_id, uprn, severity, check, +detail). Read the printed summary and `modelling_audit.md`. + +## Phase 2 — Review the high-level results + +For each check group, HIGH severity first: note the count, read a few example +rows. State, in one line each, a **root-cause hypothesis** and whether it is a +real bug, a known/expected effect, or a threshold that needs tuning. + +## Phase 3 — Deep-dive a sample per group + +For 2–3 representative properties in each meaningful group, reproduce +end-to-end (NO DB writes — never pass `--persist`): + +``` +python -m scripts.run_modelling_e2e --scenario-id +``` + +Read the printed overrides + effective SAP + measure-by-measure plan table. +Confirm the anomaly and isolate **where** it goes wrong: the baseline, a specific +measure, the SAP calc, or the bill. For SAP-value bugs, the `SapResult` +worksheet / `intermediate` trail and the `/diagnose` loop are the tools. + +## Phase 4 — Characterise sub-classes + +A group of thousands is usually several distinct causes. Within each meaningful +group, cluster the flagged properties by a distinguishing trait — EPC source +(lodged / predicted), `rebaseline_reason`, property type, the dominant measure, +fuel — using SQL against the DB. Report the sub-classes and their sizes. + +## Phase 5 — Cross-reference open work + +Before proposing fixes, check whether one is already in flight: + +``` +gh pr list --repo Hestia-Homes/Model --state open +gh pr list --repo Hestia-Homes/Model --state all --search "" +``` + +Map each sub-class to an existing PR / ADR where one applies (e.g. +baseline-vs-plan divergence → the override-aware-rebaseline + persistence-fidelity +work; oversized solar → ADR-0038 dwelling-roof cap). Flag sub-classes with no +coverage as new work. + +## Output + +A triage report. Per check group: +- count + root-cause hypothesis, +- sub-classes, each sized, +- example property ids (to reproduce), +- existing-PR/ADR coverage, +- recommended action: **fix** / **tune threshold** / **accept (expected)** / **new ticket**. + +Then propose any **new deterministic checks** worth adding to +`scripts/audit/anomalies.py` (one decorated function each) so the next run catches +the pattern automatically — the check registry is the durable output of every +audit. + +## Notes + +- Read-only on the DB. `run_modelling_e2e` is a dry run. +- **Expected, not bugs** (until the override-aware-rebaseline + persistence-fidelity + PR deploys and the portfolio is re-modelled): much of `zero-works-post-differs` + and `plan-score-below-baseline` is the pre-fix baseline-vs-plan divergence and + should shrink after re-model — note it, don't re-debug it. +- Adding a check is one decorated `(PropertyAudit) -> Optional[str]` function in + `scripts/audit/anomalies.py`; see its module docstring. diff --git a/modelling_audit.md b/modelling_audit.md index 81c5595d..c537bd98 100644 --- a/modelling_audit.md +++ b/modelling_audit.md @@ -1,3 +1,463 @@ # Modelling anomaly audit -Scanned **0** properties · flagged **0** anomalies across **0** checks. +Scanned **31919** properties · flagged **10563** anomalies across **10** checks. + +## impossible-sap-over-100 (HIGH) — 1 + +- property **726993** (uprn 100061757571): SAP > 100: effective 102.0 + +## plan-below-baseline-band (HIGH) — 363 + +- property **709810** (uprn 10096028301): post C (78.02813) worse than effective baseline B (85) +- property **709846** (uprn 10096399556): post C (79.7374) worse than effective baseline A (93) +- property **709847** (uprn 10096028354): post C (78.695816) worse than effective baseline B (91) +- property **709850** (uprn 10096028348): post C (78.750984) worse than effective baseline B (89) +- property **709959** (uprn 10096028306): post C (78.14567) worse than effective baseline B (84) +- property **710011** (uprn 10096028349): post C (78.750984) worse than effective baseline B (89) +- property **710071** (uprn 10002918889): post C (76.07915) worse than effective baseline B (82) +- property **710075** (uprn 10002918890): post C (79.84036) worse than effective baseline B (82) +- property **710117** (uprn 10096399566): post C (79.86282) worse than effective baseline A (93) +- property **710121** (uprn 10096028307): post C (78.790596) worse than effective baseline B (90) +- property **710131** (uprn 10096028350): post C (80.142784) worse than effective baseline A (94) +- property **710201** (uprn 10090944222): post C (74.58814) worse than effective baseline B (82) +- property **710222** (uprn 10096399567): post C (79.84616) worse than effective baseline A (93) +- property **710225** (uprn 10096028308): post C (78.78894) worse than effective baseline B (90) +- property **710241** (uprn 10096028351): post C (80.142784) worse than effective baseline A (94) +- property **710326** (uprn 10096028309): post C (78.610855) worse than effective baseline B (89) +- property **710335** (uprn 10096028352): post C (79.9757) worse than effective baseline A (94) +- property **710395** (uprn 10096028310): post C (79.46583) worse than effective baseline B (91) +- property **710474** (uprn 10096028355): post C (78.47655) worse than effective baseline B (91) +- property **710479** (uprn 10096028353): post C (80.291725) worse than effective baseline A (95) +- property **710537** (uprn 10096028311): post C (79.46583) worse than effective baseline B (91) +- property **710540** (uprn 100060714157): post D (68.24699) worse than effective baseline C (71) +- property **710760** (uprn 10096028302): post C (78.28315) worse than effective baseline B (85) +- property **710800** (uprn 10096399558): post C (79.7374) worse than effective baseline A (93) +- property **710802** (uprn 10096028356): post C (78.47655) worse than effective baseline B (91) +- property **710946** (uprn 44012846): post C (79.819626) worse than effective baseline B (81) +- property **711027** (uprn 10096028357): post C (77.96618) worse than effective baseline B (90) +- property **711178** (uprn 10091636026): post C (76.05113) worse than effective baseline B (81) +- property **711195** (uprn 10096028303): post C (80.00826) worse than effective baseline B (86) +- property **711236** (uprn 10096399560): post C (79.7374) worse than effective baseline A (93) +- property **711238** (uprn 10096028358): post C (78.695816) worse than effective baseline B (91) +- property **711312** (uprn 10096028346): post C (80.1876) worse than effective baseline A (93) +- property **711376** (uprn 10096028359): post C (78.47655) worse than effective baseline B (91) +- property **711489** (uprn 10096028304): post C (80.3187) worse than effective baseline B (86) +- property **711523** (uprn 10096399562): post C (79.7374) worse than effective baseline A (93) +- property **711524** (uprn 10096028360): post C (78.47655) worse than effective baseline B (91) +- property **711539** (uprn 68159801): post D (66.09142) worse than effective baseline C (71) +- property **711575** (uprn 10096028347): post C (80.1876) worse than effective baseline A (94) +- property **711639** (uprn 10096028361): post C (78.88584) worse than effective baseline B (91) +- property **711706** (uprn 10096028305): post C (78.71836) worse than effective baseline B (85) +- property **711715** (uprn 10013924857): post C (79.91303) worse than effective baseline B (81) +- property **711732** (uprn 100062190435): post C (77.935425) worse than effective baseline B (82) +- property **711795** (uprn 10090343115): post E (52.785263) worse than effective baseline C (73) +- property **711821** (uprn 10096399575): post C (80.209465) worse than effective baseline A (93) +- property **711824** (uprn 10094615095): post C (78.01443) worse than effective baseline A (92) +- property **711858** (uprn 22245014): post C (79.352325) worse than effective baseline B (81) +- property **711881** (uprn 10023444014): post C (79.504456) worse than effective baseline B (81) +- property **711897** (uprn 10012025246): post C (76.70494) worse than effective baseline B (81) +- property **711898** (uprn 10094615103): post C (79.97074) worse than effective baseline A (92) +- property **711917** (uprn 10012027840): post C (72.92677) worse than effective baseline B (81) +- … and 313 more (see CSV) + +## plan-score-below-baseline (HIGH) — 1631 + +- property **709790** (uprn 10023443426): post SAP 74.2 below effective baseline 76.0 (Δ-1.8) +- property **709810** (uprn 10096028301): post SAP 78.0 below effective baseline 85.0 (Δ-7.0) +- property **709846** (uprn 10096399556): post SAP 79.7 below effective baseline 93.0 (Δ-13.3) +- property **709847** (uprn 10096028354): post SAP 78.7 below effective baseline 91.0 (Δ-12.3) +- property **709850** (uprn 10096028348): post SAP 78.8 below effective baseline 89.0 (Δ-10.2) +- property **709959** (uprn 10096028306): post SAP 78.1 below effective baseline 84.0 (Δ-5.9) +- property **709970** (uprn 10013924859): post SAP 79.2 below effective baseline 80.0 (Δ-0.8) +- property **709975** (uprn 200001466609): post SAP 72.3 below effective baseline 79.0 (Δ-6.7) +- property **710011** (uprn 10096028349): post SAP 78.8 below effective baseline 89.0 (Δ-10.2) +- property **710071** (uprn 10002918889): post SAP 76.1 below effective baseline 82.0 (Δ-5.9) +- property **710075** (uprn 10002918890): post SAP 79.8 below effective baseline 82.0 (Δ-2.2) +- property **710117** (uprn 10096399566): post SAP 79.9 below effective baseline 93.0 (Δ-13.1) +- property **710121** (uprn 10096028307): post SAP 78.8 below effective baseline 90.0 (Δ-11.2) +- property **710122** (uprn 100090187902): post SAP 72.4 below effective baseline 74.0 (Δ-1.6) +- property **710131** (uprn 10096028350): post SAP 80.1 below effective baseline 94.0 (Δ-13.9) +- property **710140** (uprn 10023302889): post SAP 76.9 below effective baseline 79.0 (Δ-2.1) +- property **710201** (uprn 10090944222): post SAP 74.6 below effective baseline 82.0 (Δ-7.4) +- property **710222** (uprn 10096399567): post SAP 79.8 below effective baseline 93.0 (Δ-13.2) +- property **710225** (uprn 10096028308): post SAP 78.8 below effective baseline 90.0 (Δ-11.2) +- property **710241** (uprn 10096028351): post SAP 80.1 below effective baseline 94.0 (Δ-13.9) +- property **710274** (uprn 10013924864): post SAP 74.8 below effective baseline 76.0 (Δ-1.2) +- property **710326** (uprn 10096028309): post SAP 78.6 below effective baseline 89.0 (Δ-10.4) +- property **710335** (uprn 10096028352): post SAP 80.0 below effective baseline 94.0 (Δ-14.0) +- property **710362** (uprn 10090317710): post SAP 75.5 below effective baseline 79.0 (Δ-3.5) +- property **710395** (uprn 10096028310): post SAP 79.5 below effective baseline 91.0 (Δ-11.5) +- property **710472** (uprn 10013918445): post SAP 76.9 below effective baseline 79.0 (Δ-2.1) +- property **710474** (uprn 10096028355): post SAP 78.5 below effective baseline 91.0 (Δ-12.5) +- property **710479** (uprn 10096028353): post SAP 80.3 below effective baseline 95.0 (Δ-14.7) +- property **710525** (uprn 10012028784): post SAP 69.6 below effective baseline 79.0 (Δ-9.4) +- property **710537** (uprn 10096028311): post SAP 79.5 below effective baseline 91.0 (Δ-11.5) +- property **710540** (uprn 100060714157): post SAP 68.2 below effective baseline 71.0 (Δ-2.8) +- property **710544** (uprn 10008052006): post SAP 81.1 below effective baseline 83.0 (Δ-1.9) +- property **710552** (uprn 100060706143): post SAP 83.0 below effective baseline 85.0 (Δ-2.0) +- property **710569** (uprn 200000546526): post SAP 70.8 below effective baseline 73.0 (Δ-2.2) +- property **710576** (uprn 100090187795): post SAP 72.4 below effective baseline 75.0 (Δ-2.6) +- property **710645** (uprn 10009433145): post SAP 82.4 below effective baseline 83.0 (Δ-0.6) +- property **710650** (uprn 200001530089): post SAP 72.9 below effective baseline 74.0 (Δ-1.1) +- property **710686** (uprn 10009433147): post SAP 82.1 below effective baseline 83.0 (Δ-0.9) +- property **710760** (uprn 10096028302): post SAP 78.3 below effective baseline 85.0 (Δ-6.7) +- property **710785** (uprn 44006007): post SAP 75.3 below effective baseline 76.0 (Δ-0.7) +- property **710792** (uprn 5300059024): post SAP 75.7 below effective baseline 79.0 (Δ-3.3) +- property **710800** (uprn 10096399558): post SAP 79.7 below effective baseline 93.0 (Δ-13.3) +- property **710802** (uprn 10096028356): post SAP 78.5 below effective baseline 91.0 (Δ-12.5) +- property **710841** (uprn 200003688154): post SAP 70.3 below effective baseline 72.0 (Δ-1.7) +- property **710895** (uprn 100020475290): post SAP 72.4 below effective baseline 73.0 (Δ-0.6) +- property **710911** (uprn 10010221820): post SAP 82.2 below effective baseline 84.0 (Δ-1.8) +- property **710946** (uprn 44012846): post SAP 79.8 below effective baseline 81.0 (Δ-1.2) +- property **710955** (uprn 10090844951): post SAP 78.1 below effective baseline 79.0 (Δ-0.9) +- property **710988** (uprn 10023371802): post SAP 73.3 below effective baseline 75.0 (Δ-1.7) +- property **711014** (uprn 5300088717): post SAP 72.5 below effective baseline 74.0 (Δ-1.5) +- … and 1581 more (see CSV) + +## already-meets-goal-with-works (MEDIUM) — 1162 + +- property **709775** (uprn 100020933699): already C >= goal C but cost_of_works £32 +- property **709875** (uprn 100020973465): already C >= goal C but cost_of_works £38 +- property **709986** (uprn 200000539408): already C >= goal C but cost_of_works £32 +- property **709992** (uprn 100022908998): already C >= goal C but cost_of_works £555 +- property **710005** (uprn 100090178307): already C >= goal C but cost_of_works £21 +- property **710033** (uprn 100022920891): already C >= goal C but cost_of_works £855 +- property **710084** (uprn 200003444301): already C >= goal C but cost_of_works £505 +- property **710185** (uprn 100020650847): already C >= goal C but cost_of_works £455 +- property **710221** (uprn 6701328): already C >= goal C but cost_of_works £655 +- property **710247** (uprn 100020432467): already C >= goal C but cost_of_works £555 +- property **710298** (uprn 100020220397): already C >= goal C but cost_of_works £4515 +- property **710331** (uprn 100020492824): already C >= goal C but cost_of_works £855 +- property **710400** (uprn 100021011618): already C >= goal C but cost_of_works £3725 +- property **710484** (uprn 100060316083): already C >= goal C but cost_of_works £24 +- property **710517** (uprn 100020951618): already C >= goal C but cost_of_works £1029 +- property **710525** (uprn 10012028784): already C >= goal C but cost_of_works £11539 +- property **710539** (uprn 100021973960): already C >= goal C but cost_of_works £505 +- property **710540** (uprn 100060714157): already C >= goal C but cost_of_works £11600 +- property **710555** (uprn 100060718869): already C >= goal C but cost_of_works £28 +- property **710574** (uprn 100020947162): already C >= goal C but cost_of_works £28 +- property **710690** (uprn 200003688150): already C >= goal C but cost_of_works £505 +- property **710703** (uprn 100020438146): already C >= goal C but cost_of_works £32 +- property **710707** (uprn 100061820799): already C >= goal C but cost_of_works £32 +- property **710713** (uprn 200003497667): already C >= goal C but cost_of_works £11082 +- property **710828** (uprn 100060714174): already C >= goal C but cost_of_works £21 +- property **710889** (uprn 200003469113): already C >= goal C but cost_of_works £455 +- property **711058** (uprn 5300059062): already C >= goal C but cost_of_works £3962 +- property **711096** (uprn 100020455891): already C >= goal C but cost_of_works £555 +- property **711099** (uprn 100020229279): already C >= goal C but cost_of_works £1029 +- property **711334** (uprn 5300036435): already C >= goal C but cost_of_works £476 +- property **711390** (uprn 100023263012): already C >= goal C but cost_of_works £505 +- property **711539** (uprn 68159801): already C >= goal C but cost_of_works £2436 +- property **711679** (uprn 100021005287): already C >= goal C but cost_of_works £455 +- property **711694** (uprn 100062191221): already C >= goal C but cost_of_works £18 +- property **711795** (uprn 10090343115): already C >= goal C but cost_of_works £662 +- property **712249** (uprn 100021939292): already C >= goal C but cost_of_works £1029 +- property **712279** (uprn 10090343152): already C >= goal C but cost_of_works £1029 +- property **712426** (uprn 100062208849): already C >= goal C but cost_of_works £530 +- property **712446** (uprn 10090343167): already C >= goal C but cost_of_works £1029 +- property **712690** (uprn 10035061455): already C >= goal C but cost_of_works £2790 +- property **712697** (uprn 100060742520): already C >= goal C but cost_of_works £416 +- property **712764** (uprn 100060675900): already C >= goal C but cost_of_works £32 +- property **712766** (uprn 100021961011): already C >= goal C but cost_of_works £2566 +- property **712801** (uprn 100061734876): already C >= goal C but cost_of_works £555 +- property **712831** (uprn 100061736377): already C >= goal C but cost_of_works £1282 +- property **712864** (uprn 100061738178): already C >= goal C but cost_of_works £455 +- property **712865** (uprn 100061738325): already C >= goal C but cost_of_works £14266 +- property **712869** (uprn 100061738604): already C >= goal C but cost_of_works £18 +- property **712874** (uprn 100061757360): already C >= goal C but cost_of_works £755 +- property **712899** (uprn 100061739860): already C >= goal C but cost_of_works £28 +- … and 1112 more (see CSV) + +## excessive-solar-sap (MEDIUM) — 51 + +- property **710374** (uprn 10090342188): solar PV alone earns 26.1 SAP points (likely oversized array) +- property **713238** (uprn 200004784741): solar PV alone earns 32.6 SAP points (likely oversized array) +- property **714227** (uprn 100061738603): solar PV alone earns 33.9 SAP points (likely oversized array) +- property **714257** (uprn 100061740105): solar PV alone earns 29.8 SAP points (likely oversized array) +- property **714511** (uprn 100061761168): solar PV alone earns 32.3 SAP points (likely oversized array) +- property **714977** (uprn 100061761946): solar PV alone earns 31.4 SAP points (likely oversized array) +- property **715563** (uprn 100061761947): solar PV alone earns 28.5 SAP points (likely oversized array) +- property **715595** (uprn 10002468656): solar PV alone earns 29.3 SAP points (likely oversized array) +- property **715740** (uprn 100061764300): solar PV alone earns 27.3 SAP points (likely oversized array) +- property **716734** (uprn 10002469029): solar PV alone earns 31.0 SAP points (likely oversized array) +- property **716740** (uprn 100061753334): solar PV alone earns 31.1 SAP points (likely oversized array) +- property **717201** (uprn 100091206047): solar PV alone earns 31.3 SAP points (likely oversized array) +- property **718323** (uprn 10002472608): solar PV alone earns 25.2 SAP points (likely oversized array) +- property **718371** (uprn 100061764075): solar PV alone earns 35.5 SAP points (likely oversized array) +- property **718890** (uprn 100062344151): solar PV alone earns 31.9 SAP points (likely oversized array) +- property **719064** (uprn 100021004675): solar PV alone earns 25.6 SAP points (likely oversized array) +- property **719080** (uprn 100091206033): solar PV alone earns 31.3 SAP points (likely oversized array) +- property **719829** (uprn 100091206052): solar PV alone earns 31.6 SAP points (likely oversized array) +- property **720039** (uprn 100061741993): solar PV alone earns 32.6 SAP points (likely oversized array) +- property **720104** (uprn 100061764078): solar PV alone earns 29.9 SAP points (likely oversized array) +- property **721044** (uprn 100061764081): solar PV alone earns 33.9 SAP points (likely oversized array) +- property **721222** (uprn 100062480566): solar PV alone earns 27.0 SAP points (likely oversized array) +- property **722986** (uprn 100061742571): solar PV alone earns 29.8 SAP points (likely oversized array) +- property **723096** (uprn 100061763625): solar PV alone earns 34.5 SAP points (likely oversized array) +- property **723516** (uprn 100061741124): solar PV alone earns 31.0 SAP points (likely oversized array) +- property **723528** (uprn 100061742003): solar PV alone earns 30.9 SAP points (likely oversized array) +- property **724609** (uprn 100061742008): solar PV alone earns 27.0 SAP points (likely oversized array) +- property **725224** (uprn 100062480573): solar PV alone earns 27.6 SAP points (likely oversized array) +- property **725266** (uprn 10034506076): solar PV alone earns 38.5 SAP points (likely oversized array) +- property **725603** (uprn 100061761161): solar PV alone earns 35.6 SAP points (likely oversized array) +- property **727422** (uprn 200001645537): solar PV alone earns 29.4 SAP points (likely oversized array) +- property **727688** (uprn 100061761162): solar PV alone earns 28.8 SAP points (likely oversized array) +- property **727699** (uprn 200004784746): solar PV alone earns 27.3 SAP points (likely oversized array) +- property **727968** (uprn 100061740165): solar PV alone earns 34.5 SAP points (likely oversized array) +- property **728344** (uprn 100061764582): solar PV alone earns 26.9 SAP points (likely oversized array) +- property **729028** (uprn 100061735428): solar PV alone earns 32.0 SAP points (likely oversized array) +- property **729329** (uprn 100061749297): solar PV alone earns 30.2 SAP points (likely oversized array) +- property **729415** (uprn 100091206037): solar PV alone earns 31.5 SAP points (likely oversized array) +- property **731052** (uprn 100021958934): solar PV alone earns 29.3 SAP points (likely oversized array) +- property **731481** (uprn 100021935669): solar PV alone earns 25.1 SAP points (likely oversized array) +- property **731812** (uprn 200001645541): solar PV alone earns 32.1 SAP points (likely oversized array) +- property **731994** (uprn 10023371767): solar PV alone earns 25.3 SAP points (likely oversized array) +- property **732041** (uprn 100061761165): solar PV alone earns 30.1 SAP points (likely oversized array) +- property **732050** (uprn 100061764295): solar PV alone earns 30.5 SAP points (likely oversized array) +- property **732860** (uprn 100061733678): solar PV alone earns 32.7 SAP points (likely oversized array) +- property **733394** (uprn 100061748753): solar PV alone earns 25.1 SAP points (likely oversized array) +- property **739556** (uprn 100061754547): solar PV alone earns 31.3 SAP points (likely oversized array) +- property **739814** (uprn 200003724253): solar PV alone earns 31.6 SAP points (likely oversized array) +- property **739998** (uprn 100090224018): solar PV alone earns 25.9 SAP points (likely oversized array) +- property **740214** (uprn 100061762820): solar PV alone earns 30.4 SAP points (likely oversized array) +- … and 1 more (see CSV) + +## low-solar-bill-savings (MEDIUM) — 83 + +- property **710178** (uprn 100020465019): solar PV bill saving only £49/yr — check self-consumption / SEG export +- property **711663** (uprn 100020481859): solar PV bill saving only £47/yr — check self-consumption / SEG export +- property **712943** (uprn 100061741910): solar PV bill saving only £47/yr — check self-consumption / SEG export +- property **713299** (uprn 100060331540): solar PV bill saving only £46/yr — check self-consumption / SEG export +- property **713607** (uprn 100090108855): solar PV bill saving only £46/yr — check self-consumption / SEG export +- property **713631** (uprn 10002470912): solar PV bill saving only £49/yr — check self-consumption / SEG export +- property **713743** (uprn 100060359906): solar PV bill saving only £36/yr — check self-consumption / SEG export +- property **715182** (uprn 100060726237): solar PV bill saving only £46/yr — check self-consumption / SEG export +- property **715701** (uprn 100021001715): solar PV bill saving only £50/yr — check self-consumption / SEG export +- property **716152** (uprn 100090108859): solar PV bill saving only £43/yr — check self-consumption / SEG export +- property **716268** (uprn 100061763778): solar PV bill saving only £36/yr — check self-consumption / SEG export +- property **716289** (uprn 200003688201): solar PV bill saving only £43/yr — check self-consumption / SEG export +- property **716659** (uprn 100061747951): solar PV bill saving only £41/yr — check self-consumption / SEG export +- property **716693** (uprn 100061733911): solar PV bill saving only £39/yr — check self-consumption / SEG export +- property **717392** (uprn 100020450723): solar PV bill saving only £45/yr — check self-consumption / SEG export +- property **717661** (uprn 6176751): solar PV bill saving only £42/yr — check self-consumption / SEG export +- property **718073** (uprn 100021017285): solar PV bill saving only £47/yr — check self-consumption / SEG export +- property **718298** (uprn 100061741870): solar PV bill saving only £43/yr — check self-consumption / SEG export +- property **718567** (uprn 100091206050): solar PV bill saving only £50/yr — check self-consumption / SEG export +- property **719647** (uprn 100020590930): solar PV bill saving only £49/yr — check self-consumption / SEG export +- property **720335** (uprn 100020594071): solar PV bill saving only £36/yr — check self-consumption / SEG export +- property **720677** (uprn 100020969500): solar PV bill saving only £30/yr — check self-consumption / SEG export +- property **721018** (uprn 202140958): solar PV bill saving only £44/yr — check self-consumption / SEG export +- property **721529** (uprn 100060696301): solar PV bill saving only £43/yr — check self-consumption / SEG export +- property **721664** (uprn 100061765249): solar PV bill saving only £46/yr — check self-consumption / SEG export +- property **721895** (uprn 100060726253): solar PV bill saving only £48/yr — check self-consumption / SEG export +- property **722155** (uprn 100061758903): solar PV bill saving only £47/yr — check self-consumption / SEG export +- property **722390** (uprn 100020944918): solar PV bill saving only £47/yr — check self-consumption / SEG export +- property **722910** (uprn 10009428749): solar PV bill saving only £41/yr — check self-consumption / SEG export +- property **723298** (uprn 202140961): solar PV bill saving only £45/yr — check self-consumption / SEG export +- property **723578** (uprn 100020986231): solar PV bill saving only £47/yr — check self-consumption / SEG export +- property **723748** (uprn 100061757556): solar PV bill saving only £48/yr — check self-consumption / SEG export +- property **723965** (uprn 100061761860): solar PV bill saving only £50/yr — check self-consumption / SEG export +- property **724311** (uprn 100061765331): solar PV bill saving only £30/yr — check self-consumption / SEG export +- property **724331** (uprn 100022008224): solar PV bill saving only £22/yr — check self-consumption / SEG export +- property **724850** (uprn 100060719545): solar PV bill saving only £50/yr — check self-consumption / SEG export +- property **725287** (uprn 100062187008): solar PV bill saving only £46/yr — check self-consumption / SEG export +- property **725585** (uprn 100061752475): solar PV bill saving only £44/yr — check self-consumption / SEG export +- property **725807** (uprn 100020996065): solar PV bill saving only £40/yr — check self-consumption / SEG export +- property **725969** (uprn 202141567): solar PV bill saving only £31/yr — check self-consumption / SEG export +- property **726419** (uprn 100061809723): solar PV bill saving only £46/yr — check self-consumption / SEG export +- property **726507** (uprn 100061752706): solar PV bill saving only £48/yr — check self-consumption / SEG export +- property **726627** (uprn 100061152652): solar PV bill saving only £42/yr — check self-consumption / SEG export +- property **726735** (uprn 100061809725): solar PV bill saving only £46/yr — check self-consumption / SEG export +- property **726998** (uprn 100061809727): solar PV bill saving only £46/yr — check self-consumption / SEG export +- property **727114** (uprn 100061809728): solar PV bill saving only £47/yr — check self-consumption / SEG export +- property **727143** (uprn 202141571): solar PV bill saving only £45/yr — check self-consumption / SEG export +- property **727820** (uprn 100061809729): solar PV bill saving only £47/yr — check self-consumption / SEG export +- property **727994** (uprn 202141572): solar PV bill saving only £45/yr — check self-consumption / SEG export +- property **728027** (uprn 10090265602): solar PV bill saving only £45/yr — check self-consumption / SEG export +- … and 33 more (see CSV) + +## zero-works-post-differs (MEDIUM) — 5624 + +- property **709772** (uprn 10093116528): £0 works but post SAP 80.3 != effective 79.0 +- property **709773** (uprn 10093116543): £0 works but post SAP 78.5 != effective 77.0 +- property **709774** (uprn 10093116529): £0 works but post SAP 76.8 != effective 75.0 +- property **709777** (uprn 10094601392): £0 works but post SAP 77.2 != effective 74.0 +- property **709778** (uprn 10023444324): £0 works but post SAP 76.9 != effective 75.0 +- property **709779** (uprn 10092970673): £0 works but post SAP 70.9 != effective 61.0 +- property **709780** (uprn 10094601287): £0 works but post SAP 77.7 != effective 75.0 +- property **709783** (uprn 10094601162): £0 works but post SAP 78.1 != effective 74.0 +- property **709784** (uprn 10090844948): £0 works but post SAP 76.4 != effective 73.0 +- property **709786** (uprn 10093114053): £0 works but post SAP 81.3 != effective 76.0 +- property **709787** (uprn 10091568921): £0 works but post SAP 79.4 != effective 77.0 +- property **709788** (uprn 10093718424): £0 works but post SAP 80.0 != effective 78.0 +- property **709790** (uprn 10023443426): £0 works but post SAP 74.2 != effective 76.0 +- property **709791** (uprn 10093412452): £0 works but post SAP 80.2 != effective 79.0 +- property **709792** (uprn 6199384): £0 works but post SAP 80.5 != effective 78.0 +- property **709793** (uprn 10014314798): £0 works but post SAP 73.8 != effective 72.0 +- property **709794** (uprn 10094601294): £0 works but post SAP 78.1 != effective 76.0 +- property **709795** (uprn 10090343335): £0 works but post SAP 84.0 != effective 82.0 +- property **709796** (uprn 10093115480): £0 works but post SAP 78.8 != effective 77.0 +- property **709798** (uprn 10094601226): £0 works but post SAP 77.3 != effective 75.0 +- property **709800** (uprn 6701369): £0 works but post SAP 82.5 != effective 78.0 +- property **709801** (uprn 202211152): £0 works but post SAP 81.8 != effective 79.0 +- property **709803** (uprn 10093394010): £0 works but post SAP 79.9 != effective 78.0 +- property **709806** (uprn 10090341811): £0 works but post SAP 80.2 != effective 78.0 +- property **709808** (uprn 10093117227): £0 works but post SAP 77.6 != effective 76.0 +- property **709809** (uprn 10023444170): £0 works but post SAP 80.3 != effective 78.0 +- property **709810** (uprn 10096028301): £0 works but post SAP 78.0 != effective 85.0 +- property **709813** (uprn 10094601280): £0 works but post SAP 77.9 != effective 75.0 +- property **709814** (uprn 10093386418): £0 works but post SAP 78.8 != effective 76.0 +- property **709817** (uprn 10094895444): £0 works but post SAP 79.6 != effective 77.0 +- property **709818** (uprn 10092973960): £0 works but post SAP 77.6 != effective 74.0 +- property **709819** (uprn 10012028763): £0 works but post SAP 83.3 != effective 82.0 +- property **709820** (uprn 10093049867): £0 works but post SAP 78.7 != effective 75.0 +- property **709821** (uprn 10093116336): £0 works but post SAP 79.9 != effective 78.0 +- property **709823** (uprn 10093116334): £0 works but post SAP 79.0 != effective 78.0 +- property **709824** (uprn 44042992): £0 works but post SAP 79.9 != effective 79.0 +- property **709825** (uprn 10014314853): £0 works but post SAP 72.0 != effective 70.0 +- property **709828** (uprn 10091636116): £0 works but post SAP 75.8 != effective 71.0 +- property **709829** (uprn 10094601381): £0 works but post SAP 78.4 != effective 74.0 +- property **709830** (uprn 10093049853): £0 works but post SAP 81.5 != effective 78.0 +- property **709831** (uprn 10093390790): £0 works but post SAP 74.9 != effective 72.0 +- property **709832** (uprn 10093116330): £0 works but post SAP 80.1 != effective 79.0 +- property **709833** (uprn 10093116326): £0 works but post SAP 80.1 != effective 79.0 +- property **709834** (uprn 10094601351): £0 works but post SAP 79.0 != effective 76.0 +- property **709835** (uprn 10090317693): £0 works but post SAP 77.5 != effective 76.0 +- property **709836** (uprn 10090034872): £0 works but post SAP 81.4 != effective 79.0 +- property **709837** (uprn 10093115985): £0 works but post SAP 79.5 != effective 77.0 +- property **709842** (uprn 202211170): £0 works but post SAP 82.1 != effective 79.0 +- property **709845** (uprn 6701311): £0 works but post SAP 81.4 != effective 78.0 +- property **709846** (uprn 10096399556): £0 works but post SAP 79.7 != effective 93.0 +- … and 5574 more (see CSV) + +## effective-lodged-divergence (LOW) — 1527 + +- property **709779** (uprn 10092970673): effective 61 vs lodged 86 (Δ-25, reason=pre_sap10) +- property **709802** (uprn 100020665611): effective 52 vs lodged 37 (Δ+15, reason=pre_sap10) +- property **709804** (uprn 10093388044): effective 33 vs lodged 93 (Δ-60, reason=pre_sap10) +- property **709815** (uprn 100090108846): effective 57 vs lodged 79 (Δ-22, reason=pre_sap10) +- property **709828** (uprn 10091636116): effective 71 vs lodged 88 (Δ-17, reason=pre_sap10) +- property **709860** (uprn 100061086424): effective 60 vs lodged 83 (Δ-23, reason=pre_sap10) +- property **709863** (uprn 10090342180): effective 39 vs lodged 78 (Δ-39, reason=pre_sap10) +- property **709874** (uprn 10093388053): effective 35 vs lodged 94 (Δ-59, reason=pre_sap10) +- property **709892** (uprn 10090343767): effective 75 vs lodged 91 (Δ-16, reason=pre_sap10) +- property **709945** (uprn 10090342181): effective 42 vs lodged 81 (Δ-39, reason=pre_sap10) +- property **709993** (uprn 15043874): effective 68 vs lodged 49 (Δ+19, reason=pre_sap10) +- property **710003** (uprn 10091636410): effective 71 vs lodged 86 (Δ-15, reason=pre_sap10) +- property **710009** (uprn 100022895379): effective 62 vs lodged 80 (Δ-18, reason=pre_sap10) +- property **710019** (uprn 10090342182): effective 44 vs lodged 80 (Δ-36, reason=pre_sap10) +- property **710025** (uprn 10093388055): effective 55 vs lodged 93 (Δ-38, reason=pre_sap10) +- property **710037** (uprn 100090108857): effective 56 vs lodged 86 (Δ-30, reason=pre_sap10) +- property **710091** (uprn 100061086427): effective 60 vs lodged 83 (Δ-23, reason=pre_sap10) +- property **710114** (uprn 10096026315): effective 74 vs lodged 89 (Δ-15, reason=pre_sap10) +- property **710134** (uprn 10096026271): effective 73 vs lodged 88 (Δ-15, reason=pre_sap10) +- property **710139** (uprn 10090342183): effective 41 vs lodged 77 (Δ-36, reason=pre_sap10) +- property **710144** (uprn 10014314832): effective 63 vs lodged 83 (Δ-20, reason=pre_sap10) +- property **710148** (uprn 10093388056): effective 57 vs lodged 94 (Δ-37, reason=pre_sap10) +- property **710191** (uprn 10090342184): effective 49 vs lodged 79 (Δ-30, reason=pre_sap10) +- property **710198** (uprn 10012138502): effective 65 vs lodged 85 (Δ-20, reason=pre_sap10) +- property **710199** (uprn 10093388057): effective 38 vs lodged 95 (Δ-57, reason=pre_sap10) +- property **710202** (uprn 10090342864): effective 68 vs lodged 84 (Δ-16, reason=pre_sap10) +- property **710244** (uprn 10010249676): effective 74 vs lodged 90 (Δ-16, reason=pre_sap10) +- property **710246** (uprn 100061086430): effective 59 vs lodged 83 (Δ-24, reason=pre_sap10) +- property **710250** (uprn 10090342185): effective 62 vs lodged 81 (Δ-19, reason=pre_sap10) +- property **710260** (uprn 10093388058): effective 47 vs lodged 95 (Δ-48, reason=pre_sap10) +- property **710261** (uprn 10090341825): effective 75 vs lodged 91 (Δ-16, reason=pre_sap10) +- property **710300** (uprn 10090341826): effective 75 vs lodged 91 (Δ-16, reason=pre_sap10) +- property **710342** (uprn 10090342187): effective 37 vs lodged 77 (Δ-40, reason=pre_sap10) +- property **710348** (uprn 10090341827): effective 76 vs lodged 91 (Δ-15, reason=pre_sap10) +- property **710429** (uprn 10093388045): effective 49 vs lodged 93 (Δ-44, reason=pre_sap10) +- property **710449** (uprn 100020993501): effective 56 vs lodged 5 (Δ+51, reason=pre_sap10) +- property **710491** (uprn 10014314835): effective 69 vs lodged 84 (Δ-15, reason=pre_sap10) +- property **710536** (uprn 100062482536): effective 38 vs lodged 53 (Δ-15, reason=pre_sap10) +- property **710640** (uprn 10014316004): effective 71 vs lodged 88 (Δ-17, reason=pre_sap10) +- property **710658** (uprn 10014316005): effective 70 vs lodged 88 (Δ-18, reason=pre_sap10) +- property **710659** (uprn 22061763): effective 50 vs lodged 70 (Δ-20, reason=pre_sap10) +- property **710664** (uprn 10096026322): effective 74 vs lodged 89 (Δ-15, reason=pre_sap10) +- property **710672** (uprn 200003378407): effective 48 vs lodged 28 (Δ+20, reason=pre_sap10) +- property **710683** (uprn 10014316006): effective 70 vs lodged 88 (Δ-18, reason=pre_sap10) +- property **710704** (uprn 10014316007): effective 71 vs lodged 88 (Δ-17, reason=pre_sap10) +- property **710745** (uprn 10093386244): effective 50 vs lodged 83 (Δ-33, reason=pre_sap10) +- property **710752** (uprn 10093388046): effective 47 vs lodged 94 (Δ-47, reason=pre_sap10) +- property **710779** (uprn 10091636118): effective 71 vs lodged 88 (Δ-17, reason=pre_sap10) +- property **710814** (uprn 10014316008): effective 71 vs lodged 88 (Δ-17, reason=pre_sap10) +- property **710837** (uprn 10014316009): effective 71 vs lodged 88 (Δ-17, reason=pre_sap10) +- … and 1477 more (see CSV) + +## negative-bill-savings (LOW) — 100 + +- property **712847** (uprn 100061737192): energy_bill_savings £-30/yr on £16110 of works +- property **713453** (uprn 100061739869): energy_bill_savings £-68/yr on £14533 of works +- property **713721** (uprn 100061753373): energy_bill_savings £-18/yr on £14554 of works +- property **713743** (uprn 100060359906): energy_bill_savings £-98/yr on £18823 of works +- property **713791** (uprn 10013528635): energy_bill_savings £-130/yr on £1029 of works +- property **713970** (uprn 10013528640): energy_bill_savings £-130/yr on £1029 of works +- property **714001** (uprn 10013528641): energy_bill_savings £-130/yr on £1029 of works +- property **714038** (uprn 10013528642): energy_bill_savings £-130/yr on £1029 of works +- property **714373** (uprn 100062185281): energy_bill_savings £-113/yr on £14800 of works +- property **714420** (uprn 10010242243): energy_bill_savings £-162/yr on £1029 of works +- property **715227** (uprn 100061753375): energy_bill_savings £-18/yr on £14554 of works +- property **715561** (uprn 100061739874): energy_bill_savings £-69/yr on £14533 of works +- property **715999** (uprn 100061737204): energy_bill_savings £-13/yr on £14533 of works +- property **716169** (uprn 100061747915): energy_bill_savings £-53/yr on £14533 of works +- property **716251** (uprn 100061753377): energy_bill_savings £-18/yr on £14554 of works +- property **716548** (uprn 100061765632): energy_bill_savings £-70/yr on £14533 of works +- property **716811** (uprn 22106963): energy_bill_savings £-3/yr on £4785 of works +- property **717209** (uprn 100061753379): energy_bill_savings £-20/yr on £14554 of works +- property **717675** (uprn 6176752): energy_bill_savings £-75/yr on £15067 of works +- property **717998** (uprn 100061749651): energy_bill_savings £-7/yr on £14533 of works +- property **719002** (uprn 10002476550): energy_bill_savings £-101/yr on £14533 of works +- property **719091** (uprn 100061753365): energy_bill_savings £-46/yr on £14533 of works +- property **719449** (uprn 100061753383): energy_bill_savings £-18/yr on £14554 of works +- property **719680** (uprn 100061741246): energy_bill_savings £-34/yr on £17072 of works +- property **719811** (uprn 100060327562): energy_bill_savings £-92/yr on £14533 of works +- property **720349** (uprn 100021812910): energy_bill_savings £-122/yr on £1029 of works +- property **720364** (uprn 100061741682): energy_bill_savings £-51/yr on £15070 of works +- property **720800** (uprn 100061753387): energy_bill_savings £-20/yr on £14554 of works +- property **720889** (uprn 10023224588): energy_bill_savings £-172/yr on £1029 of works +- property **720989** (uprn 100061764553): energy_bill_savings £-143/yr on £14533 of works +- property **721034** (uprn 100061758484): energy_bill_savings £-145/yr on £15105 of works +- property **721070** (uprn 100061749663): energy_bill_savings £-69/yr on £14800 of works +- property **722402** (uprn 100061737194): energy_bill_savings £-73/yr on £14266 of works +- property **722448** (uprn 100061739862): energy_bill_savings £-68/yr on £14533 of works +- property **722732** (uprn 100061750203): energy_bill_savings £-155/yr on £16445 of works +- property **722808** (uprn 100062186150): energy_bill_savings £-61/yr on £14533 of works +- property **723367** (uprn 100061753394): energy_bill_savings £-56/yr on £14533 of works +- property **723503** (uprn 10010242632): energy_bill_savings £-112/yr on £1029 of works +- property **723540** (uprn 100061743541): energy_bill_savings £-35/yr on £14533 of works +- property **723771** (uprn 200001645519): energy_bill_savings £-58/yr on £14533 of works +- property **723782** (uprn 100061743542): energy_bill_savings £-30/yr on £17032 of works +- property **725039** (uprn 100061748331): energy_bill_savings £-4/yr on £14800 of works +- property **725350** (uprn 100061743145): energy_bill_savings £-189/yr on £15067 of works +- property **725715** (uprn 100061736694): energy_bill_savings £-42/yr on £14533 of works +- property **725731** (uprn 100061763962): energy_bill_savings £-71/yr on £14533 of works +- property **725988** (uprn 100061749671): energy_bill_savings £-13/yr on £14533 of works +- property **726184** (uprn 100061752891): energy_bill_savings £-66/yr on £14533 of works +- property **726268** (uprn 100061741268): energy_bill_savings £-76/yr on £14266 of works +- property **726271** (uprn 100061741692): energy_bill_savings £-63/yr on £14533 of works +- property **726302** (uprn 202141568): energy_bill_savings £-73/yr on £15721 of works +- … and 50 more (see CSV) + +## unusually-high-post-sap (LOW) — 21 + +- property **714511** (uprn 100061761168): post SAP 100.0 (near band A) — confirm not over-credited +- property **716734** (uprn 10002469029): post SAP 100.0 (near band A) — confirm not over-credited +- property **716740** (uprn 100061753334): post SAP 100.0 (near band A) — confirm not over-credited +- property **717201** (uprn 100091206047): post SAP 100.0 (near band A) — confirm not over-credited +- property **718890** (uprn 100062344151): post SAP 100.0 (near band A) — confirm not over-credited +- property **719080** (uprn 100091206033): post SAP 100.0 (near band A) — confirm not over-credited +- property **719829** (uprn 100091206052): post SAP 100.0 (near band A) — confirm not over-credited +- property **721222** (uprn 100062480566): post SAP 98.6 (near band A) — confirm not over-credited +- property **723516** (uprn 100061741124): post SAP 100.0 (near band A) — confirm not over-credited +- property **725224** (uprn 100062480573): post SAP 95.3 (near band A) — confirm not over-credited +- property **725440** (uprn 100061746447): post SAP 100.0 (near band A) — confirm not over-credited +- property **725627** (uprn 10008885879): post SAP 99.6 (near band A) — confirm not over-credited +- property **726993** (uprn 100061757571): post SAP 100.0 (near band A) — confirm not over-credited +- property **727688** (uprn 100061761162): post SAP 97.6 (near band A) — confirm not over-credited +- property **727699** (uprn 200004784746): post SAP 100.0 (near band A) — confirm not over-credited +- property **729329** (uprn 100061749297): post SAP 99.2 (near band A) — confirm not over-credited +- property **729415** (uprn 100091206037): post SAP 100.0 (near band A) — confirm not over-credited +- property **732041** (uprn 100061761165): post SAP 97.9 (near band A) — confirm not over-credited +- property **732050** (uprn 100061764295): post SAP 98.1 (near band A) — confirm not over-credited +- property **740214** (uprn 100061762820): post SAP 99.4 (near band A) — confirm not over-credited +- property **740234** (uprn 100061761814): post SAP 100.0 (near band A) — confirm not over-credited diff --git a/scripts/audit/anomalies.py b/scripts/audit/anomalies.py index 420d1be9..48f16875 100644 --- a/scripts/audit/anomalies.py +++ b/scripts/audit/anomalies.py @@ -245,6 +245,7 @@ _QUERY = text( FROM property p LEFT JOIN property_baseline_performance pbp ON pbp.property_id = p.id LEFT JOIN plan pl ON pl.property_id = p.id AND pl.is_default = TRUE + AND (:scenario_id IS NULL OR pl.scenario_id = :scenario_id) LEFT JOIN scenario s ON s.id = pl.scenario_id WHERE (:portfolio_id IS NULL OR p.portfolio_id = :portfolio_id) AND (:property_id IS NULL OR p.id = :property_id) @@ -261,6 +262,7 @@ _ROLLUP_QUERY = text( COUNT(*) AS n_measures FROM recommendation r JOIN plan pl ON pl.id = r.plan_id AND pl.is_default = TRUE + AND (:scenario_id IS NULL OR pl.scenario_id = :scenario_id) JOIN property p ON p.id = r.property_id WHERE (:portfolio_id IS NULL OR p.portfolio_id = :portfolio_id) AND (:property_id IS NULL OR p.id = :property_id) @@ -269,23 +271,26 @@ _ROLLUP_QUERY = text( ) -def _load(portfolio_id: Optional[int], property_id: Optional[int]) -> list[PropertyAudit]: +def _load( + portfolio_id: Optional[int], + property_id: Optional[int], + scenario_id: Optional[int], +) -> list[PropertyAudit]: engine = build_engine() out: list[PropertyAudit] = [] + params = { + "portfolio_id": portfolio_id, + "property_id": property_id, + "scenario_id": scenario_id, + } with engine.connect() as conn: rollups: dict[int, tuple[Optional[float], Optional[float], int]] = { m["property_id"]: (m["solar_sap"], m["solar_bill"], m["n_measures"]) for m in ( - row._mapping - for row in conn.execute( - _ROLLUP_QUERY, - {"portfolio_id": portfolio_id, "property_id": property_id}, - ) + row._mapping for row in conn.execute(_ROLLUP_QUERY, params) ) } - for r in conn.execute( - _QUERY, {"portfolio_id": portfolio_id, "property_id": property_id} - ): + for r in conn.execute(_QUERY, params): m = r._mapping solar_sap, solar_bill, n_measures = rollups.get(m["id"], (None, None, 0)) out.append( @@ -361,6 +366,9 @@ def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--portfolio", type=int, default=None, help="portfolio_id to scan") parser.add_argument("--property", type=int, default=None, help="a single property_id") + parser.add_argument( + "--scenario", type=int, default=None, help="restrict to one scenario_id" + ) parser.add_argument( "--severity", choices=[s.name.lower() for s in Severity], @@ -370,7 +378,7 @@ def main() -> None: args = parser.parse_args() min_severity = Severity[args.severity.upper()] - audits = _load(args.portfolio, args.property) + audits = _load(args.portfolio, args.property, args.scenario) anomalies = run(audits, min_severity) _write_reports(anomalies, len(audits))