Skip to content

Commit 487c23b

Browse files
committed
redundent class_assignment -> template_name
+ new isEmpty(obj) function
1 parent ec8c1f9 commit 487c23b

File tree

5 files changed

+227
-98
lines changed

5 files changed

+227
-98
lines changed

lib/AppContext.js

+98-19
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
Template,
88
getTemplatePathInScope,
99
} from '../lib/utils/templates';
10-
import { wait } from '../lib/utils/general';
10+
import { wait, isEmpty } from '../lib/utils/general';
1111
import { invert, removeNumericKeys, consolidate } from '../lib/utils/objects';
1212
import { createDataHarmonizerContainer, createDataHarmonizerTab } from '../web';
1313
import { getExportFormats } from 'schemas';
@@ -39,7 +39,7 @@ export default class AppContext {
3939
$(document).on('dhTabChange', (event, data) => {
4040
console.info(
4141
'dhTabChange',
42-
this.getCurrentDataHarmonizer().class_assignment,
42+
this.getCurrentDataHarmonizer().template_name,
4343
'->',
4444
data.specName
4545
);
@@ -278,7 +278,7 @@ export default class AppContext {
278278
// HoT settings.
279279
const dh = new DataHarmonizer(dhSubroot, this, {
280280
loadingScreenRoot: document.body,
281-
class_assignment: class_name,
281+
template_name: class_name,
282282
schema: schema, // assign during constructor so validator can init on it.
283283
hot_override_settings: {
284284
minRows: is_child ? 0 : 10,
@@ -291,7 +291,9 @@ export default class AppContext {
291291
});
292292

293293
data_harmonizers[class_name] = dh;
294-
dh.useTemplate(class_name);
294+
dh.template = schema.classes[class_name];
295+
// Set up the data structure based on LinkML class
296+
dh.useTemplate(class_name);
295297
dh.validator.useTargetClass(class_name);
296298

297299
if (is_child) {
@@ -354,6 +356,7 @@ export default class AppContext {
354356
? this.template.localized.schema
355357
: this.template.default.schema;
356358

359+
// FUTURE slot_ptr version of self.context.relations for efficiency?
357360
this.relations = this.crudGetRelations(schema);
358361

359362
// Merges any existing dataharmonizer instances with the ones newly created.
@@ -409,6 +412,13 @@ export default class AppContext {
409412
[slot_name]: [foreign_slot_name],
410413
[slot_name_2]: [foreign_slot_name_2]...
411414
}
415+
},
416+
dependent_slots: {
417+
[slot_name]: {
418+
[foreign_class]: {foreign_slot_name]}...
419+
}
420+
unique_key_slots: {
421+
[slot_name]: {[unique_key_name]:true, ...
412422
}
413423
}
414424
@@ -431,15 +441,17 @@ export default class AppContext {
431441
crudGetRelations(schema) {
432442
let relations = {};
433443
Object.entries(schema.classes).forEach(([class_name, class_obj]) => {
444+
relations[class_name] = {};
445+
434446
Object.entries(class_obj.attributes ?? {}).forEach(([slot_name, attribute]) => {
435447
if (attribute.annotations?.foreign_key?.value) {
436-
437448
let foreign_class, foreign_slot_name;
438449
let key = attribute.annotations.foreign_key.value;
439-
// Best syntax is that foreign class is dot prefix:
450+
// FIRST, TRY GETTING PARENT CLASS VIA foreign class with dot prefix:
440451
if (key.includes('.'))
441452
[foreign_class, foreign_slot_name] = key.split('.',2);
442-
// UNTESTED: Workaround is that foreign class is in range of slot:
453+
// ALTERNATELY (UNTESTED) USE range of slot having reference to a class:
454+
// NOTE: this doesn't handle multiple classes in range.
443455
else if (attribute.range in schema.classes) {
444456
foreign_slot_name = key;
445457
foreign_class = attribute.range;
@@ -448,11 +460,24 @@ export default class AppContext {
448460
console.log("Class", class_name, "has slot", slot_name, "foreign key", attribute.annotations?.foreign_key?.value, "but no target class information in key or slot range.");
449461
return;
450462
}
451-
Object.assign(relations, {[class_name]: {parent: {[foreign_class]: {[slot_name]: foreign_slot_name}}}});
452-
//And reverse relation
453-
Object.assign(relations, {[foreign_class]: {child: {[class_name]: {[foreign_slot_name]: slot_name}}}});
463+
Object.assign(relations[class_name],
464+
{parent: {[foreign_class]: {[slot_name]: foreign_slot_name}}});
465+
// And reverse relation
466+
Object.assign(relations[foreign_class],
467+
{child: {[class_name]: {[foreign_slot_name]: slot_name}}});
468+
// And dependent slots
469+
Object.assign(relations[class_name],
470+
{dependent_slots: {[slot_name]: {parent: foreign_class, slot: foreign_slot_name}}});
454471
}
455472
});
473+
474+
// Now do unique keys in class_obj
475+
Object.entries(class_obj.unique_keys ?? {}).forEach(([key_name, key_obj]) => {
476+
Object.entries(key_obj.unique_key_slots ?? {}).forEach(([index, slot_name]) => {
477+
Object.assign(relations[class_name],
478+
{unique_key_slots: {[slot_name]: {[key_name]: true}}});
479+
});
480+
});
456481
});
457482
return relations;
458483
}
@@ -481,27 +506,42 @@ export default class AppContext {
481506
return this.crudIsAncestor(class_names, ancestor_name)
482507
}
483508

484-
/* Retrieves ordered list of tables that ultimately have given template as
509+
/** Retrieves ordered list of tables that ultimately have given template as
485510
* a foreign key. Whether it is to enact cascading visibility, update or
486511
* deletion events, this provides the order in which to trigger changes.
487512
* Issue is that intermediate tables need to be checked in order due to
488513
* dependencies by foreign keys. If a table depended on an intermediate that
489514
* hadn't been refreshed, we'd get a wrong display.
490515
* Relying on javascript implicit ordering of dictionary added elements.
516+
* @param {String} class_names initial value
517+
* @param {Array} class_names array of relations
518+
* @return {Object} class_names where each descendent class_name is mentioned.
491519
*/
492520
crudGetDependents(class_names) {
493-
// Initialization case
494-
if (typeof class_names == "string") {
495-
class_names = {...this.crudGetChildren(class_names)};
521+
// Initialization case gets single class_name
522+
if (class_names && typeof class_names == "string") {
523+
class_names = this.crudGetChildren(class_names);
524+
console.log("class_names", class_names);
525+
if (!class_names)
526+
return {};
527+
}
528+
let children = {};
529+
for (const dependent_name in class_names) {
530+
children = {...children, ...this.crudGetChildren(dependent_name) }
496531
}
497-
let children = this.crudGetChildren(class_names);
498-
if (children) {
499-
class_names = {...children};
500-
return this.crudGetDependents(class_names);
532+
if (children.length >0) {
533+
return {...class_names, ...children, ...this.crudGetDependents(children)};
501534
}
502535
return class_names;
503536
}
504537

538+
crudCheckDependency (self, row, dependent_name, changes) {
539+
let keyVals = {};
540+
let change_log = '';
541+
let found = false;
542+
return [found, keyVals, change_log];
543+
}
544+
505545
/* For given class, refresh view of all dependent tables that have a direct
506546
* or indirect foreign key relationship to given class.
507547
* Performance might show up as an issue later if lots of long dependent
@@ -522,7 +562,10 @@ export default class AppContext {
522562
* primary key selection has been made!
523563
*/
524564
crudFilterByForeignKey(class_name) {
525-
const hotInstance = this.dhs[class_name].hot;
565+
const hotInstance = this.dhs[class_name]?.hot;
566+
// This can happen when one DH is rendered but not other dependents yet.
567+
if (!hotInstance)
568+
return;
526569
const hiddenRowsPlugin = hotInstance.getPlugin('hiddenRows');
527570

528571
// Ensure all rows are visible
@@ -742,4 +785,40 @@ export default class AppContext {
742785
});
743786
}
744787

788+
/** Determine if given dh has a complete set of unique key slot values as
789+
* given by key_name.
790+
* @param {Object} dh instance
791+
* @param {String} key_name of
792+
* @param {Object} changes for a row with slot_name as key.
793+
* @return {Array} of:
794+
* @return {Boolean} found boolean true if complete keyVals
795+
* @return {Object} keyVals set of slots and their values from template or change
796+
* @return {String} change_log report of changed slot values
797+
*/
798+
crudHasUniqueKey(dh, row, key_name, changes, keyVals={}, change_log='') {
799+
const key_obj = dh.template.unique_keys[key_name];
800+
let found = Object.entries(key_obj.unique_key_slots).every(([index, slot_name]) => {
801+
// Key has a changed value (incl. null?), so update it.
802+
if (slot_name in changes) {
803+
let change = changes[slot_name];
804+
change_log += `* [${slot_name}] change to "${change.value}"\n`;
805+
keyVals[slot_name] = change.value;
806+
return true;
807+
}
808+
else {
809+
let col = dh.getColumnIndexByFieldName(slot_name);
810+
let value = dh.hot.getDataAtCell(row, col);
811+
if (value != null) {
812+
keyVals[key_name] = value;
813+
return true;
814+
}
815+
}
816+
817+
// Key value hasn't changed, and is missing a value, so
818+
// unique_key is not established, so user's change can go
819+
// through.
820+
return false;
821+
});
822+
return [found, keyVals, change_log];
823+
}
745824
}

0 commit comments

Comments
 (0)