import { makeStyles } from "@material-ui/styles"; import { fontWeight } from "@mui/system"; /** * Evaluates the specified rule set on a row returned by the Neo4j driver. * @param record - a single result row produced by the Neo4j driver. * @param customization - the target customization (e.g. "text color") * @param defaultValue - the value to default to if no rules are met. * @param rules - a list of rules as produced by the rule-based styling screen. * @returns a user-defined value if a rule is met, or the default value if none are. */ export const evaluateRulesOnNeo4jRecord = (record, customization, defaultValue, rules) => { if (!record || !customization || !rules) { return defaultValue; } for (let [index, rule] of rules.entries()) { // Only look at rules relevant to the target customization. if (rule['customization'] == customization) { // if the row contains the specified field... if (record._fieldLookup[rule['field']] !== undefined) { const val = record._fields[record._fieldLookup[rule['field']]] const realValue = val && val['low'] ? val['low'] : val; const ruleValue = rule['value'] if (evaluateCondition(realValue, rule['condition'], ruleValue)) { return rule['customizationValue']; } } } } // If the rules have determined a value, return it, otherwise, return the default. return defaultValue; } /** * @deprecated - to be removed together with record mapper. * We translate the 'mapped' record back into its original using the mapping specified by the user. */ export const evaluateRulesOnMappedNeo4jRecord = (record, mapping, customization, defaultValue, rules) => { var tempRecord = {}; tempRecord['_fields'] = record['_fields']; tempRecord['_fieldLookup'] = {}; tempRecord['_fieldLookup'][mapping['index']] = record._fieldLookup['index'] tempRecord['_fieldLookup'][mapping['value']] = record._fieldLookup['value'] tempRecord['_fieldLookup'][mapping['key']] = record._fieldLookup['key'] tempRecord['keys'] = Object.values(mapping); return evaluateRulesOnNeo4jRecord(tempRecord, customization, defaultValue, rules); } /** * Evaluates the specified rule set on a dictionary of key/value pairs. * Returns the `index` of the rule that is satisfied. * * @param dict - a dictionary of key/value pairs. * @param rules - a list of rules as produced by the rule-based styling screen. * @param customizations - a list of customizations to look for. * @returns the index of the rule that is satisfied. */ export const evaluateRulesOnDict = (dict, rules, customizations) => { if (!dict || !rules) { return -1; } for (let [index, rule] of rules.entries()) { // Only check customizations that are specified if (customizations.includes(rule['customization'])) { // if the row contains the specified field... if (dict[rule['field']] !== undefined && dict[rule['field']] !== null) { const realValue = dict[rule['field']]['low'] ? dict[rule['field']]['low'] : dict[rule['field']]; const ruleValue = rule['value'] if (evaluateCondition(realValue, rule['condition'], ruleValue)) { return index; } } } } // If no rules are met, return not found (index=-1) return -1; } /** * Evaluates the specified rule set on a node object returned by the Neo4j driver. * @param node - the node representation returned by the Neo4j driver. * @param customization - the target customization (e.g. "node label color") * @param defaultValue - the value to default to if no rules are met. * @param rules - a list of rules as produced by the rule-based styling screen. * @returns a user-defined value if a rule is met, or the default value if none are. */ export const evaluateRulesOnNode = (node, customization, defaultValue, rules) => { if (!node || !customization || !rules) { return defaultValue; } for (let [index, rule] of rules.entries()) { // Only look at rules relevant to the target customization. if (rule['customization'] == customization) { // if the row contains the specified field... const label = rule['field'].split(".")[0]; const property = rule['field'].split(".")[1]; if (node.labels.includes(label)) { const realValue = node.properties[property] ? node.properties[property] : ""; const ruleValue = rule['value'] if (evaluateCondition(realValue, rule['condition'], ruleValue)) { return rule['customizationValue']; } } } } return defaultValue; } /** * @param realValue the value found in the real data returned by the query * @param condition the condition, one of [=,!=,<,<=,>,>=,contains]. * @param ruleValue the value specified in the rule. * @return whether the condition is met. */ const evaluateCondition = (realValue, condition, ruleValue) => { if (!ruleValue || !condition || !realValue) { // If something is null, rules are never met. return false; } if(!isNaN(parseFloat(ruleValue))){ ruleValue = parseFloat(ruleValue); } if (condition == "=") { return realValue == ruleValue; } if (condition == "!=") { return realValue !== ruleValue; } if (condition == "<=") { return realValue <= ruleValue; } if (condition == "<") { return realValue < ruleValue; } if (condition == ">=") { return realValue >= ruleValue; } if (condition == ">") { return realValue > ruleValue; } if (condition == "contains") { return realValue.toString().includes(ruleValue.toString()); } return false; } /** * Uses the material-ui `makeStyles` functionality to generate classes for each of the rules. * This is used for styling table rows and columns. */ export const generateClassDefinitionsBasedOnRules = (rules) => { const classes = {}; rules.forEach((rule, i) => { if (rule['customization'] == 'cell color') { classes['& .rule' + i] = { backgroundColor: rule['customizationValue'] } } if (rule['customization'] == 'cell text color') { classes['& .rule' + i] = { color: rule['customizationValue'], fontWeight: "bold" } } if (rule['customization'] == 'row color') { classes['& .rule' + i] = { backgroundColor: rule['customizationValue'] } } if (rule['customization'] == 'row text color') { classes['& .rule' + i] = { color: rule['customizationValue'], fontWeight: "bold" } } }); return makeStyles({ root: classes, }); }