I saw your several demos of dynamic scheduling where you enable the ability to pass in English version of rules so that they are parsed and applied in a dynamic scheduling workflow. That’s super impressive - well done.
I am curious to understand how you do the translation of the english prompt to the constraints in the workshop application.
I am guessing that you trigger a logic function that creates an interpretation of the rule and then adds that as a constraint.
Is the constraint added by API?
Do you create a function for each of the constraints?
Do you store the constraints into a dataset so that they get applied somehow?
+1 on this, would really appreciate documentation and/or a walkthrough video on how to get this done. Would significantly accelerate set-up time for the scheduling GANTT on my workflows.
Instead of creating individual rule functions you can have one large function that has several pre-defined rule types (deadlines, durations, gaps, etc). The rule function takes a rule object as an input – using the rule type property determine which logic it should apply and what parameters are needed when executing the function in the Scheduling Gantt.
You then use AIP Logic to take in user provided free text to create a rule object which includes the rule type (one of several pre-defined rules categories) and any relevant values that will need to be passed into rule function.
To add a bit more color, when you create a new rule in free text in the Workshop application, a Foundry Automate automation gets triggered which runs through the Logic function that then translates the English text into a JSON based on the predefined types and creates metadata objects. When loading the widget, it then calls this one large rule TypeScript function that loads in all these enabled metadata objects that were created to then evaluate them based on the current schedule. The function roughly looks like this:
@Function()
public evaluateAllRules(scheduleObjectPrimaryKeys: string[]): IRuleResult[] {
const definitions = Objects.search()
.ruleMetadataObjects()
.filter(r => r.enabled.isTrue())
.all();
const schedules = Objects.search()
.scheduleObjects()
.filter(s => s.uniqueId.exactMatch(...scheduleObjectPrimaryKeys))
.all();
// ...
const resultsByScheduleId: { [pk: string]: {
scheduleObjectPrimaryKey: string,
result: boolean | undefined,
details: Array<{ description: string; relatedPuckIds: string[] }>
} } = {};
scheduleObjectPrimaryKeys.forEach(pk => {
resultsByScheduleId[pk] = {
scheduleObjectPrimaryKey: pk,
result: undefined,
details: [],
}
})
definitions.forEach(d => {
// preprocessing
if (d.type === "deadline") {
// some logic here to evaluate rule and then add result into the `resultsByScheduleId` output map
} else if (d.type === "duration") {
// similar for all other defined types
}
// ...
});
return Object.values(resultsByScheduleId);
}
There’s also a README Notepad document in the directory once installed that has some more information + screenshots. Hope this helps but can help clarify anything else that’s still confusing!