Hi @spencer !
My name’s Molly and I’m the lead on Dynamic Scheduling. Happy to help!!
Below are details about Search Functions and two corresponding examples. Let me know if this is helpful :). If you are still running into issues, please feel free to also send your code (anonymized if there is any revealing information) and I can take a look.
Best,
Molly
INPUT AND OUTPUT
INPUT:
Search functions can be triggered from a puck’s right click menu or from a row’s (or a blank space in time) right click menu. Where you call this search function determines its input structure.
- If you are calling it from a puck’s right click menu, you must pass in
puck
as an input. The input variable must be called puck
exactly. : suggestionFunctionName(puck: <insertObjectTypeAPINameHere>)
.
- If you are calling it from a row’s right click menu, you must pass in the
rowId
and selectedTime
as inputs. The input variables must be called rowId
and selectedTime
exactly: suggestionFunctionName(rowId: string, selectedTime: Timestamp)
.
OUTPUT:
Search functions, regardless of if called from a row right click or puck right click, can RETURN slots of time to highlight and/or pucks to highlight.
See docs for the output interface. See examples below for a function that returns just time slots and a function that returns just pucks to highlight.
Here are two examples
EXAMPLE 1:
- This function is triggered from a puck’s right click menu and returns highlighted time slots.
- Description of function: The search function helps find where an order can be rescheduled by factoring in [1] that material being produced on that that order and [2] the scheduled start date. To be considered a viable option the a production line must produce that material as defined by a property on the production line object. Additionally it must have capacity to produce the order which is determined by comparing the production line’s capacity to the total number of orders currently scheduled to occur on that line for the day provided by the “triggering” order.
- Function:
@Function()
public searchValidSlotsForOrder(puck: LegolasProductionOrder): ISearchResult {
const materialType = puck.material.get()?.materialType;
if (!materialType) {
throw new UserFacingError("Linked material for order has no material type");
}
if (!puck.plantId) {
throw new UserFacingError("Order has no plant");
}
if (!puck.scheduledStartDate) {
throw new UserFacingError("Order has no scheduled start date");
}
if (!puck.durationMs) {
throw new UserFacingError("Order has no duration");
}
const linesSpec = Objects.search().legolasProductionLine().filter(l => l.lineMaterialTypes.contains(materialType));
const lines = linesSpec.all();
const orders = linesSpec.searchAroundProductionOrder()
.filter(o => o.plantId.exactMatch(puck.plantId!))
.filter(o => o.scheduledStartDate.exactMatch(puck.scheduledStartDate!))
.all();
const ordersByLineId = groupBy(orders, o => o.productionLineId);
const slots: IHighlight[] = [];
lines.forEach(l => {
const totalAfterMove = 1 + (ordersByLineId[l.lineId]?.length ?? 0);
if (totalAfterMove <= (l.lineCapacity ?? Infinity)) {
slots.push({
type: HighlightType.SLOT,
domain: {
start: puck.scheduledStartDate!.valueOf(),
end: puck.scheduledStartDate!.valueOf() + puck.durationMs!,
},
containerId: l.lineId,
});
}
});
return {
rowGroup: {
title: "Recommended Slots",
containerIds: slots.map(s => s.containerId!),
highlights: slots,
}
};
}
EXAMPLE 2:
- This function is triggered from a row (or blank space)'s right click menu and returns puck(s).
- Description: This search function helps finds what events can be assigned to a individual by factoring in [1] the time of the slot and [2] the individual’s topics of interests. To be considered a viable option an event must be at the same time as the triggering slot and be on one of the topics of interests.
- Function:
@Function()
public breakoutSessionMatchSearch(rowId: string, selectedTime: Timestamp): ISearchResultV2 {
const attendee = Objects.search().conferenceAttendee().filter(a => a.attendeeId.exactMatch(rowId)).all()[0];
const highlights: IHighlight[] = [];
if (attendee && attendee.attendeeInterests) {
const preferences = attendee.attendeeInterests!.split(',').map(p => p.trim());
const scheds = Objects.search()
.conferenceEvent().filter(e => e.topic.contains(...preferences))
.searchAroundConferenceSchedule().filter(s => Filters.not(s.attendeeId.hasProperty()))
.all();
const uniqueOptions = uniqBy(scheds, s => s.eventId);
uniqueOptions.forEach(s => {
highlights.push({
type: "PUCK",
puckId: `${s.typeId}::${s.scheduleId}`,
});
});
}
return {
rowGroup: {
title: `Breakout event matches for ${rowId}`,
containerIds: highlights.length > 0 ? [rowId, ""] : [],
highlights: highlights,
},
}
}