Skip to content

Commit

Permalink
refactor out buildSchemaTree
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethbruskiewicz committed Oct 28, 2024
1 parent 60239dd commit e40aec7
Showing 1 changed file with 159 additions and 1 deletion.
160 changes: 159 additions & 1 deletion lib/AppContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,168 @@ import {
} from '@/lib/utils/templates';
import { wait } from '@/lib/utils/general';
import { invert, removeNumericKeys, consolidate } from '@/lib/utils/objects';
import { buildSchemaTree } from '@/lib/utils/1m_dep';
import { createDataHarmonizerContainer, createDataHarmonizerTab } from '@/web';

import { buildAppContext, setup1M } from '@/lib/utils/1m';

/**
* Finds the shared keys per class in a given schema.
* @param {Object} schema - The schema object with structure containing "classes" and "slot_usage".
* @returns {Object} An object mapping class names to an array of shared keys.
*/
function findSharedKeys(schema) {
const class_names = new Set(
Object.keys(schema.classes).filter(
(k) => k !== 'dh_interface' && k !== 'Container'
)
);
let shared_keys_per_class = {};
class_names.forEach((key) => {
shared_keys_per_class[key] = []; // initialize the array to avoid checking existence later

// Filtering and mapping slots
const class_data = schema.classes[key];
const slots = class_data.attributes;
const shared_keys = Object.values(slots).map((slot_usage) => {
return {
name: slot_usage.name,
related_concept: slot_usage.range,
relation: ['dh_interface', 'Container'].includes(key)
? 'child'
: class_names.has(slot_usage.range)
? 'parent'
: 'range',
};
});

shared_keys_per_class[key] = shared_keys; // assign mapped values

// Ensure there are no duplicates; if duplicates are meaningful, this will need adjustment
shared_keys.forEach((slot) => {
shared_keys_per_class[slot.related_class] =
shared_keys_per_class[slot.related_class] || []; // ensure array exists

if (
!shared_keys_per_class[slot.related_class].find(
(s) => s.name === slot.name
)
) {
shared_keys_per_class[slot.related_class].push(slot);
}
});

shared_keys_per_class[key] = shared_keys.filter(
(obj) => obj.related_concept && ['parent', 'child'].includes(obj.relation)
);
});

delete shared_keys_per_class[undefined];

return shared_keys_per_class;
}

function createFlatSchemaTree(classNames) {
return {
Container: {
tree_root: true,
children: [...classNames],
},
...classNames.reduce((acc, key) => {
acc[key] = {
name: key,
children: [],
shared_keys: [],
};
return acc;
}, {}),
};
}

function createPreSchemaTree(classNames, sharedKeysPerClass) {
const treeBase = {
Container: { tree_root: true, children: classNames },
};

return classNames.reduce((acc, classKey) => {
const sharedKeys = sharedKeysPerClass[classKey] || [];
acc[classKey] = {
name: classKey,
shared_keys: sharedKeys,
children: sharedKeys
.map((item) => item.range)
.filter((range) => range !== classKey && typeof range !== 'undefined'),
};
return acc;
}, treeBase);
}

function updateWithChildrenAndSharedKeys(data) {
// Use a deep clone to avoid mutating the original object
const result = JSON.parse(JSON.stringify(data));

Object.keys(result).forEach((key) => {
const elem = result[key];
(elem.shared_keys || []).forEach((sk) => {
if (sk.relation === 'parent' && result[sk.related_concept]) {
const parent = result[sk.related_concept];

// Ensure 'children' array exists
if (!parent.children) {
parent.children = [];
}

// Add key to parent’s 'children' array if not already present
if (!parent.children.includes(key)) {
parent.children.push(key);
}

// Ensure 'shared_keys' array exists
if (!parent.shared_keys) {
parent.shared_keys = [];
}

// Check if reciprocal shared key already exists
const reciprocalExists = parent.shared_keys.some(
(rsk) => rsk.name === sk.name && rsk.related_concept === key
);

// Add reciprocal shared key if it doesn't exist
if (!reciprocalExists) {
parent.shared_keys.push({
name: sk.name,
related_concept: key,
relation: 'child',
});
}
}
});
});

return result;
}

/**
* Builds a schema tree from the given schema.
* @param {Object} schema - The schema object containing "classes".
* @returns {Object|null} The schema tree object, or null if no "Container" classes are found.
*/
function buildSchemaTree(schema) {
const isContainerMissing = typeof schema.classes['Container'] === 'undefined';

const classNames = Object.keys(schema.classes).filter(
(key) => key !== 'dh_interface' && key !== 'Container'
);

if (isContainerMissing) {
return createFlatSchemaTree(classNames);
}

const sharedKeysPerClass = findSharedKeys(schema);
const preSchemaTree = createPreSchemaTree(classNames, sharedKeysPerClass);

return updateWithChildrenAndSharedKeys(preSchemaTree);
}

class AppConfig {
constructor(template_path = null) {
this.rootUrl = window.location.host;
Expand Down

0 comments on commit e40aec7

Please sign in to comment.