Skip to content

Commit

Permalink
move constraint status to a more centralized location in the constrai…
Browse files Browse the repository at this point in the history
…nt store

add `Modified` constraint checking
  • Loading branch information
duranb committed Jan 28, 2025
1 parent 939ae65 commit c2ca5fb
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 109 deletions.
2 changes: 1 addition & 1 deletion src/components/constraints/ConstraintListItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@
</div>
</svelte:fragment>

<Collapse title="Parameters" className="scheduling-goal-analysis-activities" defaultExpanded={true}>
<Collapse title="Parameters" className="constraint-parameters" defaultExpanded={true}>
<Parameters disabled={false} expanded={true} {formParameters} on:change={onChangeFormParameters} />
</Collapse>

Expand Down
4 changes: 3 additions & 1 deletion src/components/constraints/ConstraintsPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,10 @@
},
],
]}
on:click={() => $plan && effects.checkConstraints($plan, true, user)}><RefreshIcon /></PanelHeaderActionButton
on:click={() => $plan && effects.checkConstraints($plan, true, user)}
>
<RefreshIcon />
</PanelHeaderActionButton>
<PanelHeaderActionButton
disabled={$simulationStatus !== Status.Complete || $constraintsStatus === Status.Complete}
tooltipContent={$simulationStatus !== Status.Complete ? 'Completed simulation required' : ''}
Expand Down
152 changes: 56 additions & 96 deletions src/stores/constraints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type {
ConstraintMetadata,
ConstraintPlanSpecification,
ConstraintResponse,
ConstraintResult,
ConstraintResultWithName,
ConstraintRun,
} from '../types/constraint';
Expand All @@ -24,8 +23,7 @@ export const constraintMetadataId: Writable<number> = writable(-1);

export const constraintPlanSpecVisibilityMapWritable: Writable<ConstraintPlanSpecVisibilityMap> = writable({});

export const rawCheckConstraintsStatus: Writable<Status | null> = writable(null);
export const rawConstraintResponses: Writable<ConstraintResponse[]> = writable([]);
export const checkConstraintsQueryStatus: Writable<Status | null> = writable(null);

export const constraintsColumns: Writable<string> = writable('1fr 3px 1fr');

Expand All @@ -38,6 +36,10 @@ export const constraintRuns = gqlSubscribable<ConstraintRun[]>(
{ simulationDatasetId: simulationDatasetLatestId },
[],
null,
value => {
checkConstraintsQueryStatus.set(null);
return value;
},
);

export const constraintPlanSpecs = gqlSubscribable<ConstraintPlanSpecification[]>(
Expand All @@ -59,9 +61,16 @@ export const constraintsMap: Readable<Record<string, ConstraintMetadata>> = deri
keyBy($constraints, 'id'),
);

export const constraintPlanSpecsMap: Readable<Record<string, ConstraintPlanSpecification>> = derived(
export const constraintPlanSpecsMap: Readable<ConstraintPlanSpecMap> = derived(
[constraintPlanSpecs],
([$constraintPlanSpecs]) => keyBy($constraintPlanSpecs, 'constraint_id'),
([$constraintPlanSpecs]) =>
$constraintPlanSpecs.reduce((prevPlanSpecMap: ConstraintPlanSpecMap, constraintPlanSpec) => {
if (!prevPlanSpecMap[constraintPlanSpec.constraint_id]) {
prevPlanSpecMap[constraintPlanSpec.constraint_id] = {};
}
prevPlanSpecMap[constraintPlanSpec.constraint_id][constraintPlanSpec.invocation_id] = constraintPlanSpec;
return prevPlanSpecMap;
}, {}),
);

export const allowedConstraintPlanSpecs: Readable<ConstraintPlanSpecification[]> = derived(
Expand Down Expand Up @@ -105,80 +114,31 @@ export const constraintVisibilityMap: Readable<ConstraintPlanSpecVisibilityMap>
},
);

export const relevantRawConstraintResponses: Readable<ConstraintResponse[]> = derived(
[rawConstraintResponses, constraintPlanSpecsMap],
([$rawConstraintResponses, $constraintPlanSpecsMap]) => {
return $rawConstraintResponses.filter(response => $constraintPlanSpecsMap[response.constraintId] != null);
},
);

export const constraintsViolationStatus: Readable<Status | null> = derived(
[relevantRawConstraintResponses],
([$relevantRawConstraintResponses]) => {
if ($relevantRawConstraintResponses.length) {
const successfulConstraintResults: ConstraintResult[] = $relevantRawConstraintResponses
.filter(constraintResponse => constraintResponse.success)
.map(constraintResponse => constraintResponse.results);

const anyViolations = successfulConstraintResults.reduce((bool, prev) => {
if (prev.violations && prev.violations.length > 0) {
bool = true;
}
return bool;
}, false);

if (successfulConstraintResults.length !== $relevantRawConstraintResponses.length) {
return Status.Failed;
}

return anyViolations ? Status.Failed : Status.Complete;
}
return null;
},
);

export const constraintResponses: Readable<ConstraintResponse[]> = derived(
[constraintRuns, relevantRawConstraintResponses, planStartTimeMs],
([$constraintRuns, $checkConstraintResponse, $planStartTimeMs]) => {
return $constraintRuns
.map(
run =>
({
constraintId: run.constraint_id,
constraintInvocationId: run.constraint_invocation_id,
constraintName: run.constraint_metadata.name,
errors: [],
results: {
...run.results,
violations:
run.results.violations?.map(violation => ({
...violation,
windows: violation.windows.map(({ end, start }) => ({
end: $planStartTimeMs + end / 1000,
start: $planStartTimeMs + start / 1000,
})),
})) ?? null,
},
success: true,
type: 'plan',
}) as ConstraintResponse,
)
.concat(
$checkConstraintResponse.map(response => ({
...response,
[constraintRuns, planStartTimeMs],
([$constraintRuns, $planStartTimeMs]) => {
return $constraintRuns.map(
run =>
({
constraintId: run.constraint_id,
constraintInvocationId: run.constraint_invocation_id,
constraintName: run.constraint_metadata.name,
errors: [],
results: {
...response.results,
...run.results,
violations:
response.results.violations?.map(violation => ({
run.results.violations?.map(violation => ({
...violation,
windows: violation.windows.map(({ end, start }) => ({
end: $planStartTimeMs + end / 1000,
start: $planStartTimeMs + start / 1000,
})),
})) ?? null,
},
})),
);
success: true,
type: 'plan',
}) as ConstraintResponse,
);
},
);

Expand Down Expand Up @@ -214,21 +174,10 @@ export const relevantConstraintRuns: Readable<ConstraintRun[]> = derived(
[constraintRuns, constraintPlanSpecsMap],
([$constraintRuns, $constraintPlanSpecsMap]) => {
return $constraintRuns.filter(constraintRun => {
const constraintPlanSpec = $constraintPlanSpecsMap[constraintRun.constraint_id];
let revision = -1;

if (constraintPlanSpec) {
if (constraintPlanSpec.constraint_revision === null) {
revision =
constraintPlanSpec.constraint_metadata?.versions[
(constraintPlanSpec.constraint_metadata?.versions.length ?? 0) - 1
]?.revision ?? -1;
} else {
revision = constraintPlanSpec.constraint_revision;
}
}
const constraintPlanSpec =
$constraintPlanSpecsMap[constraintRun.constraint_id][constraintRun.constraint_invocation_id];

return revision === constraintRun.constraint_revision;
return constraintPlanSpec !== undefined;
});
},
);
Expand All @@ -254,6 +203,17 @@ export const cachedConstraintsStatus: Readable<Status | null> = derived(
([$relevantConstraintRuns, $constraintPlanSpecsMap]) => {
return $relevantConstraintRuns.reduce(
(status: Status | null, constraintRun: ConstraintRun) => {
const constraintPlanSpec =
$constraintPlanSpecsMap[constraintRun.constraint_id][constraintRun.constraint_invocation_id];

if (
constraintPlanSpec &&
(constraintRun.constraint_revision !== constraintPlanSpec.constraint_revision ||
JSON.stringify(constraintRun.arguments) !== JSON.stringify(constraintPlanSpec.arguments))
) {
return Status.Modified;
}

if (constraintRun.results.violations?.length) {
return Status.Failed;
} else if (status !== Status.Failed) {
Expand All @@ -268,7 +228,7 @@ export const cachedConstraintsStatus: Readable<Status | null> = derived(
);

export const checkConstraintsStatus: Readable<Status | null> = derived(
[rawCheckConstraintsStatus, cachedConstraintsStatus],
[checkConstraintsQueryStatus, cachedConstraintsStatus],
([$rawCheckConstraintsStatus, $cachedConstraintsStatus]) => {
if ($rawCheckConstraintsStatus !== null) {
return $rawCheckConstraintsStatus;
Expand All @@ -283,19 +243,21 @@ export const checkConstraintsStatus: Readable<Status | null> = derived(
);

export const constraintsStatus: Readable<Status | null> = derived(
[cachedConstraintsStatus, constraintsViolationStatus, checkConstraintsStatus, uncheckedConstraintCount],
([$cachedConstraintsStatus, $constraintsViolationStatus, $checkConstraintsStatus, $uncheckedConstraintCount]) => {
if ($checkConstraintsStatus === Status.Incomplete) {
return Status.Incomplete;
} else if (!$cachedConstraintsStatus) {
[checkConstraintsQueryStatus, cachedConstraintsStatus, uncheckedConstraintCount],
([$rawCheckConstraintsStatus, $cachedConstraintsStatus, $uncheckedConstraintCount]) => {
if ($rawCheckConstraintsStatus) {
return $rawCheckConstraintsStatus;
}

if (!$cachedConstraintsStatus) {
return null;
} else if ($cachedConstraintsStatus !== Status.Complete) {
return $constraintsViolationStatus ?? $cachedConstraintsStatus;
} else if ($uncheckedConstraintCount > 0) {
return Status.PartialSuccess;
} else if ($cachedConstraintsStatus !== Status.Complete) {
return $cachedConstraintsStatus;
}

return $constraintsViolationStatus ?? $cachedConstraintsStatus;
return $cachedConstraintsStatus;
},
);

Expand Down Expand Up @@ -340,11 +302,9 @@ export function resetPlanConstraintStores() {
}

export function resetConstraintStores(): void {
rawCheckConstraintsStatus.set(null);
rawConstraintResponses.set([]);
checkConstraintsQueryStatus.set(null);
}

export function resetConstraintStoresForSimulation(): void {
rawCheckConstraintsStatus.set(Status.Unchecked);
rawConstraintResponses.set([]);
checkConstraintsQueryStatus.set(Status.Unchecked);
}
15 changes: 4 additions & 11 deletions src/utilities/effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import { SchedulingDefinitionType } from '../enums/scheduling';
import { SearchParameters } from '../enums/searchParameters';
import { Status } from '../enums/status';
import { activityDirectivesDB, selectedActivityDirectiveId } from '../stores/activities';
import {
rawCheckConstraintsStatus,
rawConstraintResponses,
resetConstraintStoresForSimulation,
} from '../stores/constraints';
import { checkConstraintsQueryStatus, resetConstraintStoresForSimulation } from '../stores/constraints';
import { catchError, catchSchedulingError } from '../stores/errors';
import {
createExpansionRuleError,
Expand Down Expand Up @@ -425,7 +421,7 @@ const effects = {

async checkConstraints(plan: Plan, force: boolean = false, user: User | null): Promise<void> {
try {
rawCheckConstraintsStatus.set(Status.Incomplete);
checkConstraintsQueryStatus.set(Status.Incomplete);
if (plan !== null) {
const { id: planId } = plan;
const data = await reqHasura<ConstraintResponse[]>(
Expand All @@ -437,8 +433,6 @@ const effects = {
user,
);
if (data.constraintResponses) {
rawConstraintResponses.set(data.constraintResponses);

// find only the constraints compiled.
const successfulConstraintResults: ConstraintResult[] = data.constraintResponses
.filter(constraintResponse => constraintResponse.success)
Expand All @@ -449,13 +443,11 @@ const effects = {
);
if (successfulConstraintResults.length === 0 && data.constraintResponses.length > 0) {
showFailureToast('All Constraints Failed');
rawCheckConstraintsStatus.set(Status.Failed);
checkConstraintsQueryStatus.set(Status.Failed);
} else if (successfulConstraintResults.length !== data.constraintResponses.length) {
showFailureToast('Constraints Partially Checked');
rawCheckConstraintsStatus.set(Status.Failed);
} else {
showSuccessToast('All Constraints Checked');
rawCheckConstraintsStatus.set(Status.Complete);
}

if (failedConstraintResponses.length > 0) {
Expand All @@ -472,6 +464,7 @@ const effects = {
throw Error('Plan is not defined.');
}
} catch (e) {
checkConstraintsQueryStatus.set(Status.Failed);
catchError('Check Constraints Failed', e as Error);
showFailureToast('Check Constraints Failed');
}
Expand Down

0 comments on commit c2ca5fb

Please sign in to comment.