UX-10: the last plain-language leftovers: log lines, two table rows, 'Why: Other', red at 0 s
- test_messages.py part 6: every dynamic part of a WARNING+ line is classified as data or as a built text, and every built text is produced by the real code and checked. This is the gap the 2d scan had: it counted `%s` as one word, so reasons built by loop_clamp_note() and friends were never seen. - Quoted reasons rewritten to STYLE.md (red-alarm shape, codes kept): OUTPUT_CLAMP, METER_FROZEN, CIRCULATING_POWER, STALE_READING, the peak guard (codes now PEAK_GUARD*), the migration report, price feed, price history and gate reasons. Plurals via messages.times()/count(). - Top table: Price history "96 prices over 24 hours for 26 Sep", Tariff "one every 15 minutes or every hour", "not final yet" dropped. - Levels and noise: per-cycle shadow line DEBUG, shadow on/off INFO, aiohttp access log only at log level debug, settings warnings once per start, one plain line per Home Assistant outage (hass.Outage). - INFO jargon and error texts: no "§8.3", ISO times, "banked", "keys ARE", "NOT_OBSERVED", "did NOT land", "AND". - Display bugs: WHY_WINNER covers every winner and safety reason (main.why_key(), part 7 finds them in the code); the meter row is amber "waiting for the first reading" inside its limit after a start; the missing-reading age is counted when the page asks. - STYLE.md and DECISIONS/UX-08-short-notifications.md (UX-10 section). No change to safety behaviour, alarm levels, notification ids, triggers, cadence or the banner cascade. One row colour changes (meter at start), justified in DECISIONS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
125b0702a2
commit
23a3a3f5b5
@@ -4107,7 +4107,8 @@ _T11_SLOTS = {
|
||||
"carried": r"\d+ published and (\d+) estimated",
|
||||
"horizon": r"for (\d+) hours ahead",
|
||||
"age_s": r"Price plan from (\d+) s ago",
|
||||
"spacings": r"([\d+]+) s apart",
|
||||
# UX-10: the spacing is in words ("one every 15 minutes or every hour").
|
||||
"spacings": r"one (every [a-z0-9 ]+?)(?:,|\.|$)",
|
||||
# ⚠️ THE *Arbitrage* ROW'S OWN AGE SLOT, and it is deliberately the same
|
||||
# pattern for "decided N s ago" and "the last one was taken N s ago" -
|
||||
# WHICH of the two sentences was rendered is graded by inequality below,
|
||||
@@ -4125,6 +4126,18 @@ _T11_SLOTS = {
|
||||
}
|
||||
|
||||
|
||||
def _spacing_s(raw):
|
||||
"""`every 15 minutes or every hour` -> `[900, 3600]`; raises otherwise."""
|
||||
unit = {"second": 1, "minute": 60, "hour": 3600}
|
||||
out = []
|
||||
for part in raw.split(" or "):
|
||||
m = re.fullmatch(r"every (?:(\d+) )?(second|minute|hour)s?", part.strip())
|
||||
if m is None:
|
||||
raise ValueError(f"no spacing in {part!r}")
|
||||
out.append(int(m.group(1) or 1) * unit[m.group(2)])
|
||||
return out
|
||||
|
||||
|
||||
def _slot(row, name):
|
||||
"""The number in ONE NAMED SLOT of a rendered row, or `_Raised`.
|
||||
|
||||
@@ -4140,18 +4153,18 @@ def _slot(row, name):
|
||||
if m is None:
|
||||
raise ValueError(f"no {name!r} slot in {row[:90]!r}")
|
||||
raw = m.group(1)
|
||||
return ([int(x) for x in raw.split("+")] if name == "spacings"
|
||||
else int(raw))
|
||||
return (_spacing_s(raw) if name == "spacings" else int(raw))
|
||||
return derived(_get)
|
||||
|
||||
|
||||
_T11_GOOD = ("x. Price plan from 7 s ago, for 48 hours ahead: day-ahead "
|
||||
"prices from the price feed, 132 prices over 33 hours, "
|
||||
"36 published and 96 estimated, 900+3600 s apart")
|
||||
"36 published and 96 estimated, one every 15 minutes or every "
|
||||
"hour")
|
||||
_T11_SHIPPED_DEFECT = ("x. Price plan from 7 s ago, for 48 hours ahead: "
|
||||
"day-ahead prices from the price feed, 132 prices over "
|
||||
"132 hours, 96 published and 36 estimated, 900+3600 s "
|
||||
"apart")
|
||||
"132 hours, 96 published and 36 estimated, one every "
|
||||
"15 minutes or every hour")
|
||||
# The slots divide into the ones a *Tariff*/*Arbitrage* ROW carries and the
|
||||
# ones a REFUSAL sentence carries; each group is calibrated against its own
|
||||
# well-formed example, because a slot asked of the wrong kind of sentence is
|
||||
|
||||
Reference in New Issue
Block a user