diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue index 64e75bad9..b724789fd 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue @@ -111,7 +111,7 @@ import { mapState, mapGetters } from "vuex" import Modal from 'ChillMainAssets/vuejs/_components/Modal' import VueMultiselect from 'vue-multiselect' import { getRelationsList, postRelationship } from "./api"; -import { adapt2vis, splitId } from "./vis-network"; +import { splitId } from "./vis-network"; export default { name: "App", @@ -142,11 +142,12 @@ export default { computed: { ...mapGetters(['nodes', 'edges']), ...mapState(['households', 'courses', 'excludedNodesIds', 'persons', - // not used 'links', 'relationships', 'personLoadedIds', 'householdLoadingIds', 'courseLoadedIds', 'relationshipLoadedIds', + // not used + 'links', 'relationships', 'personLoadedIds', 'householdLoadingIds', 'courseLoadedIds', 'relationshipLoadedIds', ]), visgraph_data() { - console.log('::: visgraph_data :::', this.nodes.length, 'nodes,', this.edges.length, 'edges') + //console.log('::: visgraph_data :::', this.nodes.length, 'nodes,', this.edges.length, 'edges') return { nodes: this.nodes, edges: this.edges @@ -154,12 +155,12 @@ export default { }, refreshNetwork() { - console.log('--- refresh network') + //console.log('--- refresh network') window.network.setData(this.visgraph_data) }, legendLayers() { - console.log('--- refresh legend') + //console.log('--- refresh legend') return [ ...this.households, ...this.courses @@ -167,7 +168,7 @@ export default { }, rebuildCheckedLayers() { - console.log('--- rebuild checked Layers') + //console.log('--- rebuild checked Layers') this.checkedLayers = [] let layersDisplayed = [ ...this.nodes.filter(n => n.id.startsWith('household')), @@ -215,7 +216,7 @@ export default { eventHub.off('delete-relationship-modal', this.deleteRelationshipModal) }, mounted() { - console.log('=== mounted: init graph') + //console.log('=== mounted: init graph')hill this.initGraph() this.listenOnGraph() this.getRelationsList() @@ -235,24 +236,30 @@ export default { } let nodeType = splitId(data.nodes[0], 'type') switch (nodeType) { + case 'person': let person = this.nodes.filter(n => n.id === data.nodes[0])[0] - console.log('@@@@@@@@ event on selected Person Node @@@@@@@@', person.id, person) + console.log('@@@@@@@@ event on selected Person Node @@@@@@@@', person.id) if (person.label === null) { this.$store.commit('unfoldPerson', person) this.forceUpdateComponent() } break + case 'household': let household = this.nodes.filter(n => n.id === data.nodes[0])[0] - console.log('######## event on selected Household Node ########', household.id, household) + console.log('######## event on selected Household Node ########', household.id) + this.$store.dispatch('unfoldPersonsByHousehold', household) + this.forceUpdateComponent() break + case 'accompanying_period': let course = this.nodes.filter(n => n.id === data.nodes[0])[0] - console.log('&&&&&&&& event on selected AccompanyingCourse Node &&&&&&&&', course.id, course) + console.log('&&&&&&&& event on selected AccompanyingCourse Node &&&&&&&&', course.id) this.$store.dispatch('unfoldPersonsByCourse', course) this.forceUpdateComponent() break + default: throw 'this node type is undefined' } @@ -265,7 +272,7 @@ export default { */ }, forceUpdateComponent() { - console.log('forceUpdateComponent') + //console.log('!! forceUpdateComponent !!') this.$forceUpdate() this.refreshNetwork }, @@ -280,14 +287,14 @@ export default { } }, addLayer(id) { - console.log('+ addLayer', id) + //console.log('+ addLayer', id) this.checkedLayers.push(id) - this.$store.commit('removeExcludedNode', id) + this.$store.dispatch('removeExcludedNode', id) }, removeLayer(id) { - console.log('- removeLayer', id) + //console.log('- removeLayer', id) this.checkedLayers = this.checkedLayers.filter(i => i !== id) - this.$store.commit('addExcludedNode', id) + this.$store.dispatch('addExcludedNode', id) }, addRelationshipModal(edgeData) { @@ -327,15 +334,15 @@ export default { }, getRelationsList() { - console.log('fetch relationsList') + //console.log('fetch relationsList') return getRelationsList().then(relations => new Promise(resolve => { - console.log('+ relations list', relations.results.length) + //console.log('+ relations list', relations.results.length) this.relations = relations.results.filter(r => r.isActive === true) resolve() })).catch() }, customLabel(value) { - console.log('customLabel', value) + //console.log('customLabel', value) return (value.title && value.reverseTitle) ? `${value.title.fr} ↔ ${value.reverseTitle.fr}` : '' }, getPerson(idtext) { @@ -353,7 +360,7 @@ export default { ) .then(relationship => new Promise(resolve => { console.log('post response', relationship) - this.$store.dispatch('addLinkFromRelationship', adapt2vis(relationship)) + this.$store.dispatch('addLinkFromRelationship', relationship) this.modal.showModal = false this.resetForm() resolve() @@ -370,7 +377,7 @@ export default { return patchRelationship(relationship) .then(response => new Promise(resolve => { console.log('patch response', response) - this.$store.dispatch('updateLinkFromRelationship', adapt2vis(response)) + this.$store.dispatch('updateLinkFromRelationship', response) this.modal.showModal = false this.resetForm() resolve() diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/store.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/store.js index 0fe35e406..d2a930658 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/store.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/store.js @@ -1,6 +1,7 @@ import { createStore } from 'vuex' import { getHouseholdByPerson, getCoursesByPerson, getRelationshipsByPerson } from './api' -import { adapt2vis, getHouseholdLabel, getHouseholdWidth, getRelationshipLabel, getRelationshipTitle, getRelationshipDirection, splitId } from './vis-network' +import { getHouseholdLabel, getHouseholdWidth, getRelationshipLabel, getRelationshipTitle, getRelationshipDirection, splitId, getGender, getAge } from './vis-network' +import {visMessages} from "./i18n"; const debug = process.env.NODE_ENV !== 'production' @@ -54,27 +55,100 @@ const store = createStore({ }, isPersonLoaded: (state) => (person_id) => { return state.personLoadedIds.includes(person_id) - } + }, + isExcludedNode: (state) => (id) => { + return state.excludedNodesIds.includes(id) + }, + + getParticipationsByCourse: (state) => (course_id) => { + //console.log('getParticipationsByCourse', course_id) + const course = state.courses.filter(c => c.id === course_id)[0] + const currentParticipations = course.participations.filter(p => p.endDate === null) + const participations = [] + currentParticipations.forEach(p => { + participations.push(p) + }) + console.log('participations', + participations.map(i => i.person.id), + participations + .filter(i => typeof i.person._id !== 'undefined') + .map(i => i.person.id) + ) + return participations + }, + + getMembersByHousehold: (state) => (household_id) => { + //console.log('getMembersByHousehold', household_id) + const household = state.households.filter(h => h.id === household_id)[0] + const currentMembers = household.members.filter(m => household.current_members_id.includes(m.id)) + const members = [] + currentMembers.forEach(m => { + members.push(m) + }) + console.log('members', + members.map(i => i.person.id), + members + .filter(i => typeof i.person._id !== 'undefined') + .map(i => i.person.id) + ) + return members + }, + + getFoldedPersons: () => (array) => { + return array + .filter(i => typeof i.person._id !== 'undefined') + .map(i => i.person) + }, + + }, mutations: { - addPerson(state, person) { - console.log('+ addPerson', person.id) + addPerson(state, [person, options]) { + //console.log('+ addPerson', person.id) + + person.group = person.type + person._id = person.id + person.id = `person_${person.id}` + person.label = `*${person.text}*\n_${getGender(person.gender)} - ${getAge(person.birthdate)}_` + if (options.folded) { + person.title = visMessages.fr.visgraph.click_to_expand + person._label = person.label // keep label + person.label = null + } + state.persons.push(person) }, addHousehold(state, household) { - console.log('+ addHousehold', household.id) + //console.log('+ addHousehold', household.id) + + household.group = household.type + household._id = household.id + household.label = `${visMessages.fr.visgraph.Household} n° ${household.id}` + household.id = `household_${household.id}` + state.households.push(household) }, addCourse(state, course) { - console.log('+ addCourse', course.id) - state.courses.push(course) + //console.log('+ addCourse', course.id) + + course.group = course.type + course._id = course.id + course.label = `${visMessages.fr.visgraph.Course} n° ${course.id}` + course.id = `accompanying_period_${course.id}` + + state.courses.push(course) }, addRelationship(state, relationship) { - console.log('+ addRelationship', relationship.id) + //console.log('+ addRelationship', relationship.id) + + relationship.group = relationship.type + relationship._id = relationship.id + relationship.id = `relationship_${relationship.id}` + state.relationships.push(relationship) }, addLink(state, link) { - console.log('+ addLink from', link.from, 'to', link.to) + //console.log('+ addLink from', link.from, 'to', link.to) state.links.push(link) }, @@ -86,7 +160,7 @@ const store = createStore({ state.personLoadedIds = state.personLoadedIds.filter(i => i !== id) }, markHouseholdLoading(state, id) { - console.log('..loading household', id) + //console.log('..loading household', id) state.householdLoadingIds.push(id) }, unmarkHouseholdLoading(state, id) { @@ -107,17 +181,19 @@ const store = createStore({ //// excluded addExcludedNode(state, id) { + console.log('exclude list: +', id) state.excludedNodesIds.push(id) }, removeExcludedNode(state, id) { + console.log('exclude list: -', id) state.excludedNodesIds = state.excludedNodesIds.filter(e => e !== id) }, //// unfold unfoldPerson(state, person) { - console.log('unfoldPerson', person) + //console.log('unfoldPerson', person) if (person.label !== null) { - throw 'person is not expandable' + throw 'person is not folded' } person.label = person._label delete person._label @@ -132,7 +208,7 @@ const store = createStore({ */ addPerson({ commit, dispatch }, person) { commit('markPersonLoaded', person.id) - commit('addPerson', adapt2vis(person, { folded: false })) + commit('addPerson', [person, { folded: false }]) dispatch('fetchInfoForPerson', person) }, @@ -156,14 +232,13 @@ const store = createStore({ * @param person */ fetchHouseholdForPerson({ commit, getters, dispatch }, person) { - console.log(' isHouseholdLoading ?', getters.isHouseholdLoading(person.current_household_id)) + //console.log(' isHouseholdLoading ?', getters.isHouseholdLoading(person.current_household_id)) if (! getters.isHouseholdLoading(person.current_household_id)) { commit('markHouseholdLoading', person.current_household_id) getHouseholdByPerson(person) .then(household => new Promise(resolve => { - //console.log('getHouseholdByPerson', household) - commit('addHousehold', adapt2vis(household)) - //commit('addExcludedNode', household.id) + commit('addHousehold', household) + //dispatch('addExcludedNode', household.id) // layer uncheck when added dispatch('addLinkFromPersonsToHousehold', household) resolve() }) @@ -179,9 +254,9 @@ const store = createStore({ * @param household */ addLinkFromPersonsToHousehold({ commit, getters, dispatch }, household) { - const currentMembers = household.members.filter(v => household.current_members_id.includes(v.id)) - currentMembers.forEach(m => { - //console.log('-> addLink from person', m.person.id, 'to household', m.person.current_household_id) + let members = getters.getMembersByHousehold(household.id) + console.log('add link for', members.length, 'members') + members.forEach(m => { commit('addLink', { from: `${m.person.type}_${m.person.id}`, to: `household_${m.person.current_household_id}`, @@ -193,8 +268,7 @@ const store = createStore({ width: getHouseholdWidth(m), }) if (!getters.isPersonLoaded(m.person.id)) { - console.log(' person is not loaded', m.person.id) - dispatch('addMissingPerson', m.person) + dispatch('addMissingPerson', [m.person, household]) } }) }, @@ -208,7 +282,7 @@ const store = createStore({ //console.log('fetchCoursesByPerson', person) getCoursesByPerson(person) .then(courses => new Promise(resolve => { - console.log('fetch courses', courses.length) + //console.log('fetch courses', courses.length) dispatch('addCourses', courses) resolve() })) @@ -223,12 +297,12 @@ const store = createStore({ //console.log('addCourse', courses) let currentCourses = courses.filter(c => c.closingDate === null) currentCourses.forEach(course => { - console.log(' isCourseLoaded ?', getters.isCourseLoaded(course.id)) + //console.log(' isCourseLoaded ?', getters.isCourseLoaded(course.id)) if (! getters.isCourseLoaded(course.id)) { //console.log('course', course.id) commit('markCourseLoaded', course.id) - commit('addCourse', adapt2vis(course)) - commit('addExcludedNode', course.id) + commit('addCourse', course) + dispatch('addExcludedNode', course.id) // layer uncheck when added dispatch('addLinkFromPersonsToCourse', course) } }) @@ -240,9 +314,10 @@ const store = createStore({ * @param course */ addLinkFromPersonsToCourse({ commit, getters, dispatch }, course) { - let currentParticipations = course.participations.filter(p => p.endDate === null) - console.log(' participations', currentParticipations.length) - currentParticipations.forEach(p => { + let participations = getters.getParticipationsByCourse(course.id) + console.log('add link for', participations.length, 'participations') + participations.forEach(p => { + //console.log(p.person.id) commit('addLink', { from: `${p.person.type}_${p.person.id}`, to: `${course.id}`, @@ -252,8 +327,7 @@ const store = createStore({ font: { color: 'darkorange' }, }) if (!getters.isPersonLoaded(p.person.id)) { - console.log(' person is not loaded', p.person.id) - dispatch('addMissingPerson', p.person) + dispatch('addMissingPerson', [p.person, course]) } }) }, @@ -267,13 +341,12 @@ const store = createStore({ //console.log('fetchRelationshipByPerson', person) getRelationshipsByPerson(person) .then(relationships => new Promise(resolve => { - console.log('fetch relationships', relationships.length) + //console.log('fetch relationships', relationships.length) dispatch('addRelationships', relationships) resolve() })) }, - /** * 9) Add each distinct relationship * @param object @@ -281,11 +354,11 @@ const store = createStore({ */ addRelationships({ commit, getters, dispatch }, relationships) { relationships.forEach(relationship => { - console.log(' isRelationshipLoaded ?', getters.isRelationshipLoaded(relationship.id)) + //console.log(' isRelationshipLoaded ?', getters.isRelationshipLoaded(relationship.id)) if (! getters.isRelationshipLoaded(relationship.id)) { //console.log('relationship', relationship.id) commit('markRelationshipLoaded', relationship.id) - commit('addRelationship', adapt2vis(relationship)) + commit('addRelationship', relationship) dispatch('addLinkFromRelationship', relationship) } }) @@ -311,8 +384,7 @@ const store = createStore({ }) for (let person of [r.fromPerson, r.toPerson]) { if (!getters.isPersonLoaded(person.id)) { - console.log(' person is not loaded', person.id) - dispatch('addMissingPerson', person) + dispatch('addMissingPerson', [person, r]) } } }, @@ -321,28 +393,122 @@ const store = createStore({ * Add missing person. node is displayed without label (folded). * We stop here and listen on events to unfold person and expand its fetch infos * @param object - * @param person + * @param array */ - addMissingPerson({ commit, getters, dispatch }, person) { + addMissingPerson({ commit, getters, dispatch }, [person, parent]) { + console.log('! add missing Person', person.id) commit('markPersonLoaded', person.id) - commit('addPerson', adapt2vis(person, { folded: true })) + commit('addPerson', [person, { folded: true }]) + if (getters.isExcludedNode(parent.id)) { + commit('addExcludedNode', person.id) //1 + } }, + /** + * ============================================================================= + */ + /** * Triggered by a vis-network event when clicking on a Course Node. * Each folded node is unfold, then expanded with fetch infos * @param object * @param course */ - unfoldPersonsByCourse({ commit, dispatch }, course) { - let currentParticipations = course.participations.filter(p => p.endDate === null) - currentParticipations.forEach(p => { + unfoldPersonsByCourse({ getters, commit, dispatch }, course) { + const participations = getters.getParticipationsByCourse(course.id) + const folded = getters.getFoldedPersons(participations) + console.log('folded', folded) + participations.forEach(p => { + console.log('participation person', p.person.id) if (p.person.label === null) { + console.log(' -> unfold folded') commit('unfoldPerson', p.person) dispatch('fetchInfoForPerson', p.person) } }) }, + + /** + * Triggered by a vis-network event when clicking on a Household Node. + * Each folded node is unfold, then expanded with fetch infos + * @param object + * @param household + */ + unfoldPersonsByHousehold({ getters, commit, dispatch }, household) { + const members = getters.getMembersByHousehold(household.id) + const folded = getters.getFoldedPersons(members) + console.log('folded', folded) + + members.forEach(m => { + console.log('member person', m.person.id) + if (m.person.label === null) { + console.log(' -> unfold folded') + commit('unfoldPerson', m.person) + dispatch('fetchInfoForPerson', m.person) + } + }) + }, + + /** + * For an excluded node, detect folded person and exclude too + * @param object + * @param id + */ + addExcludedNode({ getters, commit }, id) { + console.log('==> addExcludedNode') + commit('addExcludedNode', id) + switch (splitId(id, 'type')) { + case 'accompanying_period': + let participations = getters.getParticipationsByCourse(id) + participations.forEach(p => { + if (p.person.label === null) { + commit('addExcludedNode', p.person.id) + } + }) + break + case 'household': + let members = getters.getMembersByHousehold(id) + members.forEach(m => { + if (m.person.label === null) { + commit('addExcludedNode', m.person.id) + } + }) + break + default: + throw 'case undefined' + } + }, + + /** + * For an excluded node, remove relative persons excluded too + * @param object + * @param id + */ + removeExcludedNode({ getters, commit }, id) { + console.log('<== removeExcludedNode') + commit('removeExcludedNode', id) + switch (splitId(id, 'type')) { + case 'accompanying_period': + let participations = getters.getParticipationsByCourse(id) + participations.forEach(p => { + if (p.person.label === null) { + commit('removeExcludedNode', p.person.id) + } + }) + break + case 'household': + let members = getters.getMembersByHousehold(id) + members.forEach(m => { + if (m.person.label === null) { + commit('removeExcludedNode', m.person.id) + } + }) + break + default: + throw 'case undefined' + } + }, + } }) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/vis-network.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/vis-network.js index 2160d51bd..a66a6e842 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/vis-network.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/vis-network.js @@ -8,7 +8,7 @@ import { TinyEmitter } from "tiny-emitter"; * cfr. https://github.com/almende/vis/issues/2524#issuecomment-307108271 */ -console.log('@@@ init eventHub App @@@') +//console.log('@@@ init eventHub App @@@') window.eventHub = new TinyEmitter() window.network = {} @@ -179,46 +179,6 @@ window.options = { } } -/** - * Adapt entity to graph (id, label) - * rename id in _id and add properties needed by vis - * @param entity - * @param options - * @returns entity - */ -const adapt2vis = (entity, options = {}) => { - entity.group = entity.type - switch (entity.type) { - case 'person': - entity._id = entity.id - entity.label = `*${entity.text}*\n_${getGender(entity.gender)} - ${getAge(entity.birthdate)}_` - if (options.folded) { - entity.title = visMessages.fr.visgraph.click_to_expand - entity._label = entity.label // keep label - entity.label = null - } - entity.id = `person_${entity.id}` - break - case 'household': - entity._id = entity.id - entity.label = `${visMessages.fr.visgraph.Household} n° ${entity.id}` - entity.id = `household_${entity.id}` - break - case 'accompanying_period': - entity._id = entity.id - entity.label = `${visMessages.fr.visgraph.Course} n° ${entity.id}` - entity.id = `accompanying_period_${entity.id}` - break - case 'relationship': - entity._id = entity.id - entity.id = `relationship_${entity.id}` - break - default: - throw 'entity undefined' - } - return entity -} - /** * @param gender * @returns {string} @@ -324,7 +284,8 @@ const splitId = (id, position) => { } export { - adapt2vis, + getGender, + getAge, getHouseholdLabel, getHouseholdWidth, getRelationshipDirection,