Skip to content

Commit 080d35e

Browse files
author
Andrey Leybovich
committed
fixed errors on empty description and double quotes in description
1 parent 1c1474d commit 080d35e

File tree

2 files changed

+105
-68
lines changed

2 files changed

+105
-68
lines changed

rules/aliquoting_rules.yaml

+98-63
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,43 @@
11
conditions:
2-
check_powder_protocols:
2+
create_products:
3+
description: Create products for order items in state "pending"
34
default: true
5+
check: |
6+
function() {
7+
debug(context, "create_products check")
8+
return true
9+
}
10+
true:
11+
action: |
12+
function() {
13+
pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending')
14+
15+
context.Products = pendingOrderItems.map(oi => ({
16+
Ref: oi.Ref,
17+
Rank: oi.Rank,
18+
OrderItemRef: oi.Ref,
19+
OrderType: oi.OrderType,
20+
ProductType: oi.ProductType,
21+
Amount: oi.Amount,
22+
Concentration: oi.Concentration,
23+
Solvent: oi.Solvent,
24+
State: 'pending'
25+
}));
26+
}
27+
next: check_powder_protocols
28+
check_powder_protocols:
429
description: Check if there are any powder protocols among the products.
530
check: |
631
function () {
7-
if (debug) ( debug(context, "I'm in check_powder_protocols") )
8-
return context.OrderItems.find(p => p.ProductType === 'powder') !== undefined;
32+
return context.Products.find(p => p.ProductType === 'powder') !== undefined;
933
}
1034
true:
1135
description: Fail all powder products and their corresponding order items.
1236
action: |
1337
function () {
14-
const powderOrderItems = context.OrderItems.filter(p => p.ProductType === 'powder');
15-
powderOrderItems.forEach(p => {
38+
const powderProducts = context.Products.filter(p => p.ProductType === 'powder');
39+
powderProducts.forEach(p => {
1640
p.State = 'fail';
17-
const orderItem = context.OrderItems.find(oi => oi.Ref === p.OrderItemRef);
18-
if (orderItem) {
19-
orderItem.State = 'fail';
20-
}
2141
});
2242
}
2343
next: check_mixed_solvents
@@ -27,21 +47,41 @@ conditions:
2747
description: Check if there are mixed solvents or concentrations among the solution order items.
2848
check: |
2949
function () {
30-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
31-
const primaryOrderItem = pendingOrderItems.find(oi => oi.OrderType === 'primary');
32-
const earlySolutionOrderItems = pendingOrderItems.filter(oi => oi.OrderType !== 'primary' && oi.ProductType === 'solution');
33-
return earlySolutionOrderItems.find(oi => oi.Solvant !== primaryOrderItem.Solvant || oi.Concentration !== primaryOrderItem.Concentration) !== undefined;
50+
// get all pending products
51+
52+
const pendingProducts = context.Products.filter(p => p.State === 'pending');
53+
if (pendingProducts == null) {
54+
return false
55+
}
56+
57+
// take solvent and concentration or order item with order type primaryOrderItem
58+
primaryProduct = context.Products.find(oi => oi.OrderType === 'primary') ?? pendingProducts[0]
59+
60+
concentration = primaryProduct.Concentration
61+
solvent = primaryProduct.Solvent
62+
63+
// check if there are any order items with different solvent or concentration
64+
return pendingProducts.find(p => p.Solvent !== solvent || p.Concentration !== concentration) !== undefined;
3465
}
3566
true:
3667
description: Fail order items and products with mixed solvents or concentrations.
3768
action: |
3869
function () {
39-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
40-
const primaryOrderItem = pendingOrderItems.find(oi => oi.OrderType === 'primary');
41-
const failedOrderItems = pendingOrderItems.filter(oi => oi.OrderType !== 'primary' && (oi.Solvant !== primaryOrderItem.Solvant || oi.Concentration !== primaryOrderItem.Concentration));
42-
failedOrderItems.forEach(oi => oi.State = 'fail');
43-
const failedProducts = context.Products.filter(p => failedOrderItems.find(oi => oi.Ref === p.OrderItemRef) !== undefined);
44-
failedProducts.forEach(p => p.State = 'fail');
70+
// get all pending products
71+
const pendingProducts = context.Products.filter(p => p.State === 'pending');
72+
73+
// take solvent and concentration or order item with order type primaryOrderItem
74+
primaryOrderItem = context.OrderItems.find(oi => oi.OrderType === 'primary') ?? context.OrderItems[0]
75+
76+
concentration = primaryOrderItem[0].Concentration
77+
solvent = primaryOrderItem[0].Solvent
78+
79+
// set state to failed for all products with different solvent or concentration
80+
pendingProducts.forEach(p => {
81+
if (p.Solvent !== solvent || p.Concentration !== concentration) {
82+
p.State = 'fail';
83+
}
84+
});
4585
}
4686
next: check_overflow
4787
false:
@@ -50,16 +90,15 @@ conditions:
5090
description: Check if the total required amount exceeds the container amount.
5191
check: |
5292
function () {
53-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
54-
const totalRequiredAmount = pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
93+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
94+
const totalRequiredAmount = pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
5595
return totalRequiredAmount > context.Container.Amount;
5696
}
5797
true:
5898
description: Fail all products and order items due to container overflow.
5999
action: |
60100
function () {
61101
context.Products.forEach(p => p.State = 'fail');
62-
context.OrderItems.forEach(oi => oi.State = 'fail');
63102
}
64103
terminate: true
65104
false:
@@ -68,24 +107,27 @@ conditions:
68107
description: Check if the actual amount is less than the required amount.
69108
check: |
70109
function () {
71-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
72-
const totalRequiredAmount = pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
110+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
111+
const totalRequiredAmount = pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
73112
const diff = context.Container.Amount - totalRequiredAmount;
74113
return diff < 0;
75114
}
76115
true:
77116
description: Fail the lowest-ranked order items and their corresponding products until the remaining amount can be fulfilled.
78117
action: |
79118
function () {
80-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
81-
const totalRequiredAmount = pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
82-
const diff = context.Container.Amount - totalRequiredAmount;
83-
const sortedOrderItems = pendingOrderItems.sort((a, b) => a.Ranking - b.Ranking);
84-
let remainingAmount = -diff;
85-
for (const orderItem of sortedOrderItems) {
86-
if (remainingAmount >= orderItem.Amount) {
87-
remainingAmount -= orderItem.Amount;
88-
orderItem.State = 'fail';
119+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
120+
const totalRequiredAmount = pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
121+
const diff = totalRequiredAmount - context.Container.Amount;
122+
123+
// sort products by rank in ascending order, so we can fail the lowest-ranked ones first
124+
// (rank 1 failed first, rank 2 failed second, etc.)
125+
const sortedProducts = pendingProducts.sort((a, b) => a.Ranking - b.Ranking);
126+
127+
for (const p of sortedProducts) {
128+
if (diff > 0) {
129+
diff -= p.Amount;
130+
p.State = 'fail';
89131
} else {
90132
break;
91133
}
@@ -94,39 +136,27 @@ conditions:
94136
# After failing some order items, we might have a certain amount left over
95137
# It might be less than required by any failed order item, but still usable for a spare
96138
next: check_leftovers
97-
false:
98-
next: check_amount_equal_to_required
99-
check_amount_equal_to_required:
100-
description: Check if the actual amount is equal to the required amount.
101-
check: |
102-
function () {
103-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
104-
const totalRequiredAmount = pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
105-
const diff = context.Container.Amount - totalRequiredAmount;
106-
return diff === 0;
107-
}
108-
true:
109-
terminate: true
110139
false:
111140
next: check_amount_more_than_required
112141
check_amount_more_than_required:
113142
description: Check if the actual amount is more than the required amount.
114143
check: |
115144
function () {
116-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
117-
const totalRequiredAmount = pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
145+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
146+
const totalRequiredAmount = pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
118147
return context.Container.Amount - totalRequiredAmount > 0;
119148
}
120149
true:
121150
next: check_remainder_less_than_50
122151
false:
152+
# Amount equals to required if we got here
123153
terminate: true
124154
check_leftovers:
125155
description: Check if there are any non-consumed leftovers.
126156
check: |
127157
function () {
128-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
129-
return context.Container.Amount > pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
158+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
159+
return context.Container.Amount > pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
130160
}
131161
true:
132162
next: check_remainder_less_than_50
@@ -136,8 +166,8 @@ conditions:
136166
description: Check if the remainder is less than 50 μl.
137167
check: |
138168
function () {
139-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
140-
const totalRequiredAmount = pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
169+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
170+
const totalRequiredAmount = pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
141171
const remainder = context.Container.Amount - totalRequiredAmount;
142172
return remainder < 50;
143173
}
@@ -149,22 +179,24 @@ conditions:
149179
description: Check if the remainder is between 50 μl and 950 μl.
150180
check: |
151181
function () {
152-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
153-
const totalRequiredAmount = pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
182+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
183+
const totalRequiredAmount = pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
154184
const remainder = context.Container.Amount - totalRequiredAmount;
155185
return remainder >= 50 && remainder < 950;
156186
}
157187
true:
158188
description: Create a spare tube with the remaining amount.
159189
action: |
160190
function () {
161-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
162-
const remainder = context.Container.Amount - pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
191+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
192+
const remainder = context.Container.Amount - pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
163193
const newProduct = {
164194
Ref: context.Products.length + 1,
165195
OrderItemRef: null,
166196
ProductType: 'solution',
167197
Amount: remainder,
198+
Concentration: pendingProducts[0].Concentration,
199+
Solvent: pendingProducts[0].Solvent,
168200
State: 'pending'
169201
};
170202
context.Products.push(newProduct);
@@ -176,29 +208,34 @@ conditions:
176208
description: Check if the remainder is between 950 μl and 1800 μl.
177209
check: |
178210
function () {
179-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
180-
const totalRequiredAmount = pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
211+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
212+
const totalRequiredAmount = pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
181213
const remainder = context.Container.Amount - totalRequiredAmount;
182214
return remainder >= 950 && remainder <= 1800;
183215
}
184216
true:
185217
description: Create two spare tubes, one with 900 μl and another with the remaining amount.
186218
action: |
187219
function () {
188-
const pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
189-
const remainder = context.Container.Amount - pendingOrderItems.reduce((sum, oi) => sum + oi.Amount, 0);
220+
const pendingProducts = context.Products.filter(oi => oi.State === 'pending');
221+
const totalRequiredAmount = pendingProducts.reduce((sum, oi) => sum + oi.Amount, 0);
222+
const remainder = context.Container.Amount - totalRequiredAmount;
190223
const newProduct1 = {
191224
Ref: context.Products.length + 1,
192225
OrderItemRef: null,
193226
ProductType: 'solution',
194227
Amount: 900,
228+
Concentration: pendingProducts[0].Concentration,
229+
Solvent: pendingProducts[0].Solvent,
195230
State: 'pending'
196231
};
197232
const newProduct2 = {
198233
Ref: context.Products.length + 2,
199234
OrderItemRef: null,
200235
ProductType: 'solution',
201236
Amount: remainder - 900,
237+
Concentration: pendingProducts[0].Concentration,
238+
Solvent: pendingProducts[0].Solvent,
202239
State: 'pending'
203240
};
204241
context.Products.push(newProduct1, newProduct2);
@@ -208,8 +245,6 @@ conditions:
208245
description: Fail all order items with comment
209246
action: |
210247
function () {
211-
pendingOrderItems = context.OrderItems.filter(oi => oi.State === 'pending');
212-
pendingOrderItems.forEach(ppi => ppi.State = "fail")
213-
context.Products = []
248+
context.Products.filter(p => p.State === 'pending').forEach(p => p.State = "fail")
214249
}
215250
terminate: true

static/convert.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ function convertYamlToMermaid(yamlInput) {
99

1010
function generateConditionDeclaration(conditionName, description) {
1111
if (declaredElements[conditionName]) return "";
12-
if (description) return ` ${conditionName}{"\`${description}\`"}\n`;
13-
return ""
12+
return ` ${conditionName}{"\`${sanitize(description) ?? conditionName}\`"}\n`;
1413
}
1514

1615
function generateActionDeclaration(actionName, description) {
1716
if (declaredElements[conditionName]) return "";
18-
if (description) return ` ${actionName}["\`${description}\`"]\n`;
19-
return ""
17+
return ` ${actionName}["\`${sanitize(description) ?? actionName}\`"]\n`;
2018
}
2119

2220
function generateTerminateDeclaration(terminateName) {
@@ -72,7 +70,7 @@ function convertYamlToMermaid(yamlInput) {
7270
mermaidCode += generateDecision(conditionName, condition.true, 'true');
7371
mermaidCode += generateDecision(conditionName, condition.false, 'false');
7472

75-
// rememebr metadata
73+
// remember metadata
7674
metadata[conditionName] = { func: condition.Check, type: "condition" };
7775
if (condition.true && condition.true.action) {
7876
metadata[conditionName + '_true'] = {func: condition.true.action, type: "action", value: true};
@@ -83,4 +81,8 @@ function convertYamlToMermaid(yamlInput) {
8381
}
8482

8583
return mermaidCode;
84+
}
85+
86+
function sanitize(input) {
87+
return !input ? input : input.replaceAll(`"`, "&ampquot");
8688
}

0 commit comments

Comments
 (0)