Skip to content

Commit a5251b6

Browse files
committed
fixing ability to do numeric/date entry + NullValueMenu
1 parent 569b947 commit a5251b6

File tree

1 file changed

+79
-39
lines changed

1 file changed

+79
-39
lines changed

lib/Validator.js

+79-39
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,13 @@ class Validator {
5757
if (!todos || !todos.length) {
5858
return;
5959
}
60+
// todos is an array of strings.
61+
const slotType = this.getSlotType(slotDefinition);// LinkML schema.type object
6062

61-
const slotType = this.getSlotType(slotDefinition);
62-
63+
// Slot type could be:
64+
// a number, string, date ...
65+
// null - if it is only a menu
66+
// both date and NullValueList
6367
if (slotType?.uri === 'xsd:date') {
6468
for (const todo of todos) {
6569
if (todo.substring(0, 2) === '>=') {
@@ -70,16 +74,11 @@ class Validator {
7074
}
7175
}
7276

73-
for (const def of slotDefinition.any_of || []) {
74-
processTodos(def, todos);
75-
}
76-
for (const def of slotDefinition.all_of || []) {
77-
processTodos(def, todos);
78-
}
79-
for (const def of slotDefinition.exactly_one_of || []) {
80-
processTodos(def, todos);
81-
}
82-
for (const def of slotDefinition.none_of || []) {
77+
// Cycle through each slotDefinition any_of etc. object entries and get
78+
// the datatype of its .range (or recurse) and in LinkML fashion attach
79+
// minimum_value and maximum_value to the slotDefinition OR its any_of
80+
// etc array BASED ON top level todos. E.g. inheriting min/max criteria.
81+
for (const def of slotDefinition.any_of ?? slotDefinition.all_of ?? slotDefinition.exactly_one_of ?? slotDefinition.none_of ?? []) {
8382
processTodos(def, todos);
8483
}
8584
};
@@ -94,13 +93,15 @@ class Validator {
9493
*/
9594
this.#dependantMinimumValuesMap = new Map();
9695
this.#dependantMaximumValuesMap = new Map();
96+
const RE_TODOS = new RegExp(/^([><])={(.*?)}$/);
97+
9798
for (const slotDefinition of Object.values(this.#targetClassInducedSlots)) {
9899
const { todos } = slotDefinition;
99100
if (!todos || !todos.length) {
100101
continue;
101102
}
102103
for (const todo of todos) {
103-
const match = todo.match(/^([><])={(.*?)}$/);
104+
const match = todo.match(RE_TODOS);
104105
if (match == null) {
105106
continue;
106107
}
@@ -140,7 +141,7 @@ class Validator {
140141
this.#valueValidatorMap = new Map();
141142
}
142143

143-
/* This returns a single primitve data type for a slot - a decimal, date,
144+
/* This returns a single primitive data type for a slot - a decimal, date,
144145
string etc. or possibly an enumeration. Enumerations are handled
145146
separately however (by const slotEnum = ...). Slots either use "range"
146147
attribute, OR they use any_of or exactly_one_of etc. attribute expression
@@ -164,6 +165,12 @@ class Validator {
164165
return slotType;
165166
}
166167

168+
/* A validation function is prepared and cached for every slot presented, so
169+
that validation throug rows of data can make use of already established
170+
validator for each column. Particular columns may have sensitivity to other
171+
column values (via min/max references) or special {today} e.g. so
172+
173+
*/
167174
getValidatorForSlot(slot, options = {}) {
168175
const { cacheKey, inheritedRange } = options;
169176
if (typeof cacheKey === 'string' && this.#valueValidatorMap.has(cacheKey)) {
@@ -177,11 +184,13 @@ class Validator {
177184
slotDefinition = slot;
178185
}
179186

187+
// This digs down into any_of, all_of etc to find first date, number etc.
188+
// slotType is LinkML schema.type object
180189
if (!slotDefinition.range && inheritedRange) {
181190
slotDefinition.range = inheritedRange;
182191
}
183192

184-
const slotType = this.getSlotType(slotDefinition);
193+
const slotType = this.getSlotType(slotDefinition); // LinkML schema.type object
185194

186195
const slotEnum = this.#schema.enums?.[slotDefinition.range];
187196
const slotPermissibleValues = Object.values(
@@ -194,11 +203,13 @@ class Validator {
194203
// TEST CASE:
195204
// if (slotDefinition.name == "sample_collection_date")
196205
// console.log("any_of", DEBUG INFO)
206+
// inheritedRange comes from original slot, so it might be a date or number + menu
197207
const anyOfValidators = (slotDefinition.any_of ?? []).map((subSlot) =>
198208
this.getValidatorForSlot(subSlot, {
199209
inheritedRange: slotDefinition.range,
200210
})
201211
);
212+
202213
const allOfValidators = (slotDefinition.all_of ?? []).map((subSlot) =>
203214
this.getValidatorForSlot(subSlot, {
204215
inheritedRange: slotDefinition.range,
@@ -239,6 +250,7 @@ class Validator {
239250
if (!value) {
240251
if (slotDefinition.required)
241252
return 'This field is required';
253+
// value_presence is subject to dynamic rules?
242254
if (slotDefinition.value_presence === 'PRESENT')
243255
return 'Value is not present';
244256
return;
@@ -266,39 +278,62 @@ class Validator {
266278
splitValues = [value];
267279
}
268280

281+
// For any of the value(s), whether they are valid depends on either
282+
// an ok primitive data type parsing, OR a categorical menu match.
283+
// Message needs
269284
for (const value of splitValues) {
270-
if (slotType) {
285+
let parse_error = false;
286+
if (slotType) {// Doesn't pertain to slots which are ONLY enumerations.
271287
const parsed = this.#parser.parse(value, slotType.uri);
272288

289+
// Issue: parse can fail on decimal but menu has "Missing"
273290
if (parsed === undefined) {
274-
return `Value does not match format for ${slotType.uri}`;
275-
}
291+
parse_error = `Value does not match format for ${slotType.uri}`;
276292

277-
if (slotMinimumValue !== undefined && parsed < slotMinimumValue) {
278-
return 'Value is less than minimum value';
293+
//if (!(anyOfValidators.length || allOfValidators.length || exactlyOneOfValidators.length || noneOfValidators.length)) {
294+
// return parse_error;
295+
//}
279296
}
297+
// All these cases have encountered an item which matches basic data
298+
// datatype and so sudden death is ok.
299+
else {
280300

281-
if (slotMaximumValue !== undefined && parsed > slotMaximumValue) {
282-
return 'Value is greater than maximum value';
283-
}
301+
if (slotMinimumValue !== undefined && parsed < slotMinimumValue) {
302+
return 'Value is less than minimum value';
303+
}
304+
305+
if (slotMaximumValue !== undefined && parsed > slotMaximumValue) {
306+
return 'Value is greater than maximum value';
307+
}
308+
309+
if (
310+
(slotDefinition.equals_string !== undefined &&
311+
parsed !== slotDefinition.equals_string) ||
312+
(slotDefinition.equals_number !== undefined &&
313+
parsed !== slotDefinition.equals_number)
314+
) {
315+
return 'Value does not match constant';
316+
}
284317

285-
if (
286-
(slotDefinition.equals_string !== undefined &&
287-
parsed !== slotDefinition.equals_string) ||
288-
(slotDefinition.equals_number !== undefined &&
289-
parsed !== slotDefinition.equals_number)
290-
) {
291-
return 'Value does not match constant';
292-
}
318+
if (
319+
slotDefinition.pattern !== undefined &&
320+
!value.match(slotDefinition.pattern)
321+
) {
322+
return 'Value does not match pattern';
323+
}
293324

294-
if (
295-
slotDefinition.pattern !== undefined &&
296-
!value.match(slotDefinition.pattern)
297-
) {
298-
return 'Value does not match pattern';
325+
// Here slotType value tested and is ok!
326+
continue;
299327
}
328+
329+
// Here value didn't parse to slotType
330+
331+
}
332+
else {
333+
// No basic slot type here so only enumeration handling.
300334
}
301335

336+
// Single range for slot given so no need to evaluate all_of, any_of etc.
302337
if (slotEnum && !slotPermissibleValues.includes(value)) {
303338
return 'Value is not allowed';
304339
}
@@ -342,12 +377,17 @@ class Validator {
342377
return 'One or more expressions of none_of held';
343378
}
344379
}
380+
381+
if (anyOfValidators.length || allOfValidators.length || exactlyOneOfValidators.length || noneOfValidators.length) {
382+
// We passed validation here which means a parse error can be overriden
383+
}
384+
else if (parse_error.length) {
385+
//There were no other ranges besides basic slotType so
386+
return parse_error;
387+
}
345388
}
346389
};
347390

348-
if (typeof cacheKey === 'string') {
349-
this.#valueValidatorMap.set(cacheKey, validate);
350-
}
351391

352392
return validate;
353393
}

0 commit comments

Comments
 (0)