Finding association duplicates; sanitizing appropriately; beginning to edit narrativise function in UI

This commit is contained in:
efarooqui
2020-08-26 22:06:13 -07:00
parent 1d1a9971ab
commit fe9a5302fa
5 changed files with 72 additions and 56 deletions

View File

@@ -26,6 +26,7 @@ module.exports = {
USE_CATEGORIES: true, USE_CATEGORIES: true,
CATEGORIES_AS_FILTERS: true, CATEGORIES_AS_FILTERS: true,
USE_ASSOCIATIONS: true, USE_ASSOCIATIONS: true,
USE_ASSOCIATION_DESCRIPTIONS: true,
USE_SOURCES: true, USE_SOURCES: true,
USE_COVER: true, USE_COVER: true,
USE_SEARCH: false, USE_SEARCH: false,

View File

@@ -199,18 +199,18 @@ export function binarySearch (ar, el, compareFn) {
return -m - 1 return -m - 1
} }
export const isFilterLeaf = node => (Object.keys(node.children).length === 0) // export const isFilterLeaf = node => (Object.keys(node.children).length === 0)
export const isFilterDuplicate = (node, set) => { return (set.has(node.key)) } // export const isFilterDuplicate = (node, set) => { return (set.has(node.key)) }
export function findDescriptionInFilterTree (key, node) { // export function findDescriptionInFilterTree (key, node) {
if (node.key === key) return node.description // if (node.key === key) return node.description
if (isFilterLeaf(node)) return false // if (isFilterLeaf(node)) return false
const descs = Object.keys(node.children) // const descs = Object.keys(node.children)
.map(c => findDescriptionInFilterTree(key, node.children[c])) // .map(c => findDescriptionInFilterTree(key, node.children[c]))
.filter(v => !!v) // .filter(v => !!v)
if (descs.length !== 1) return false // if (descs.length !== 1) return false
return descs[0] // return descs[0]
} // }
export function makeNiceDate (datetime) { export function makeNiceDate (datetime) {
if (datetime === null) return null if (datetime === null) return null

View File

@@ -40,7 +40,8 @@ class Dashboard extends React.Component {
componentDidMount () { componentDidMount () {
if (!this.props.app.isMobile) { if (!this.props.app.isMobile) {
this.props.actions.fetchDomain() this.props.actions.fetchDomain()
.then(domain => this.props.actions.updateDomain({ .then(domain =>
this.props.actions.updateDomain({
domain, domain,
features: this.props.features features: this.props.features
})) }))

View File

@@ -29,46 +29,62 @@ function isValidDate (d) {
* Traverse a filter tree and check its duplicates. Also recompose as * Traverse a filter tree and check its duplicates. Also recompose as
* description if `features.USE_ASSOCIATION_DESCRIPTIONS` is true. * description if `features.USE_ASSOCIATION_DESCRIPTIONS` is true.
*/ */
function validateFilterTree (node, parent, set, duplicates, hasFilterDescriptions) { // function validateFilterTree (node, parent, set, duplicates, hasAssociationDescriptions) {
if (hasFilterDescriptions) { // if (hasAssociationDescriptions) {
if (node.key === '_root') { // if (node.key === '_root') {
node.isDescription = true // setting first set of nodes to values // node.isDescription = true // setting first set of nodes to values
} else if (!parent.isDescription) { // } else if (!parent.isDescription) {
node.isDescription = true // node.isDescription = true
} else { // } else {
node.isDescription = false // node.isDescription = false
} // }
if (node.isDescription && node.key !== 'root') { // if (node.isDescription && node.key !== 'root') {
parent.description = node.key // parent.description = node.key
parent.children = node.children // parent.children = node.children
delete parent.isDescription // delete parent.isDescription
} // }
if (isFilterLeaf(node)) { // if (isFilterLeaf(node)) {
delete parent.isDescription // delete parent.isDescription
} // }
} // }
if (typeof (node) !== 'object' || typeof (node.children) !== 'object') { // if (typeof (node) !== 'object' || typeof (node.children) !== 'object') {
return // return
} // }
// If it's a leaf, check that it's not duplicate // // If it's a leaf, check that it's not duplicate
if (isFilterLeaf(node)) { // if (isFilterLeaf(node)) {
if (isFilterDuplicate(node, set)) { // if (isFilterDuplicate(node, set)) {
// duplicates.push({
// id: node.key,
// error: makeError('Filters', node.key, 'filter was found more than once in hierarchy. Ignoring duplicate.')
// })
// delete parent.children[node.key]
// } else {
// set.add(node.key)
// }
// } else {
// // If it's not a leaf, simply keep going
// Object.values(node.children).forEach((childNode) => {
// validateFilterTree(childNode, node, set, duplicates, hasAssociationDescriptions)
// })
// }
// }
function findDuplicateAssociations (associations) {
const seenSet = new Set([])
const duplicates = []
associations.forEach(item => {
if (seenSet.has(item.id)) {
duplicates.push({ duplicates.push({
id: node.key, id: item.id,
error: makeError('Filters', node.key, 'filter was found more than once in hierarchy. Ignoring duplicate.') error: makeError('Association', item.id, 'association was found more than once. Ignoring duplicate.')
}) })
delete parent.children[node.key]
} else { } else {
set.add(node.key) seenSet.add(item.id)
} }
} else { })
// If it's not a leaf, simply keep going return duplicates
Object.values(node.children).forEach((childNode) => {
validateFilterTree(childNode, node, set, duplicates, hasFilterDescriptions)
})
}
} }
/* /*
@@ -162,10 +178,10 @@ export function validateDomain (domain, features) {
) )
// Validate uniqueness of associations // Validate uniqueness of associations
const associationSet = new Set([]) // const associationSet = new Set([])
const duplicateAssociations = [] // const duplicateAssociations = []
validateFilterTree(domain.associations, {}, associationSet, duplicateAssociations, features.USE_ASSOCIATION_DESCRIPTIONS) // validateFilterTree(domain.associations, {}, associationSet, duplicateAssociations, features.USE_ASSOCIATION_DESCRIPTIONS)
const duplicateAssociations = findDuplicateAssociations(domain.associations)
// Duplicated associations // Duplicated associations
if (duplicateAssociations.length > 0) { if (duplicateAssociations.length > 0) {
sanitizedDomain.notifications.push({ sanitizedDomain.notifications.push({

View File

@@ -6,17 +6,16 @@ const initial = {
* The Domain or 'domain' of this state refers to the tree of data * The Domain or 'domain' of this state refers to the tree of data
* available for render and display. * available for render and display.
* Selections and filters in the 'app' subtree will operate the domain * Selections and filters in the 'app' subtree will operate the domain
* in mapStateToProps of the Dashboard, and deterimne which items * in mapStateToProps of the Dashboard, and determine which items
* in the domain will get rendered by React * in the domain will get rendered by React
*/ */
domain: { domain: {
events: [], events: [],
narratives: [],
locations: [], locations: [],
categories: [], categories: [],
associations: [],
sources: {}, sources: {},
sites: [], sites: [],
filters: {},
notifications: [] notifications: []
}, },
@@ -24,7 +23,7 @@ const initial = {
* The 'app' subtree of this state determines the data and information to be * The 'app' subtree of this state determines the data and information to be
* displayed. * displayed.
* It may refer to those the user interacts with, by selecting, * It may refer to those the user interacts with, by selecting,
* fitlering and so on, which ultimately operate on the data to be displayed. * filtering and so on, which ultimately operate on the data to be displayed.
* Additionally, some of the 'app' flags are determined by the config file * Additionally, some of the 'app' flags are determined by the config file
* or by the characteristics of the client, browser, etc. * or by the characteristics of the client, browser, etc.
*/ */
@@ -137,12 +136,11 @@ const initial = {
features: { features: {
USE_COVER: false, USE_COVER: false,
USE_FILTERS: false, USE_ASSOCIATIONS: false,
USE_SEARCH: false, USE_SEARCH: false,
USE_SITES: false, USE_SITES: false,
USE_SOURCES: false, USE_SOURCES: false,
USE_SHAPES: false, USE_SHAPES: false,
USE_NARRATIVES: false,
GRAPH_NONLOCATED: false, GRAPH_NONLOCATED: false,
HIGHLIGHT_GROUPS: false HIGHLIGHT_GROUPS: false
} }