loopable / platform /harness /windows.py
fsanyoto's picture
Deploy AIOS web (React glide grid + FastAPI slice)
bf8519f verified
Raw
History Blame Contribute Delete
17.2 kB
"""harness/windows.py β€” the DATE WINDOW vocabulary (CG-7, owner item 2, 2026-07-26).
A filter condition on a measure reads:
Where [Sales] [in the last 90 days] [>] [5,000]
The middle bracket is this module. It turns a window SPEC β€” a small JSON object a saved view can
persist β€” into a concrete `(date_from, date_to)` pair of ISO dates.
WHY IT IS ITS OWN MODULE, AND WHY IT TAKES `today` AS AN ARGUMENT
-----------------------------------------------------------------------------------------------
Two engines must agree on what "this quarter" means: this one (which compiles the SQL) and
`customer-grid/windows.ts` (which renders the label the user reads and the bounds the client
engine would use). If they disagree by one day, the grid shows a number the label denies, and
nothing errors. `aios-web/verify_windows.py` holds them in lock-step over a fixed set of probe
dates, exactly as verify_filter_engine.py does for the filter tree.
`today` is a PARAMETER, never `date.today()` read inside. Three reasons, all learned:
- a gate cannot compare two engines on "now" β€” the two runs are milliseconds apart and land on
different sides of midnight roughly once every few thousand runs, which is the worst kind of
flake because it looks like a real divergence
- a window resolved during a request must not shift between the count query and the row query
- the tenant's day boundary is the tenant's, not the server's
DESIGN NOTES THAT ARE EASY TO GET WRONG
-----------------------------------------------------------------------------------------------
- Bounds are INCLUSIVE at both ends and are DATES, not timestamps. `store_query` casts both
sides to TIMESTAMP and appends 23:59:59 to the upper bound, so a bare date is correct there;
the client compares the 10-char ISO prefix, so a bare date is correct there too.
- "Last N days" INCLUDES today. Airtable's "the past week" means the last 7 days up to and
including now, not the 7 days before yesterday. Off-by-one here silently drops today's
orders from every "recent" filter β€” invisible until someone asks why a sale they just
entered is missing.
- Week starts MONDAY (ISO 8601). Stated rather than defaulted: Python's weekday() is
Monday=0 and JavaScript's getDay() is Sunday=0, so the two engines disagree unless one of
them is explicitly corrected. That correction is the single most likely divergence in this
file, and the gate probes a Sunday and a Monday for exactly that reason.
- A `custom` window with only one bound is legal and means open-ended on the other side.
- An UNRESOLVABLE window returns None rather than a default. A window that quietly becomes
"all time" would widen a filter while still reporting an authoritative count.
"""
import datetime as _dt
#: Every window kind the UI may offer. Mirrors WINDOW_KINDS in customer-grid/windows.ts;
#: verify_windows.py asserts the two lists are identical, because a kind the client can emit and
#: the server cannot resolve is a filter that silently stops narrowing.
WINDOW_KINDS = (
"all_time",
"today",
"yesterday",
"this_week",
"last_week",
"this_month",
"last_month",
"this_quarter",
"last_quarter",
"this_year",
"last_year",
"ytd",
"ytd_last_year",
"ltm",
"past_week",
"past_month",
"past_year",
"last_n_days",
"next_n_days",
"custom",
)
#: Kinds that carry an integer `n`.
N_KINDS = frozenset({"last_n_days", "next_n_days"})
#: How each kind reads in a sentence. The UI renders these; they are here so the label and the
#: arithmetic cannot drift apart in the one place a user would never think to check.
WINDOW_LABELS = {
"all_time": "all time",
"today": "today",
"yesterday": "yesterday",
"this_week": "this week",
"last_week": "last week",
"this_month": "this month",
"last_month": "last month",
"this_quarter": "this quarter",
"last_quarter": "last quarter",
"this_year": "this year",
"last_year": "last year",
"ytd": "year to date",
# "last year to date" (LYTD), not "year to date, last year": the longer phrasing CLIPPED to
# "year to date, last ye" in the 146px window select β€” and this vocabulary is CLOSED, so
# the rule for it is that it stays readable rather than that it fits. Seen in a live
# screenshot of the owner's own comparison; every assertion in that run was green.
"ytd_last_year": "last year to date",
"ltm": "the last 12 months",
"past_week": "the past week",
"past_month": "the past month",
"past_year": "the past year",
"last_n_days": "the last {n} days",
"next_n_days": "the next {n} days",
"custom": "a custom range",
}
MAX_N = 3650 # ten years; a bound, not a business rule
# --- the DATE-VALUE anchors (owner item 3, 2026-07-26) ----------------------------------------
# The second half of a date CONDITION, as distinct from a measure's window:
#
# Where [Last order] [is before] [one month ago]
# ^ op ^ THIS
#
# A window answers "over what period do I sum"; an anchor answers "which single date am I
# comparing against". Both live in this module for one reason: they are the same two-engine
# contract, resolved from the same `today`, and a gate that holds one in lock-step and not the
# other would leave half the sentence free to drift.
#: Anchor modes, in the order the picker offers them. `exact` is the historical behaviour β€” a
#: rule with NO mode is an `exact` rule whose value is an ISO date, which is what every view
#: saved before this change carries.
ANCHOR_MODES = (
"today",
"yesterday",
"one_week_ago",
"one_month_ago",
"n_days_ago",
"exact",
)
#: Modes that carry NO value. ⚠ This set is load-bearing far outside this module: a rule whose
#: value is blank normally reads as INACTIVE, and an inactive rule is IGNORED β€” which WIDENS the
#: result under a count nobody would doubt. Every activeness check (`isRuleActive` in TS,
#: `filter_sql.is_rule_active`, and the validator's value handling) has to know these four are
#: active with an empty value.
ANCHOR_VALUE_FREE = frozenset({"today", "yesterday", "one_week_ago", "one_month_ago"})
ANCHOR_LABELS = {
"today": "today",
"yesterday": "yesterday",
"one_week_ago": "one week ago",
"one_month_ago": "one month ago",
"n_days_ago": "{n} days ago",
"exact": "an exact date",
}
def _iso(d):
return d.isoformat()
def _month_start(d):
return d.replace(day=1)
def _month_end(d):
return _next_month(d.replace(day=1)) - _dt.timedelta(days=1)
def _next_month(d):
return (d.replace(day=28) + _dt.timedelta(days=4)).replace(day=1)
def _quarter_start(d):
return _dt.date(d.year, 3 * ((d.month - 1) // 3) + 1, 1)
def _days_in_month(y, m):
return (_dt.date(y + (m == 12), 1 if m == 12 else m + 1, 1) - _dt.timedelta(days=1)).day
def _shift_months(d, n):
"""`d` moved `n` months, CLAMPING the day to the target month's length.
Jan 31 back one month is Dec 31, but Mar 31 back one month is Feb 28 (or 29) β€” there is no
Feb 31 to land on. Both engines must clamp identically or "the past month" differs by up to
three days for a third of the calendar; the gate probes a 31st and a leap day for exactly it.
"""
total = (d.year * 12 + (d.month - 1)) + n
y, m = divmod(total, 12)
m += 1
return _dt.date(y, m, min(d.day, _days_in_month(y, m)))
def _parse_date(v):
"""Accept 'YYYY-MM-DD' (and tolerate a longer ISO timestamp by taking its date part)."""
if isinstance(v, _dt.date):
return v
s = str(v or "").strip()[:10]
if not s:
return None
try:
return _dt.date.fromisoformat(s)
except ValueError:
return None
def normalize(spec):
"""Coerce an untrusted window spec to `{kind, n?, from?, to?}` or None.
Fail-closed on the KIND (an unknown kind is not a window), tolerant on the rest β€” the same
split `clean_filter_tree` uses, so one malformed window cannot cost a user their saved view.
"""
if not isinstance(spec, dict):
return None
kind = spec.get("kind")
if kind not in WINDOW_KINDS:
return None
out = {"kind": kind}
if kind in N_KINDS:
try:
n = int(spec.get("n"))
except (TypeError, ValueError):
return None
if n < 1 or n > MAX_N:
return None
out["n"] = n
if kind == "custom":
f, t = _parse_date(spec.get("from")), _parse_date(spec.get("to"))
if f is None and t is None:
return None # a custom range with no bounds is not a window
if f is not None and t is not None and f > t:
f, t = t, f # the builder cannot enforce order; the engine can
if f is not None:
out["from"] = _iso(f)
if t is not None:
out["to"] = _iso(t)
return out
def resolve(spec, today):
"""Window spec + the tenant's today -> `(date_from, date_to)`, both inclusive ISO dates.
Either side may be None, meaning open-ended. Returns None when the spec is not a window at
all β€” callers MUST treat that as "this condition cannot be evaluated" and refuse, never as
"no window", which would silently widen the result to all time.
"""
spec = normalize(spec)
if spec is None:
return None
kind = spec["kind"]
d = _parse_date(today)
if d is None:
raise ValueError("resolve() needs an explicit `today` β€” see this module's docstring")
if kind == "all_time":
return (None, None)
if kind == "today":
return (_iso(d), _iso(d))
if kind == "yesterday":
y = d - _dt.timedelta(days=1)
return (_iso(y), _iso(y))
# ISO 8601: the week starts MONDAY. Python's weekday() is already Monday=0; the TS mirror
# has to correct getDay(), which is Sunday=0. The gate probes both a Sunday and a Monday.
if kind == "this_week":
start = d - _dt.timedelta(days=d.weekday())
return (_iso(start), _iso(start + _dt.timedelta(days=6)))
if kind == "last_week":
start = d - _dt.timedelta(days=d.weekday() + 7)
return (_iso(start), _iso(start + _dt.timedelta(days=6)))
if kind == "this_month":
return (_iso(_month_start(d)), _iso(_month_end(d)))
if kind == "last_month":
prev = _month_start(d) - _dt.timedelta(days=1)
return (_iso(_month_start(prev)), _iso(prev))
if kind == "this_quarter":
qs = _quarter_start(d)
qe = _month_end(_dt.date(qs.year, qs.month + 2, 1))
return (_iso(qs), _iso(qe))
if kind == "last_quarter":
prev_end = _quarter_start(d) - _dt.timedelta(days=1)
qs = _quarter_start(prev_end)
return (_iso(qs), _iso(prev_end))
if kind == "this_year":
return (_iso(_dt.date(d.year, 1, 1)), _iso(_dt.date(d.year, 12, 31)))
if kind == "last_year":
return (_iso(_dt.date(d.year - 1, 1, 1)), _iso(_dt.date(d.year - 1, 12, 31)))
if kind == "ytd":
# year-to-date ENDS TODAY, unlike this_year which runs to Dec 31. The distinction is the
# whole reason both exist: comparing "this year" against last year double-counts the
# months that have not happened yet.
return (_iso(_dt.date(d.year, 1, 1)), _iso(d))
if kind == "ytd_last_year":
# SAME PERIOD last year β€” Jan 1 LY through today's month/day LY. This is the honest
# partner of `ytd`, and it is not a nicety: it mirrors `core.periods.ytd_last_year`
# EXACTLY (including the Feb 29 -> Feb 28 clamp), which is what `pool()` computes the
# `revenue_ly` column from. `Sales[ytd] < Sales[ytd_last_year]` therefore reproduces the
# retired `at_risk > 0` condition rather than approximating it. Comparing `ytd` against
# `last_year` instead would pit seven months against twelve.
return (_iso(_dt.date(d.year - 1, 1, 1)), _iso(_shift_months(d, -12)))
if kind == "ltm":
# the last twelve months INCLUDING today: 365 days back, both ends inclusive
return (_iso(d - _dt.timedelta(days=364)), _iso(d))
# "The past week/month/year" are ROLLING and end TODAY β€” Airtable's wording, and NOT the
# calendar kinds above: `last_week` is the previous Monday-Sunday, `past_week` is the seven
# days ending today. Both are offered because a user means different things by them, and
# picking one to serve both would silently answer the other question.
# past_week = the same rule as last_n_days(7) β€” one period back, PLUS ONE DAY, so today
# is included and the span is exactly 7 days rather than 8.
# past_year agrees with `ltm` except across a leap day, where the calendar shift keeps the
# same month/day and the 364-day subtraction cannot.
if kind == "past_week":
return (_iso(d - _dt.timedelta(days=6)), _iso(d))
if kind == "past_month":
return (_iso(_shift_months(d, -1) + _dt.timedelta(days=1)), _iso(d))
if kind == "past_year":
return (_iso(_shift_months(d, -12) + _dt.timedelta(days=1)), _iso(d))
if kind == "last_n_days":
# INCLUSIVE of today β€” "the last 7 days" is today and the 6 before it, not the 7 before
# yesterday. Getting this wrong drops today's orders from every recent-activity filter.
return (_iso(d - _dt.timedelta(days=spec["n"] - 1)), _iso(d))
if kind == "next_n_days":
return (_iso(d), _iso(d + _dt.timedelta(days=spec["n"] - 1)))
if kind == "custom":
return (spec.get("from"), spec.get("to"))
raise AssertionError(f"unhandled window kind {kind!r}") # unreachable; WINDOW_KINDS is closed
def label(spec):
"""How a window reads in the condition sentence ('in the last 90 days')."""
spec = normalize(spec)
if spec is None:
return "an invalid range"
return WINDOW_LABELS[spec["kind"]].format(n=spec.get("n"))
def resolve_anchor(mode, value, today):
"""A date condition's right-hand side -> ONE inclusive ISO date, or None.
`mode` is an entry of ANCHOR_MODES; None/'' means `exact`, which is what every view saved
before anchors existed carries (op + an ISO date in `value`). `value` supplies the number for
`n_days_ago` and the date for `exact`, and is ignored by the four value-free modes.
Returns None when the anchor cannot be resolved β€” an unknown mode, a non-numeric N, a value
that is not a date. The caller must then treat the CONDITION as unanswerable and match
nothing. It must NOT fall through to "no condition": that widens the result while the count
beside it still looks authoritative, which is the whole reason this returns None rather than
a best guess (same contract as `resolve()` above).
`n_days_ago` accepts n = 0 where a WINDOW requires n >= 1. A zero-day window is empty and
could only be a mistake; "0 days ago" is today, which is a date a person can mean.
"""
m = mode or "exact"
if m not in ANCHOR_MODES:
return None
d = _parse_date(today)
if d is None:
raise ValueError("resolve_anchor() needs an explicit `today` β€” see this module's "
"docstring on why the clock is never read here")
if m == "today":
return _iso(d)
if m == "yesterday":
return _iso(d - _dt.timedelta(days=1))
if m == "one_week_ago":
# A week ago is a DATE (today minus 7), not the past-week RANGE. The two read almost
# identically in English and mean different things in a comparison.
return _iso(d - _dt.timedelta(days=7))
if m == "one_month_ago":
return _iso(_shift_months(d, -1))
if m == "n_days_ago":
s = str("" if value is None else value).strip()
# `[0-9]` not `\d`: a Python \d matches every Unicode decimal digit, so a fullwidth
# '30' would resolve here and be rejected by the TS mirror's Number() β€” the exact
# two-engine divergence filter_sql.to_num was rewritten to avoid.
if not s or not all("0" <= ch <= "9" for ch in s):
return None
n = int(s)
# No `n < 0` guard: the digit test above already excludes a sign, so it would be
# unreachable. The negative control proved that β€” removing it changed nothing, which is
# how dead code hides in a defensive-looking line. The CAP is the live guard.
if n > MAX_N:
return None
return _iso(d - _dt.timedelta(days=n))
parsed = _parse_date(value) # exact
return None if parsed is None else _iso(parsed)
def anchor_label(mode, value):
"""How an anchor reads in the condition sentence ('one month ago', '30 days ago')."""
m = mode or "exact"
if m not in ANCHOR_MODES:
return "an invalid date"
if m == "n_days_ago":
return ANCHOR_LABELS[m].format(n=str("" if value is None else value).strip() or "N")
if m == "exact":
return str("" if value is None else value).strip() or ANCHOR_LABELS[m]
return ANCHOR_LABELS[m]