feat: add PlanningDurationService for estimating planning duration
- Implemented PlanningDurationService to provide an indicative wait time before calculation based on user input and calendar settings. - Introduced duration estimation logic that considers horizon dates, balances, and rules without triggering a calculation. - Enhanced user input component to display estimated duration using the new service. - Updated translation files to include new duration estimate strings in multiple languages. - Added tests for PlanningDurationService to ensure accurate duration estimation and calibration. - Refactored leave planner service to remove hardcoded deadlines and improve worker responsiveness.
This commit is contained in:
1 parent
2edf2004f5
commit
bc8570d8ca
29 files changed
+1064
-123
No files matched your search
@@ -0,0 +1,109 @@
|
||||
# Indicative wait before Calculate
|
||||
|
||||
The small range beside **Calculate** describes the configuration currently in
|
||||
the form. Editing dates, balances or rules updates it without requesting a plan
|
||||
or changing the previously displayed calendar. It is an approximation, with no
|
||||
accuracy guarantee or automatic stopping deadline.
|
||||
|
||||
## Initial estimate
|
||||
|
||||
`PlanningDurationService` shares the date-only request with `CalendarYear` so
|
||||
the estimate and calculation use the same horizon, balances and rules. Invalid
|
||||
horizon dates hide the estimate and retain the existing disabled Calculate
|
||||
control. Other malformed inputs keep a rough fallback estimate; the planner
|
||||
still performs its own validation.
|
||||
|
||||
The initial model is a deliberately rough engineering prior grounded in the
|
||||
[browser reference measurements](benchmark/README.md): the 2026-10-03 annual
|
||||
30-day scenario returned its outcome in about 0.6 seconds, and the 2026-10-05
|
||||
two- and three-year calendar runs with 30 days per year took 5.08 and 10.86
|
||||
seconds. These are throttled desktop measurements, not phone measurements or a
|
||||
validated prediction model. The annual outcome timing does not include final
|
||||
calendar rendering. The new rendered-calendar measurements below supplement
|
||||
those references.
|
||||
|
||||
For a half-open horizon of `D` calendar days, the model uses:
|
||||
|
||||
```text
|
||||
initial_ms = 300 + 600 × (D / 365.25)^1.2 × (max(1, P) / 30)^1.5 × R
|
||||
```
|
||||
|
||||
`P` approximates placement load. Each non-negative finite balance contributes
|
||||
at most five sevenths of its calendar-day overlap with the horizon, availability
|
||||
and inclusive expiry. An at-risk balance has weight 1; a deferred balance has
|
||||
weight 0.25 to account for possible supporting placements. Already expired and
|
||||
not-yet-available balances contribute nothing. Each enabled IF_THEN rule adds
|
||||
its non-negative earned quantity times `D / 365.25` as a rough allowance.
|
||||
|
||||
`R` starts at 1. Each enabled rule adds `max(1, placeDays / 3)` times 0.5 for an
|
||||
obligation or 0.15 for a preference. TIME_WINDOW rules wholly outside the
|
||||
horizon contribute nothing. These weights approximate extra search, rather than
|
||||
solving constraints or counting holiday occurrences in the form. Typed
|
||||
obligations, date distribution, interacting rules, earned entitlements, device
|
||||
load and browser differences can all change the actual wait substantially.
|
||||
|
||||
The powers and weights are implementation choices, not complexity guarantees
|
||||
or fitted accuracy claims. With 30 available days per year, the model's search
|
||||
term grows as the number of years to the power 2.7, rather than assuming a
|
||||
linear increase. Horizons beyond the references are unvalidated extrapolation.
|
||||
The estimate performs one pass over balances and rules and runs no planner.
|
||||
|
||||
## Local refinement and precision
|
||||
|
||||
After each successfully rendered `Plan`, the service retains the ratio of its
|
||||
observed elapsed time to the initial model for that calculation's immutable
|
||||
submitted snapshot. The median of the most recent eight ratios scales the
|
||||
initial estimate for the current form. A changed form therefore does not
|
||||
relabel the completed measurement as a calculation of the new configuration.
|
||||
The median limits the effect of isolated unusually slow runs; it cannot remove
|
||||
systematic model error or predict arbitrary obligation searches.
|
||||
|
||||
Only eight numeric ratios are retained in memory for the current application
|
||||
session. Reloading restores the initial estimate. Nothing is persisted or sent
|
||||
to a remote service, so there is no saved-measurement restoration dependency.
|
||||
Storage failure does not affect this service.
|
||||
|
||||
The displayed lower bound is half the estimated duration, rounded down; the
|
||||
upper bound is three times that duration, rounded up. Values are whole seconds,
|
||||
or whole minutes when the lower bound is at least one minute. The minimum
|
||||
displayed value is 1 and the upper bound is at least one unit higher. Internal
|
||||
duration rounding to whole milliseconds prevents floating-point noise at unit
|
||||
boundaries. Very fast runs can finish before the displayed lower bound. The
|
||||
range is explicitly indicative, not a confidence interval or a promise.
|
||||
|
||||
## What is timed
|
||||
|
||||
Manual requests capture `performance.now()` inside
|
||||
`UserInputService.requestComputation`, before updating the calculation trigger.
|
||||
The existing initial calculation falls back to the start of `startCalculation`.
|
||||
The endpoint is Angular's `afterNextRender` callback after the complete plan has
|
||||
been projected into the calendar, its months and placements have been rendered,
|
||||
and the loading overlay has been removed. This measures request scheduling,
|
||||
calendar preparation, worker startup, search, trace capture that delays the
|
||||
outcome, outcome transport, projection and Angular rendering. It is a DOM
|
||||
readiness endpoint, rather than a claim to measure a physical screen's paint.
|
||||
|
||||
Neither core `planningWorkMs` nor artifact timings supply the calibration
|
||||
measurement. The callback does not wait for the explanation or trace promises;
|
||||
later delivery, navigation and trace errors never add a second measurement.
|
||||
Cancellation (including a retained incumbent), incomplete outcomes, invalid
|
||||
inputs, conflicts and calculation errors do not calibrate successful-plan
|
||||
waiting time. Superseded calculations and callbacks for destroyed calendars
|
||||
are ignored. Zero, negative and non-finite elapsed values are discarded.
|
||||
|
||||
Exceeding the range has no side effects. The estimate supplies no deadline,
|
||||
timer, date adjustment or new calculation. **Cancel** remains explicit.
|
||||
|
||||
## Verification
|
||||
|
||||
Controlled-clock tests at the existing calendar and input seams cover initial
|
||||
estimation, unsubmitted edits, minute formatting, local refinement, request
|
||||
scheduling, a twenty-second calculation exceeding its initial range, a trace
|
||||
that fails five minutes after calendar readiness, cancellation with and without
|
||||
an incumbent, incomplete/error outcomes and superseded calculations. No timing
|
||||
assertion depends on CI hardware speed. Existing settings, planner and worker
|
||||
tests cover saved horizons, long calculations and original interrupted traces.
|
||||
|
||||
Rendered annual, two-year and three-year references, with the actual displayed
|
||||
range and separate later trace wait, are recorded in
|
||||
[the benchmark report](benchmark/README.md).
|
||||
Reference in new issue
Block a user