v0.7.0 — generalized shift splitting (--split-by shifts) - #3
Merged
Conversation
…/occurrence/comparison Add the core generalized shift-splitting model to analysis.py: - Shift / ShiftSet with parse(), from_spec(), default(), HH:MM validation, duplicate-name + zero-length-window rejection, and wrap-past-midnight windows (end <= start). - ShiftSet.coverage_issues() warns on gaps/overlaps in the 24h tiling. - gather_store(store, indices): slice_store sibling over non-contiguous index lists (a named shift recurs daily). - aggregate_shifts(): group record indices by shift NAME, evaluated in the REPORT timezone (localize before applying HH:MM rule). First matching window wins; non-matching records -> 'unassigned'. - shift_occurrences(): contiguous per-instance buckets; a midnight-spanning night is ONE occurrence labeled by its start date. - shift_comparison_rows() + _shift_row(): the headline per-shift aggregate (records, hours, kWh, P avg/min/max, peak rolling demand, PF avg, V_LN avg/p5/p95, V_THD p95, event counts, outage minutes). 20 new tests cover parsing, wrap logic, tz-localized assignment, non-contiguous aggregation, gap/overlap/unassigned, occurrences (incl. midnight span), and comparison-row schema/values. Full suite: 243 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New shifts_file.py loader (JSON {shifts:[{name,start,end}]} or bare list),
mirroring the --rules-file pattern.
- CLI: --split-by shifts routes to _run_shifts; --shifts SPEC (inline) and
--shifts-file FILE; default day=06:00-18:00,night=18:00-06:00 when neither
given. Windows evaluated in --tz (UTC if unset; printed in output).
- _run_shifts emits the two required outputs:
1. shift_comparison.csv + .json (headline per-shift aggregate;
tz/spec/coverage_issues/demand_window in the JSON header),
2. per-occurrence contiguous buckets under <out>/shifts/<name_date>/
(session.csv, events.json+ITIC, summary.txt), reusing per-bucket
machinery; midnight-spanning occurrences are one bucket by start date.
- Coverage gap/overlap warnings printed to stderr.
- Shift-comparison table embedded in summary.txt.
Validated read-only on the real ES.004 session with --tz America/Chicago:
day/night split lands exactly on 06:00/18:00 Central (43200 rec each per day).
Tests: 6 CLI E2E (default, boundary-cross, tz-central, shifts-file, 3-shift,
summary-table) + 3 shifts_file loader. Full suite: 251 passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Port the shift model to web/analysis.js: Shift, ShiftSet (parse/fromSpec/ default/coverageIssues), localMinuteOfDay (Intl-based, matching tzutil), aggregateShifts, shiftOccurrences, shiftComparisonRows. tz contract mirrors Python: minute-of-day is localized to the report tz BEFORE the HH:MM rule. - Extend the golden generator (test_analysis_parity_golden.py) with a deterministic 3-day minute-resolution session: emit comparison_utc, occurrences_utc, and a tz-localized A/B/C-in-America/Chicago comparison. - New web/tests/shifts_parity.test.js recreates that exact session and asserts aggregate grouping, occurrences row-for-row, comparison rows (UTC + tz), parse-error handling, and coverage gap/overlap — within float tolerance. - Fix: shiftComparisonRows passed an already-wrapped source into aggregateShifts; pass the original source so asColumnSource resolves once. Tests: py 251 passed; web 120 passed (6 new parity subtests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… bump - New docs/SHIFTS.md (format, 2-shift + 3-shift examples, tz contract, output schema, parity note). - README options table: --split-by gains 'shifts'; add --shifts / --shifts-file. - CHANGELOG 0.7.0 entry; ROADMAP 'Shipped in v0.7' section (shift slice of the comparison-polish theme). - Bump version 0.6.0 -> 0.7.0 (pyproject.toml + __init__.py). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Generalized, named, configurable shift periods
Lets users define multiple named time-of-day windows (that may wrap midnight) and compare power usage across them — e.g. day vs night, or A/B/C shifts.
--split-by shifts+--shifts "day=06:00-18:00,night=18:00-06:00"(any count;end<=startwraps midnight) or--shifts-file FILE(JSON). Defaults to day/night.--tz(UTC fallback, stated in output). Gap/overlap warnings; first-match wins; non-matching →unassigned.shift_comparison.{csv,json}(per shift: kWh, P avg/min/max, peak demand, PF, V L-N + p5/p95, V THD p95, outage/dip/swell counts, outage minutes) + per-occurrence buckets (a midnight-spanning night = one occurrence by start date) + a table insummary.txt.Tests: Python 251 · Web 120, green. Validated on the real ES.004 session (day/night land on 06:00/18:00 Central — no UTC skew).
🤖 Generated with Claude Code