dabke 0.82.0 → 0.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +22 -24
- package/dist/client.d.ts +39 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/client.schemas.d.ts +118 -18
- package/dist/client.schemas.d.ts.map +1 -1
- package/dist/client.schemas.js +110 -6
- package/dist/client.schemas.js.map +1 -1
- package/dist/client.types.d.ts +63 -7
- package/dist/client.types.d.ts.map +1 -1
- package/dist/client.types.js.map +1 -1
- package/dist/cpsat/cost.d.ts +5 -2
- package/dist/cpsat/cost.d.ts.map +1 -1
- package/dist/cpsat/cost.js +13 -6
- package/dist/cpsat/cost.js.map +1 -1
- package/dist/cpsat/model-builder.d.ts +42 -61
- package/dist/cpsat/model-builder.d.ts.map +1 -1
- package/dist/cpsat/model-builder.js +451 -56
- package/dist/cpsat/model-builder.js.map +1 -1
- package/dist/cpsat/response.d.ts +19 -7
- package/dist/cpsat/response.d.ts.map +1 -1
- package/dist/cpsat/response.js +48 -9
- package/dist/cpsat/response.js.map +1 -1
- package/dist/cpsat/rule-descriptor.d.ts +250 -0
- package/dist/cpsat/rule-descriptor.d.ts.map +1 -0
- package/dist/cpsat/rule-descriptor.js +51 -0
- package/dist/cpsat/rule-descriptor.js.map +1 -0
- package/dist/cpsat/rules/artifacts.d.ts +9 -0
- package/dist/cpsat/rules/artifacts.d.ts.map +1 -0
- package/dist/cpsat/rules/artifacts.js +31 -0
- package/dist/cpsat/rules/artifacts.js.map +1 -0
- package/dist/cpsat/rules/assign-together.d.ts +18 -20
- package/dist/cpsat/rules/assign-together.d.ts.map +1 -1
- package/dist/cpsat/rules/assign-together.js +90 -54
- package/dist/cpsat/rules/assign-together.js.map +1 -1
- package/dist/cpsat/rules/assigned-day.d.ts +12 -0
- package/dist/cpsat/rules/assigned-day.d.ts.map +1 -0
- package/dist/cpsat/rules/assigned-day.js +43 -0
- package/dist/cpsat/rules/assigned-day.js.map +1 -0
- package/dist/cpsat/rules/assignment-priority.d.ts +15 -29
- package/dist/cpsat/rules/assignment-priority.d.ts.map +1 -1
- package/dist/cpsat/rules/assignment-priority.js +47 -50
- package/dist/cpsat/rules/assignment-priority.js.map +1 -1
- package/dist/cpsat/rules/assignment-terms.d.ts +6 -0
- package/dist/cpsat/rules/assignment-terms.d.ts.map +1 -0
- package/dist/cpsat/rules/assignment-terms.js +19 -0
- package/dist/cpsat/rules/assignment-terms.js.map +1 -0
- package/dist/cpsat/rules/concurrent-intervals.d.ts +19 -0
- package/dist/cpsat/rules/concurrent-intervals.d.ts.map +1 -0
- package/dist/cpsat/rules/concurrent-intervals.js +57 -0
- package/dist/cpsat/rules/concurrent-intervals.js.map +1 -0
- package/dist/cpsat/rules/cost-utils.d.ts +1 -3
- package/dist/cpsat/rules/cost-utils.d.ts.map +1 -1
- package/dist/cpsat/rules/cost-utils.js +0 -7
- package/dist/cpsat/rules/cost-utils.js.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.d.ts +10 -10
- package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.js +88 -77
- package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.d.ts +10 -10
- package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.js +76 -65
- package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/index.d.ts +30 -21
- package/dist/cpsat/rules/index.d.ts.map +1 -1
- package/dist/cpsat/rules/index.js +30 -21
- package/dist/cpsat/rules/index.js.map +1 -1
- package/dist/cpsat/rules/location-preference.d.ts +14 -21
- package/dist/cpsat/rules/location-preference.d.ts.map +1 -1
- package/dist/cpsat/rules/location-preference.js +45 -45
- package/dist/cpsat/rules/location-preference.js.map +1 -1
- package/dist/cpsat/rules/max-concurrent-assignments.d.ts +46 -0
- package/dist/cpsat/rules/max-concurrent-assignments.d.ts.map +1 -0
- package/dist/cpsat/rules/max-concurrent-assignments.js +121 -0
- package/dist/cpsat/rules/max-concurrent-assignments.js.map +1 -0
- package/dist/cpsat/rules/max-consecutive-days.d.ts +18 -17
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.js +67 -66
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/max-days-of-week-per-period.d.ts +45 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.js +95 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.js.map +1 -0
- package/dist/cpsat/rules/max-days-week.d.ts +30 -0
- package/dist/cpsat/rules/max-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-week.js +83 -0
- package/dist/cpsat/rules/max-days-week.js.map +1 -0
- package/dist/cpsat/rules/max-hours-day.d.ts +14 -31
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-day.js +56 -72
- package/dist/cpsat/rules/max-hours-day.js.map +1 -1
- package/dist/cpsat/rules/max-hours-week.d.ts +15 -31
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-week.js +60 -75
- package/dist/cpsat/rules/max-hours-week.js.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.d.ts +17 -33
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.js +57 -73
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.d.ts +18 -18
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.js +123 -94
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/min-days-of-week-per-period.d.ts +35 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.js +108 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.js.map +1 -0
- package/dist/cpsat/rules/min-days-week.d.ts +21 -0
- package/dist/cpsat/rules/min-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-week.js +95 -0
- package/dist/cpsat/rules/min-days-week.js.map +1 -0
- package/dist/cpsat/rules/min-hours-day.d.ts +14 -17
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-day.js +72 -54
- package/dist/cpsat/rules/min-hours-day.js.map +1 -1
- package/dist/cpsat/rules/min-hours-week.d.ts +15 -18
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-week.js +75 -57
- package/dist/cpsat/rules/min-hours-week.js.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts +14 -17
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.js +148 -138
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
- package/dist/cpsat/rules/minimize-cost.d.ts +2 -18
- package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
- package/dist/cpsat/rules/minimize-cost.js +151 -163
- package/dist/cpsat/rules/minimize-cost.js.map +1 -1
- package/dist/cpsat/rules/must-assign.d.ts +12 -0
- package/dist/cpsat/rules/must-assign.d.ts.map +1 -0
- package/dist/cpsat/rules/must-assign.js +54 -0
- package/dist/cpsat/rules/must-assign.js.map +1 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts +6 -10
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.js +118 -112
- package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts +6 -11
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.js +104 -99
- package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +9 -21
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.js +181 -199
- package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts +13 -12
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.js +143 -143
- package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts +7 -11
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.js +124 -119
- package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
- package/dist/cpsat/rules/pattern-eligibility.d.ts +6 -0
- package/dist/cpsat/rules/pattern-eligibility.d.ts.map +1 -0
- package/dist/cpsat/rules/pattern-eligibility.js +16 -0
- package/dist/cpsat/rules/pattern-eligibility.js.map +1 -0
- package/dist/cpsat/rules/pattern-time.d.ts +5 -0
- package/dist/cpsat/rules/pattern-time.d.ts.map +1 -0
- package/dist/cpsat/rules/pattern-time.js +20 -0
- package/dist/cpsat/rules/pattern-time.js.map +1 -0
- package/dist/cpsat/rules/registry.d.ts +217 -27
- package/dist/cpsat/rules/registry.d.ts.map +1 -1
- package/dist/cpsat/rules/registry.js +98 -31
- package/dist/cpsat/rules/registry.js.map +1 -1
- package/dist/cpsat/rules/resolver.d.ts +9 -7
- package/dist/cpsat/rules/resolver.d.ts.map +1 -1
- package/dist/cpsat/rules/resolver.js +19 -18
- package/dist/cpsat/rules/resolver.js.map +1 -1
- package/dist/cpsat/rules/role-preference.d.ts +24 -0
- package/dist/cpsat/rules/role-preference.d.ts.map +1 -0
- package/dist/cpsat/rules/role-preference.js +72 -0
- package/dist/cpsat/rules/role-preference.js.map +1 -0
- package/dist/cpsat/rules/rules.types.d.ts +93 -17
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.d.ts +25 -12
- package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.js +14 -19
- package/dist/cpsat/rules/scope.types.js.map +1 -1
- package/dist/cpsat/rules/target-days-week.d.ts +31 -0
- package/dist/cpsat/rules/target-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/target-days-week.js +80 -0
- package/dist/cpsat/rules/target-days-week.js.map +1 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts +33 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts.map +1 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.js +146 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.js.map +1 -0
- package/dist/cpsat/rules/time-cost-surcharge.d.ts +15 -14
- package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.js +82 -96
- package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/time-off.d.ts +21 -57
- package/dist/cpsat/rules/time-off.d.ts.map +1 -1
- package/dist/cpsat/rules/time-off.js +114 -127
- package/dist/cpsat/rules/time-off.js.map +1 -1
- package/dist/cpsat/rules/variables.d.ts +4 -0
- package/dist/cpsat/rules/variables.d.ts.map +1 -0
- package/dist/cpsat/rules/variables.js +10 -0
- package/dist/cpsat/rules/variables.js.map +1 -0
- package/dist/cpsat/rules.d.ts +20 -3
- package/dist/cpsat/rules.d.ts.map +1 -1
- package/dist/cpsat/rules.js +19 -2
- package/dist/cpsat/rules.js.map +1 -1
- package/dist/cpsat/semantic-time.d.ts +7 -6
- package/dist/cpsat/semantic-time.d.ts.map +1 -1
- package/dist/cpsat/semantic-time.js +15 -28
- package/dist/cpsat/semantic-time.js.map +1 -1
- package/dist/cpsat/types.d.ts +47 -4
- package/dist/cpsat/types.d.ts.map +1 -1
- package/dist/cpsat/types.js +24 -1
- package/dist/cpsat/types.js.map +1 -1
- package/dist/cpsat/utils.d.ts +23 -6
- package/dist/cpsat/utils.d.ts.map +1 -1
- package/dist/cpsat/utils.js +76 -15
- package/dist/cpsat/utils.js.map +1 -1
- package/dist/cpsat/validation-reporter.d.ts +6 -2
- package/dist/cpsat/validation-reporter.d.ts.map +1 -1
- package/dist/cpsat/validation-reporter.js +122 -10
- package/dist/cpsat/validation-reporter.js.map +1 -1
- package/dist/cpsat/validation.types.d.ts +21 -10
- package/dist/cpsat/validation.types.d.ts.map +1 -1
- package/dist/datetime.utils.d.ts +7 -13
- package/dist/datetime.utils.d.ts.map +1 -1
- package/dist/datetime.utils.js +29 -36
- package/dist/datetime.utils.js.map +1 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/dist/schedule/cost.d.ts +192 -0
- package/dist/schedule/cost.d.ts.map +1 -0
- package/dist/schedule/cost.js +151 -0
- package/dist/schedule/cost.js.map +1 -0
- package/dist/schedule/coverage.d.ts +114 -0
- package/dist/schedule/coverage.d.ts.map +1 -0
- package/dist/schedule/coverage.js +56 -0
- package/dist/schedule/coverage.js.map +1 -0
- package/dist/schedule/definition.d.ts +346 -0
- package/dist/schedule/definition.d.ts.map +1 -0
- package/dist/schedule/definition.js +668 -0
- package/dist/schedule/definition.js.map +1 -0
- package/dist/schedule/index.d.ts +67 -0
- package/dist/schedule/index.d.ts.map +1 -0
- package/dist/schedule/index.js +69 -0
- package/dist/schedule/index.js.map +1 -0
- package/dist/schedule/rules.d.ts +678 -0
- package/dist/schedule/rules.d.ts.map +1 -0
- package/dist/schedule/rules.js +554 -0
- package/dist/schedule/rules.js.map +1 -0
- package/dist/schedule/shift-patterns.d.ts +17 -0
- package/dist/schedule/shift-patterns.d.ts.map +1 -0
- package/dist/schedule/shift-patterns.js +24 -0
- package/dist/schedule/shift-patterns.js.map +1 -0
- package/dist/schedule/time-periods.d.ts +79 -0
- package/dist/schedule/time-periods.d.ts.map +1 -0
- package/dist/schedule/time-periods.js +101 -0
- package/dist/schedule/time-periods.js.map +1 -0
- package/dist/types.d.ts +67 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +64 -0
- package/dist/types.js.map +1 -1
- package/package.json +8 -14
- package/solver/src/solver/models.py +41 -4
- package/solver/src/solver/solver.py +546 -157
- package/solver/tests/test_solver.py +363 -7
- package/src/client.schemas.ts +127 -10
- package/src/client.ts +3 -0
- package/src/client.types.ts +73 -7
- package/src/cpsat/cost.ts +17 -7
- package/src/cpsat/model-builder.ts +614 -129
- package/src/cpsat/response.ts +57 -13
- package/src/cpsat/rule-descriptor.ts +311 -0
- package/src/cpsat/rules/artifacts.ts +56 -0
- package/src/cpsat/rules/assign-together.ts +104 -69
- package/src/cpsat/rules/assigned-day.ts +70 -0
- package/src/cpsat/rules/assignment-priority.ts +54 -58
- package/src/cpsat/rules/assignment-terms.ts +43 -0
- package/src/cpsat/rules/concurrent-intervals.ts +93 -0
- package/src/cpsat/rules/cost-utils.ts +1 -9
- package/src/cpsat/rules/day-cost-multiplier.ts +101 -81
- package/src/cpsat/rules/day-cost-surcharge.ts +91 -70
- package/src/cpsat/rules/index.ts +30 -21
- package/src/cpsat/rules/location-preference.ts +47 -52
- package/src/cpsat/rules/max-concurrent-assignments.ts +170 -0
- package/src/cpsat/rules/max-consecutive-days.ts +74 -91
- package/src/cpsat/rules/max-days-of-week-per-period.ts +127 -0
- package/src/cpsat/rules/max-days-week.ts +117 -0
- package/src/cpsat/rules/max-hours-day.ts +64 -84
- package/src/cpsat/rules/max-hours-week.ts +74 -90
- package/src/cpsat/rules/max-shifts-day.ts +65 -85
- package/src/cpsat/rules/min-consecutive-days.ts +161 -127
- package/src/cpsat/rules/min-days-of-week-per-period.ts +144 -0
- package/src/cpsat/rules/min-days-week.ts +122 -0
- package/src/cpsat/rules/min-hours-day.ts +79 -58
- package/src/cpsat/rules/min-hours-week.ts +87 -62
- package/src/cpsat/rules/min-rest-between-shifts.ts +191 -178
- package/src/cpsat/rules/minimize-cost.ts +175 -188
- package/src/cpsat/rules/must-assign.ts +71 -0
- package/src/cpsat/rules/overtime-daily-multiplier.ts +158 -140
- package/src/cpsat/rules/overtime-daily-surcharge.ts +141 -126
- package/src/cpsat/rules/overtime-tiered-multiplier.ts +234 -253
- package/src/cpsat/rules/overtime-weekly-multiplier.ts +183 -175
- package/src/cpsat/rules/overtime-weekly-surcharge.ts +161 -150
- package/src/cpsat/rules/pattern-eligibility.ts +26 -0
- package/src/cpsat/rules/pattern-time.ts +30 -0
- package/src/cpsat/rules/registry.ts +140 -56
- package/src/cpsat/rules/resolver.ts +58 -30
- package/src/cpsat/rules/role-preference.ts +92 -0
- package/src/cpsat/rules/rules.types.ts +92 -17
- package/src/cpsat/rules/scope.types.ts +32 -27
- package/src/cpsat/rules/target-days-week.ts +105 -0
- package/src/cpsat/rules/target-peak-concurrent-assignments.ts +212 -0
- package/src/cpsat/rules/time-cost-surcharge.ts +116 -122
- package/src/cpsat/rules/time-off.ts +154 -163
- package/src/cpsat/rules/variables.ts +11 -0
- package/src/cpsat/rules.ts +43 -17
- package/src/cpsat/semantic-time.ts +29 -43
- package/src/cpsat/types.ts +53 -4
- package/src/cpsat/utils.ts +92 -20
- package/src/cpsat/validation-reporter.ts +170 -19
- package/src/cpsat/validation.types.ts +22 -11
- package/src/datetime.utils.ts +45 -44
- package/src/index.ts +72 -15
- package/src/schedule/cost.ts +221 -0
- package/src/schedule/coverage.ts +164 -0
- package/src/schedule/definition.ts +1447 -0
- package/src/schedule/index.ts +132 -0
- package/src/schedule/rules.ts +1028 -0
- package/src/schedule/shift-patterns.ts +29 -0
- package/src/schedule/time-periods.ts +120 -0
- package/src/types.ts +114 -2
- package/dist/llms.d.ts +0 -2
- package/dist/llms.d.ts.map +0 -1
- package/dist/llms.js +0 -3
- package/dist/llms.js.map +0 -1
- package/dist/schedule.d.ts +0 -917
- package/dist/schedule.d.ts.map +0 -1
- package/dist/schedule.js +0 -1323
- package/dist/schedule.js.map +0 -1
- package/llms.txt +0 -758
- package/src/llms.ts +0 -3
- package/src/schedule.ts +0 -1960
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import type { TimeOfDay } from "../../types.js";
|
|
3
|
-
import
|
|
3
|
+
import { defineRuleDescriptor } from "../rule-descriptor.js";
|
|
4
|
+
import type { RuleArtifact, RuleCompileContext } from "../rule-descriptor.js";
|
|
5
|
+
import type { ValidationReporter } from "../validation-reporter.js";
|
|
4
6
|
import type { ResolvedShiftAssignment } from "../response.js";
|
|
5
7
|
import { normalizeEndMinutes, priorityToPenalty, timeOfDayToMinutes } from "../utils.js";
|
|
6
|
-
import type { ValidationReporter } from "../validation-reporter.js";
|
|
7
8
|
import {
|
|
8
9
|
PrioritySchema,
|
|
9
10
|
entityScope,
|
|
10
|
-
requiredTimeScope,
|
|
11
11
|
parseEntityScope,
|
|
12
12
|
parseTimeScope,
|
|
13
|
-
|
|
13
|
+
requiredTimeScope,
|
|
14
14
|
resolveActiveDaysFromScope,
|
|
15
|
+
resolveMembersFromScope,
|
|
15
16
|
ruleGroup,
|
|
16
17
|
} from "./scope.types.js";
|
|
18
|
+
import { assignmentVar } from "./variables.js";
|
|
19
|
+
import { hardConstraint, reportValidation, softConstraint } from "./artifacts.js";
|
|
20
|
+
import { canAssignMemberToPattern, isPatternAvailableOnDay } from "./pattern-eligibility.js";
|
|
17
21
|
|
|
18
22
|
const timeOfDaySchema = z.object({
|
|
19
23
|
hours: z.number().int().min(0).max(23),
|
|
20
24
|
minutes: z.number().int().min(0).max(59),
|
|
21
25
|
});
|
|
22
26
|
|
|
23
|
-
const TimeOffSchema = z
|
|
27
|
+
export const TimeOffSchema = z
|
|
24
28
|
.object({
|
|
25
29
|
priority: PrioritySchema,
|
|
26
30
|
startTime: timeOfDaySchema.optional(),
|
|
@@ -40,173 +44,143 @@ const TimeOffSchema = z
|
|
|
40
44
|
);
|
|
41
45
|
|
|
42
46
|
/**
|
|
43
|
-
* Configuration for {@link
|
|
44
|
-
*
|
|
45
|
-
* - `priority` (required): how strictly the solver enforces this rule
|
|
46
|
-
* - `startTime` (optional): start of the time-off window within each day; must be paired with `endTime`
|
|
47
|
-
* - `endTime` (optional): end of the time-off window within each day; must be paired with `startTime`
|
|
48
|
-
*
|
|
49
|
-
* Entity scoping (at most one):
|
|
50
|
-
* - `memberIds`: restrict to specific members
|
|
51
|
-
* - `roleIds`: restrict to members with matching roles
|
|
52
|
-
* - `skillIds`: restrict to members with matching skills
|
|
53
|
-
*
|
|
54
|
-
* Time scoping (exactly one required):
|
|
55
|
-
* - `dateRange`: contiguous date range
|
|
56
|
-
* - `specificDates`: specific dates
|
|
57
|
-
* - `dayOfWeek`: days of the week
|
|
58
|
-
* - `recurringPeriods`: recurring calendar periods
|
|
47
|
+
* Configuration for {@link timeOffRuleDescriptor}.
|
|
59
48
|
*/
|
|
60
49
|
export type TimeOffConfig = z.infer<typeof TimeOffSchema>;
|
|
61
50
|
|
|
62
51
|
/**
|
|
63
|
-
*
|
|
52
|
+
* Low-level descriptor for the `time-off` rule.
|
|
64
53
|
*
|
|
65
|
-
*
|
|
66
|
-
* (date ranges, specific dates, days of week, recurring periods).
|
|
67
|
-
* Optionally supports partial-day time-off with startTime/endTime.
|
|
68
|
-
*
|
|
69
|
-
* @param config - See {@link TimeOffConfig}
|
|
70
|
-
* @example Full day vacation
|
|
71
|
-
* ```ts
|
|
72
|
-
* createTimeOffRule({
|
|
73
|
-
* memberIds: ["alice"],
|
|
74
|
-
* dateRange: { start: "2024-02-01", end: "2024-02-05" },
|
|
75
|
-
* priority: "MANDATORY",
|
|
76
|
-
* });
|
|
77
|
-
* ```
|
|
78
|
-
*
|
|
79
|
-
* @example Every Wednesday afternoon off for students
|
|
80
|
-
* ```ts
|
|
81
|
-
* createTimeOffRule({
|
|
82
|
-
* roleIds: ["student"],
|
|
83
|
-
* dayOfWeek: ["wednesday"],
|
|
84
|
-
* startTime: { hours: 14, minutes: 0 },
|
|
85
|
-
* endTime: { hours: 23, minutes: 59 },
|
|
86
|
-
* priority: "MANDATORY",
|
|
87
|
-
* });
|
|
88
|
-
* ```
|
|
89
|
-
*
|
|
90
|
-
* @example Specific date, partial day
|
|
91
|
-
* ```ts
|
|
92
|
-
* createTimeOffRule({
|
|
93
|
-
* memberIds: ["bob"],
|
|
94
|
-
* specificDates: ["2024-03-15"],
|
|
95
|
-
* startTime: { hours: 16, minutes: 0 },
|
|
96
|
-
* endTime: { hours: 23, minutes: 59 },
|
|
97
|
-
* priority: "MANDATORY",
|
|
98
|
-
* });
|
|
99
|
-
* ```
|
|
54
|
+
* @category Rules
|
|
100
55
|
*/
|
|
101
|
-
export
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
priorityToPenalty(priority),
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
|
|
161
|
-
validate(
|
|
162
|
-
assignments: ResolvedShiftAssignment[],
|
|
163
|
-
reporter: ValidationReporter,
|
|
164
|
-
context: RuleValidationContext,
|
|
165
|
-
) {
|
|
166
|
-
// MANDATORY time-off is a hard constraint - can't be violated
|
|
167
|
-
if (priority === "MANDATORY") return;
|
|
168
|
-
|
|
169
|
-
const targetMembers = resolveMembersFromScope(entityScopeValue, context.members);
|
|
170
|
-
if (targetMembers.length === 0) return;
|
|
171
|
-
|
|
172
|
-
const activeDays = resolveActiveDaysFromScope(timeScopeValue, context.days);
|
|
173
|
-
if (activeDays.length === 0) return;
|
|
174
|
-
|
|
175
|
-
for (const emp of targetMembers) {
|
|
176
|
-
for (const day of activeDays) {
|
|
177
|
-
const violated = assignments.some(
|
|
178
|
-
(a) =>
|
|
179
|
-
a.memberId === emp.id &&
|
|
180
|
-
a.day === day &&
|
|
181
|
-
assignmentOverlapsTimeWindow(a, timeWindowStart, timeWindowEnd),
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
if (violated) {
|
|
185
|
-
reporter.reportRuleViolation({
|
|
186
|
-
rule: "time-off",
|
|
187
|
-
message: `Time-off request for ${emp.id} on ${day} could not be honored`,
|
|
188
|
-
context: {
|
|
189
|
-
memberIds: [emp.id],
|
|
190
|
-
days: [day],
|
|
191
|
-
},
|
|
56
|
+
export const timeOffRuleDescriptor = defineRuleDescriptor({
|
|
57
|
+
name: "time-off",
|
|
58
|
+
schema: TimeOffSchema,
|
|
59
|
+
compile(config, ctx) {
|
|
60
|
+
const { priority, startTime, endTime } = config;
|
|
61
|
+
const fullDayStart: TimeOfDay = { hours: 0, minutes: 0 };
|
|
62
|
+
const fullDayEnd: TimeOfDay = { hours: 23, minutes: 59 };
|
|
63
|
+
const timeWindowStart = startTime ?? fullDayStart;
|
|
64
|
+
const timeWindowEnd = endTime ?? fullDayEnd;
|
|
65
|
+
|
|
66
|
+
const entityScopeValue = parseEntityScope(config);
|
|
67
|
+
const timeScopeValue = parseTimeScope(config);
|
|
68
|
+
const group = ruleGroup("time-off", "Time off", entityScopeValue, timeScopeValue);
|
|
69
|
+
const targetMembers = resolveMembersFromScope(entityScopeValue, [...ctx.members]);
|
|
70
|
+
const activeDays = resolveActiveDaysFromScope(timeScopeValue, [...ctx.days]);
|
|
71
|
+
const timeWindowKey = formatTimeWindowKey(timeWindowStart, timeWindowEnd);
|
|
72
|
+
|
|
73
|
+
const constraintArtifacts: RuleArtifact[] = [];
|
|
74
|
+
for (const member of targetMembers) {
|
|
75
|
+
for (const day of activeDays) {
|
|
76
|
+
const overlappingVars = ctx.shiftPatterns
|
|
77
|
+
.filter(
|
|
78
|
+
(pattern) =>
|
|
79
|
+
canAssignMemberToPattern(member, pattern) &&
|
|
80
|
+
isPatternAvailableOnDay(pattern, day) &&
|
|
81
|
+
shiftOverlapsTimeWindow(pattern, timeWindowStart, timeWindowEnd),
|
|
82
|
+
)
|
|
83
|
+
.map((pattern) => assignmentVar(member.id, pattern.id, day.iso));
|
|
84
|
+
|
|
85
|
+
if (overlappingVars.length === 0) continue;
|
|
86
|
+
|
|
87
|
+
const description = buildTimeOffDescription(
|
|
88
|
+
member.id,
|
|
89
|
+
day.iso,
|
|
90
|
+
timeWindowStart,
|
|
91
|
+
timeWindowEnd,
|
|
92
|
+
);
|
|
93
|
+
const context = { memberIds: [member.id], days: [day.iso] };
|
|
94
|
+
const constraintId = `time-off:${group.key}:${member.id}:${day.iso}:${timeWindowKey}`;
|
|
95
|
+
const terms = overlappingVars.map((varName) => ({ var: varName, coeff: 1 }));
|
|
96
|
+
|
|
97
|
+
if (priority === "MANDATORY") {
|
|
98
|
+
constraintArtifacts.push({
|
|
99
|
+
kind: "coverage-exclusion",
|
|
100
|
+
group,
|
|
101
|
+
memberId: member.id,
|
|
102
|
+
day: day.iso,
|
|
103
|
+
startTime: timeWindowStart,
|
|
104
|
+
endTime: timeWindowEnd,
|
|
105
|
+
});
|
|
106
|
+
constraintArtifacts.push(
|
|
107
|
+
hardConstraint({
|
|
192
108
|
group,
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
109
|
+
description,
|
|
110
|
+
context,
|
|
111
|
+
validation: reportValidation(constraintId),
|
|
112
|
+
terms,
|
|
113
|
+
comparator: "<=",
|
|
114
|
+
targetValue: 0,
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
} else {
|
|
118
|
+
constraintArtifacts.push(
|
|
119
|
+
softConstraint({
|
|
202
120
|
group,
|
|
203
|
-
|
|
204
|
-
|
|
121
|
+
description,
|
|
122
|
+
context,
|
|
123
|
+
validation: reportValidation(),
|
|
124
|
+
terms,
|
|
125
|
+
comparator: "<=",
|
|
126
|
+
targetValue: 0,
|
|
127
|
+
penalty: priorityToPenalty(priority),
|
|
128
|
+
constraintId,
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
205
131
|
}
|
|
206
132
|
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const postSolveValidator =
|
|
136
|
+
priority === "MANDATORY"
|
|
137
|
+
? []
|
|
138
|
+
: [
|
|
139
|
+
{
|
|
140
|
+
kind: "post-solve-feedback" as const,
|
|
141
|
+
run(
|
|
142
|
+
assignments: readonly ResolvedShiftAssignment[],
|
|
143
|
+
reporter: ValidationReporter,
|
|
144
|
+
validationContext: RuleCompileContext,
|
|
145
|
+
) {
|
|
146
|
+
const validatorMembers = resolveMembersFromScope(entityScopeValue, [
|
|
147
|
+
...validationContext.members,
|
|
148
|
+
]);
|
|
149
|
+
const validatorDays = resolveActiveDaysFromScope(timeScopeValue, [
|
|
150
|
+
...validationContext.days,
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
for (const member of validatorMembers) {
|
|
154
|
+
for (const day of validatorDays) {
|
|
155
|
+
const violated = assignments.some(
|
|
156
|
+
(assignment) =>
|
|
157
|
+
assignment.memberId === member.id &&
|
|
158
|
+
assignment.day === day.iso &&
|
|
159
|
+
assignmentOverlapsTimeWindow(assignment, timeWindowStart, timeWindowEnd),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (violated) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
reporter.reportRulePassed({
|
|
167
|
+
rule: "time-off",
|
|
168
|
+
message: `Time-off honored for ${member.id} on ${day.iso}`,
|
|
169
|
+
context: { memberIds: [member.id], days: [day.iso] },
|
|
170
|
+
group,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
rule: "time-off",
|
|
180
|
+
artifacts: [...constraintArtifacts, ...postSolveValidator],
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
});
|
|
210
184
|
|
|
211
185
|
function shiftOverlapsTimeWindow(
|
|
212
186
|
pattern: { startTime: TimeOfDay; endTime: TimeOfDay },
|
|
@@ -223,7 +197,7 @@ function shiftOverlapsTimeWindow(
|
|
|
223
197
|
}
|
|
224
198
|
|
|
225
199
|
function assignmentOverlapsTimeWindow(
|
|
226
|
-
assignment:
|
|
200
|
+
assignment: { startTime: TimeOfDay; endTime: TimeOfDay },
|
|
227
201
|
windowStart: TimeOfDay,
|
|
228
202
|
windowEnd: TimeOfDay,
|
|
229
203
|
): boolean {
|
|
@@ -235,3 +209,20 @@ function assignmentOverlapsTimeWindow(
|
|
|
235
209
|
|
|
236
210
|
return Math.max(assignStart, winStart) < Math.min(assignEnd, winEnd);
|
|
237
211
|
}
|
|
212
|
+
|
|
213
|
+
function buildTimeOffDescription(
|
|
214
|
+
memberId: string,
|
|
215
|
+
dayIso: string,
|
|
216
|
+
windowStart: TimeOfDay,
|
|
217
|
+
windowEnd: TimeOfDay,
|
|
218
|
+
): string {
|
|
219
|
+
return `Time off for ${memberId} on ${dayIso} (${formatTimeWindowKey(windowStart, windowEnd)})`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function formatTimeWindowKey(windowStart: TimeOfDay, windowEnd: TimeOfDay): string {
|
|
223
|
+
return `${formatTimeOfDay(windowStart)}-${formatTimeOfDay(windowEnd)}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function formatTimeOfDay(time: TimeOfDay): string {
|
|
227
|
+
return `${time.hours.toString().padStart(2, "0")}:${time.minutes.toString().padStart(2, "0")}`;
|
|
228
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function assignmentVar(memberId: string, patternId: string, dayIso: string): string {
|
|
2
|
+
return `assign:${memberId}:${patternId}:${dayIso}`;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function assignedDayVariableName(memberId: string, dayIso: string): string {
|
|
6
|
+
return `assigned_day_${memberId}_${dayIso}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function assignedDayStartVariableName(memberId: string, dayIso: string): string {
|
|
10
|
+
return `assigned_day_start_${memberId}_${dayIso}`;
|
|
11
|
+
}
|
package/src/cpsat/rules.ts
CHANGED
|
@@ -1,27 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level CP-SAT rule descriptor and registry APIs.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* These exports are intended for advanced consumers building custom rule
|
|
6
|
+
* registries or integrating directly with the descriptor layer underneath the
|
|
7
|
+
* high-level {@link schedule} helpers.
|
|
8
|
+
*
|
|
9
|
+
* Rules are the feedback unit. Validation groups summarize rule outcomes for
|
|
10
|
+
* presentation, while individual solver constraints remain implementation
|
|
11
|
+
* details. Descriptor compilation therefore emits artifacts that make the
|
|
12
|
+
* validation story explicit: reported constraints, pre-solve feedback,
|
|
13
|
+
* post-solve validators, cost contributions, and explicit skip strategies for
|
|
14
|
+
* scaffolding or artifacts with no meaningful validation feedback.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
|
|
1
19
|
export {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
assignTogetherRuleDescriptor,
|
|
21
|
+
assignmentPriorityRuleDescriptor,
|
|
22
|
+
locationPreferenceRuleDescriptor,
|
|
23
|
+
maxConcurrentAssignmentsRuleDescriptor,
|
|
24
|
+
maxConsecutiveDaysRuleDescriptor,
|
|
25
|
+
maxHoursDayRuleDescriptor,
|
|
26
|
+
maxHoursWeekRuleDescriptor,
|
|
27
|
+
minConsecutiveDaysRuleDescriptor,
|
|
28
|
+
minHoursDayRuleDescriptor,
|
|
29
|
+
minHoursWeekRuleDescriptor,
|
|
30
|
+
minRestBetweenShiftsRuleDescriptor,
|
|
31
|
+
targetDaysWeekRuleDescriptor,
|
|
32
|
+
targetPeakConcurrentAssignmentsRuleDescriptor,
|
|
33
|
+
timeOffRuleDescriptor,
|
|
13
34
|
} from "./rules/index.js";
|
|
14
35
|
|
|
15
|
-
export {
|
|
36
|
+
export {
|
|
37
|
+
assertValidCpsatRuleRegistry,
|
|
38
|
+
builtInCpsatRuleRegistry,
|
|
39
|
+
createCpsatRuleRegistry,
|
|
40
|
+
} from "./rules/registry.js";
|
|
16
41
|
|
|
17
42
|
export type {
|
|
43
|
+
AnyCpsatRuleConfigEntry,
|
|
44
|
+
BuiltInCpsatRuleConfigRegistry,
|
|
45
|
+
BuiltInCpsatRuleRegistry,
|
|
46
|
+
CpsatRuleConfigByName,
|
|
47
|
+
CpsatRuleConfigEntry,
|
|
48
|
+
CpsatRuleConfigEntryFor,
|
|
18
49
|
CpsatRuleName,
|
|
19
50
|
CpsatRuleRegistry,
|
|
20
|
-
CpsatRuleFactories,
|
|
21
|
-
BuiltInCpsatRuleFactories,
|
|
22
|
-
CpsatRuleRegistryFromFactories,
|
|
23
|
-
CreateCpsatRuleFunction,
|
|
24
|
-
CpsatRuleConfigEntry,
|
|
25
51
|
} from "./rules/rules.types.js";
|
|
26
52
|
|
|
27
53
|
export { buildCpsatRules, getMemberIdsForScope, resolveRuleScopes } from "./rules/resolver.js";
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { DayOfWeek, TimeOfDay } from "../types.js";
|
|
2
|
-
import { toDayOfWeekUTC } from "../datetime.utils.js";
|
|
3
|
-
import { parseDayString } from "./utils.js";
|
|
1
|
+
import type { DateString, DayOfWeek, SchedulingDay, TimeOfDay } from "../types.js";
|
|
4
2
|
import type { CoverageRequirement, Priority } from "./types.js";
|
|
5
3
|
import { assertSafeKeySegment, type ValidationGroup } from "./validation.types.js";
|
|
6
4
|
|
|
@@ -40,7 +38,7 @@ export interface SemanticTimeVariant extends SemanticTimeDef {
|
|
|
40
38
|
/** Restrict this entry to specific days of the week. */
|
|
41
39
|
dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]];
|
|
42
40
|
/** Restrict this entry to specific dates (YYYY-MM-DD). */
|
|
43
|
-
dates?:
|
|
41
|
+
dates?: DateString[];
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
/**
|
|
@@ -59,7 +57,7 @@ interface SemanticCoverageRequirementBase<S extends string> {
|
|
|
59
57
|
/** Scope this requirement to specific days of the week */
|
|
60
58
|
dayOfWeek?: [DayOfWeek, ...DayOfWeek[]];
|
|
61
59
|
/** Scope this requirement to specific dates (YYYY-MM-DD) */
|
|
62
|
-
dates?:
|
|
60
|
+
dates?: DateString[];
|
|
63
61
|
/**
|
|
64
62
|
* Override the auto-generated group for validation reporting.
|
|
65
63
|
* If not provided, a group is auto-generated from the semantic time name,
|
|
@@ -141,7 +139,7 @@ export type SemanticCoverageRequirement<S extends string> =
|
|
|
141
139
|
* Base fields shared by concrete coverage requirement variants.
|
|
142
140
|
*/
|
|
143
141
|
interface ConcreteCoverageRequirementBase {
|
|
144
|
-
day:
|
|
142
|
+
day: DateString;
|
|
145
143
|
startTime: TimeOfDay;
|
|
146
144
|
endTime: TimeOfDay;
|
|
147
145
|
targetCount: number;
|
|
@@ -199,6 +197,7 @@ export type ConcreteCoverageRequirement =
|
|
|
199
197
|
/**
|
|
200
198
|
* A day-specific count within a variant {@link cover} call.
|
|
201
199
|
*
|
|
200
|
+
* @remarks
|
|
202
201
|
* Each variant specifies a count and optional day/date scope. During
|
|
203
202
|
* resolution, the most specific matching variant wins for each day
|
|
204
203
|
* (`dates` > `dayOfWeek` > default), mirroring {@link SemanticTimeVariant}.
|
|
@@ -221,7 +220,7 @@ export interface CoverageVariant {
|
|
|
221
220
|
/** Restrict this variant to specific days of the week. */
|
|
222
221
|
dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]];
|
|
223
222
|
/** Restrict this variant to specific dates (YYYY-MM-DD). */
|
|
224
|
-
dates?:
|
|
223
|
+
dates?: DateString[];
|
|
225
224
|
/** Defaults to `"MANDATORY"`. */
|
|
226
225
|
priority?: Priority;
|
|
227
226
|
}
|
|
@@ -337,7 +336,7 @@ export interface SemanticTimeContext<S extends string> {
|
|
|
337
336
|
* Resolve all coverage requirements to concrete CoverageRequirement[]
|
|
338
337
|
* for the given days in the scheduling horizon.
|
|
339
338
|
*/
|
|
340
|
-
resolve(reqs: MixedCoverageRequirement<S>[], days:
|
|
339
|
+
resolve(reqs: MixedCoverageRequirement<S>[], days: SchedulingDay[]): CoverageRequirement[];
|
|
341
340
|
}
|
|
342
341
|
|
|
343
342
|
/**
|
|
@@ -394,7 +393,7 @@ export function defineSemanticTimes<const T extends Record<string, SemanticTimeE
|
|
|
394
393
|
return reqs;
|
|
395
394
|
},
|
|
396
395
|
|
|
397
|
-
resolve(reqs: MixedCoverageRequirement<S>[], days:
|
|
396
|
+
resolve(reqs: MixedCoverageRequirement<S>[], days: SchedulingDay[]): CoverageRequirement[] {
|
|
398
397
|
return resolveSemanticCoverage(defs, reqs, days);
|
|
399
398
|
},
|
|
400
399
|
};
|
|
@@ -405,7 +404,7 @@ export function defineSemanticTimes<const T extends Record<string, SemanticTimeE
|
|
|
405
404
|
* Handles the discriminated union by checking which variant to construct.
|
|
406
405
|
*/
|
|
407
406
|
function buildCoverageRequirement(
|
|
408
|
-
day:
|
|
407
|
+
day: DateString,
|
|
409
408
|
startTime: TimeOfDay,
|
|
410
409
|
endTime: TimeOfDay,
|
|
411
410
|
roleIds: [string, ...string[]] | undefined,
|
|
@@ -417,14 +416,11 @@ function buildCoverageRequirement(
|
|
|
417
416
|
const base = { day, startTime, endTime, targetCount, priority, group };
|
|
418
417
|
|
|
419
418
|
if (roleIds && roleIds.length > 0) {
|
|
420
|
-
// Role-based (with optional skills)
|
|
421
419
|
return skillIds && skillIds.length > 0 ? { ...base, roleIds, skillIds } : { ...base, roleIds };
|
|
422
420
|
} else if (skillIds && skillIds.length > 0) {
|
|
423
|
-
// Skill-only
|
|
424
421
|
return { ...base, skillIds };
|
|
425
422
|
}
|
|
426
423
|
|
|
427
|
-
// This shouldn't happen if input types are correct, but handle gracefully
|
|
428
424
|
throw new Error(
|
|
429
425
|
`Coverage requirement for day "${day}" must have at least one of roleIds or skillIds`,
|
|
430
426
|
);
|
|
@@ -437,15 +433,15 @@ function buildCoverageRequirement(
|
|
|
437
433
|
function resolveSemanticCoverage<S extends string>(
|
|
438
434
|
defs: Record<S, SemanticTimeEntry>,
|
|
439
435
|
reqs: MixedCoverageRequirement<S>[],
|
|
440
|
-
days:
|
|
436
|
+
days: SchedulingDay[],
|
|
441
437
|
): CoverageRequirement[] {
|
|
442
438
|
const result: CoverageRequirement[] = [];
|
|
443
|
-
const
|
|
439
|
+
const dayIsoSet = new Set(days.map((d) => d.iso));
|
|
444
440
|
|
|
445
441
|
for (const req of reqs) {
|
|
446
442
|
if (isConcreteCoverage(req)) {
|
|
447
443
|
// Concrete requirement - pass through if day is in horizon
|
|
448
|
-
if (
|
|
444
|
+
if (dayIsoSet.has(req.day)) {
|
|
449
445
|
const defaultGroup = concreteCoverageGroup(req);
|
|
450
446
|
result.push(
|
|
451
447
|
buildCoverageRequirement(
|
|
@@ -478,7 +474,7 @@ function resolveSemanticCoverage<S extends string>(
|
|
|
478
474
|
|
|
479
475
|
result.push(
|
|
480
476
|
buildCoverageRequirement(
|
|
481
|
-
day,
|
|
477
|
+
day.iso,
|
|
482
478
|
resolved.startTime,
|
|
483
479
|
resolved.endTime,
|
|
484
480
|
req.roleIds,
|
|
@@ -504,7 +500,7 @@ function resolveSemanticCoverage<S extends string>(
|
|
|
504
500
|
if (resolved) {
|
|
505
501
|
result.push(
|
|
506
502
|
buildCoverageRequirement(
|
|
507
|
-
day,
|
|
503
|
+
day.iso,
|
|
508
504
|
resolved.startTime,
|
|
509
505
|
resolved.endTime,
|
|
510
506
|
req.roleIds,
|
|
@@ -568,21 +564,18 @@ function variantCoverageGroup<S extends string>(
|
|
|
568
564
|
*/
|
|
569
565
|
function resolveVariantForDay(
|
|
570
566
|
variants: readonly CoverageVariant[],
|
|
571
|
-
day:
|
|
567
|
+
day: SchedulingDay,
|
|
572
568
|
): CoverageVariant | null {
|
|
573
|
-
const date = parseDayString(day);
|
|
574
|
-
const dayOfWeek = toDayOfWeekUTC(date);
|
|
575
|
-
|
|
576
569
|
let dateMatch: CoverageVariant | null = null;
|
|
577
570
|
let dowMatch: CoverageVariant | null = null;
|
|
578
571
|
let defaultMatch: CoverageVariant | null = null;
|
|
579
572
|
|
|
580
573
|
for (const variant of variants) {
|
|
581
|
-
if (variant.dates && variant.dates.includes(day)) {
|
|
574
|
+
if (variant.dates && variant.dates.includes(day.iso)) {
|
|
582
575
|
dateMatch = variant;
|
|
583
576
|
break;
|
|
584
577
|
}
|
|
585
|
-
if (variant.dayOfWeek && variant.dayOfWeek.includes(dayOfWeek)) {
|
|
578
|
+
if (variant.dayOfWeek && variant.dayOfWeek.includes(day.dayOfWeek)) {
|
|
586
579
|
dowMatch = variant;
|
|
587
580
|
}
|
|
588
581
|
if (!variant.dates && !variant.dayOfWeek) {
|
|
@@ -607,8 +600,9 @@ function concreteCoverageGroup(req: ConcreteCoverageRequirement): ValidationGrou
|
|
|
607
600
|
* Formats days of week for display in group keys.
|
|
608
601
|
*/
|
|
609
602
|
function formatDaysScope(days: DayOfWeek[]): string {
|
|
610
|
-
|
|
611
|
-
if (
|
|
603
|
+
const first = days[0];
|
|
604
|
+
if (!first) return "";
|
|
605
|
+
if (days.length === 1) return first;
|
|
612
606
|
|
|
613
607
|
// Check for common patterns
|
|
614
608
|
const weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday"];
|
|
@@ -640,20 +634,18 @@ function formatTime(time: TimeOfDay): string {
|
|
|
640
634
|
* Filter days based on optional day-of-week and specific date constraints.
|
|
641
635
|
*/
|
|
642
636
|
function filterDays(
|
|
643
|
-
allDays:
|
|
637
|
+
allDays: SchedulingDay[],
|
|
644
638
|
dayOfWeek?: DayOfWeek[],
|
|
645
639
|
specificDates?: string[],
|
|
646
|
-
):
|
|
640
|
+
): SchedulingDay[] {
|
|
647
641
|
let result = allDays;
|
|
648
642
|
|
|
649
643
|
if (specificDates && specificDates.length > 0) {
|
|
650
|
-
// If specific dates are provided, use only those (intersection with allDays)
|
|
651
644
|
const dateSet = new Set(specificDates);
|
|
652
|
-
result = result.filter((d) => dateSet.has(d));
|
|
645
|
+
result = result.filter((d) => dateSet.has(d.iso));
|
|
653
646
|
} else if (dayOfWeek && dayOfWeek.length > 0) {
|
|
654
|
-
// Filter by day of week
|
|
655
647
|
const daySet = new Set(dayOfWeek);
|
|
656
|
-
result = result.filter((d) => daySet.has(
|
|
648
|
+
result = result.filter((d) => daySet.has(d.dayOfWeek));
|
|
657
649
|
}
|
|
658
650
|
|
|
659
651
|
return result;
|
|
@@ -663,26 +655,21 @@ function filterDays(
|
|
|
663
655
|
* Resolve the time definition for a specific day.
|
|
664
656
|
* Returns the most specific match: date-specific > day-of-week > default.
|
|
665
657
|
*/
|
|
666
|
-
function resolveTimeForDay(entry: SemanticTimeEntry, day:
|
|
667
|
-
// Simple definition - applies to all days
|
|
658
|
+
function resolveTimeForDay(entry: SemanticTimeEntry, day: SchedulingDay): SemanticTimeDef | null {
|
|
668
659
|
if (!Array.isArray(entry)) {
|
|
669
660
|
return entry;
|
|
670
661
|
}
|
|
671
662
|
|
|
672
|
-
const date = parseDayString(day);
|
|
673
|
-
const dayOfWeek = toDayOfWeekUTC(date);
|
|
674
|
-
|
|
675
|
-
// Find best match: date-specific first, then day-of-week, then default
|
|
676
663
|
let dateMatch: SemanticTimeVariant | null = null;
|
|
677
664
|
let dowMatch: SemanticTimeVariant | null = null;
|
|
678
665
|
let defaultMatch: SemanticTimeVariant | null = null;
|
|
679
666
|
|
|
680
667
|
for (const variant of entry) {
|
|
681
|
-
if (variant.dates && variant.dates.includes(day)) {
|
|
668
|
+
if (variant.dates && variant.dates.includes(day.iso)) {
|
|
682
669
|
dateMatch = variant;
|
|
683
|
-
break;
|
|
670
|
+
break;
|
|
684
671
|
}
|
|
685
|
-
if (variant.dayOfWeek && variant.dayOfWeek.includes(dayOfWeek)) {
|
|
672
|
+
if (variant.dayOfWeek && variant.dayOfWeek.includes(day.dayOfWeek)) {
|
|
686
673
|
dowMatch = variant;
|
|
687
674
|
}
|
|
688
675
|
if (!variant.dates && !variant.dayOfWeek) {
|
|
@@ -690,6 +677,5 @@ function resolveTimeForDay(entry: SemanticTimeEntry, day: string): SemanticTimeD
|
|
|
690
677
|
}
|
|
691
678
|
}
|
|
692
679
|
|
|
693
|
-
|
|
694
|
-
return match ?? null;
|
|
680
|
+
return dateMatch ?? dowMatch ?? defaultMatch ?? null;
|
|
695
681
|
}
|