From 34f920abdd750ace9457b50897ea87ff6b4f75f5 Mon Sep 17 00:00:00 2001 From: Laurent Deketelaere Date: Tue, 11 Jul 2023 15:09:22 +0200 Subject: [PATCH 1/8] Fix models types --- app/models/administrative-unit.ts | 14 ++++++++++---- app/models/agenda-item-handling.ts | 6 +++--- app/models/mandatary.ts | 6 +++--- app/models/membership.ts | 4 ++-- app/models/vote.ts | 8 ++++---- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/models/administrative-unit.ts b/app/models/administrative-unit.ts index 83bc3b72..551d5cb8 100644 --- a/app/models/administrative-unit.ts +++ b/app/models/administrative-unit.ts @@ -1,4 +1,10 @@ -import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; +import Model, { + attr, + belongsTo, + hasMany, + AsyncHasMany, + AsyncBelongsTo, +} from '@ember-data/model'; import GoverningBodyModel from './governing-body'; import AdministrativeUnitClasssificationCodeModel from './location'; import LocationModel from './location'; @@ -8,14 +14,14 @@ export default class AdministrativeUnitModel extends Model { declare name: string; @hasMany('governing-body', { async: true, inverse: 'administrativeUnit' }) - declare governingBodies: GoverningBodyModel; + declare governingBodies: AsyncHasMany; @belongsTo('administrative-unit-classification-code', { async: true, inverse: null, }) - declare classification: AdministrativeUnitClasssificationCodeModel; + declare classification: AsyncBelongsTo; - @belongsTo('location', { async: true, inverse: null }) + @belongsTo('location', { async: false, inverse: null }) declare location: LocationModel; } diff --git a/app/models/agenda-item-handling.ts b/app/models/agenda-item-handling.ts index 7aadf5bc..a747514e 100644 --- a/app/models/agenda-item-handling.ts +++ b/app/models/agenda-item-handling.ts @@ -1,4 +1,4 @@ -import Model, { attr, hasMany } from '@ember-data/model'; +import Model, { AsyncHasMany, attr, hasMany } from '@ember-data/model'; import ResolutionModel from './resolution'; import VoteModel from './vote'; @@ -6,8 +6,8 @@ export default class AgendaItemHandlingModel extends Model { @attr('boolean') declare public?: boolean; @hasMany('vote', { async: true, inverse: null }) - declare hasVotes?: VoteModel; + declare hasVotes?: AsyncHasMany; @hasMany('resolution', { async: true, inverse: null }) - declare resolutions?: ResolutionModel; + declare resolutions?: AsyncHasMany; } diff --git a/app/models/mandatary.ts b/app/models/mandatary.ts index b21efcf6..c427730d 100644 --- a/app/models/mandatary.ts +++ b/app/models/mandatary.ts @@ -1,4 +1,4 @@ -import Model, { attr, belongsTo } from '@ember-data/model'; +import Model, { AsyncBelongsTo, attr, belongsTo } from '@ember-data/model'; import MembershipModel from './membership'; import PersonModel from './person'; @@ -7,8 +7,8 @@ export default class MandataryModel extends Model { @attr('date') declare endDate: Date; @belongsTo('person', { async: true, inverse: null }) - declare alias: PersonModel; + declare alias: AsyncBelongsTo; @belongsTo('membership', { async: true, inverse: null }) - declare hasMembership: MembershipModel; + declare hasMembership: AsyncBelongsTo; } diff --git a/app/models/membership.ts b/app/models/membership.ts index a5d3f16d..c72bbddc 100644 --- a/app/models/membership.ts +++ b/app/models/membership.ts @@ -1,7 +1,7 @@ -import Model, { belongsTo } from '@ember-data/model'; +import Model, { AsyncBelongsTo, belongsTo } from '@ember-data/model'; import parliamentaryGroupModel from './parliamentary-group'; export default class MembershipModel extends Model { @belongsTo('parliamentary-group', { async: true, inverse: null }) - declare innerGroup: parliamentaryGroupModel; + declare innerGroup: AsyncBelongsTo; } diff --git a/app/models/vote.ts b/app/models/vote.ts index 96dab035..12ae2450 100644 --- a/app/models/vote.ts +++ b/app/models/vote.ts @@ -1,4 +1,4 @@ -import Model, { attr, hasMany } from '@ember-data/model'; +import Model, { AsyncHasMany, attr, hasMany } from '@ember-data/model'; import MandataryModel from './mandatary'; export default class VoteModel extends Model { @@ -8,11 +8,11 @@ export default class VoteModel extends Model { @attr('boolean') declare secret: boolean; @hasMany('mandatary', { async: true, inverse: null }) - declare hasAbstainers: MandataryModel; + declare hasAbstainers: AsyncHasMany; @hasMany('mandatary', { async: true, inverse: null }) - declare hasOpponents: MandataryModel; + declare hasOpponents: AsyncHasMany; @hasMany('mandatary', { async: true, inverse: null }) - declare hasProponents: MandataryModel; + declare hasProponents: AsyncHasMany; } From a45f54bd05038f69996ada16401788f534f825c0 Mon Sep 17 00:00:00 2001 From: Laurent Deketelaere Date: Tue, 11 Jul 2023 16:33:01 +0200 Subject: [PATCH 2/8] Fix `agenda-item`, `session` and `governing-body` relationship related to lblod/app-burgernabije-besluitendatabank#17 --- app/controllers/map.ts | 3 +-- app/models/agenda-item.ts | 20 ++++++++++++--- app/models/governing-body.ts | 23 ++++++++++++++--- app/models/session.ts | 20 ++++++--------- app/routes/agenda-items.ts | 43 ++++++++++++++++---------------- app/routes/detail.ts | 24 +++++++----------- app/routes/map.ts | 45 ++++++++++++++++++++-------------- app/routes/municipality.ts | 30 +++++++++++++---------- app/routes/sessions/index.ts | 18 +++++++++----- app/routes/sessions/session.ts | 7 +++--- 10 files changed, 134 insertions(+), 99 deletions(-) diff --git a/app/controllers/map.ts b/app/controllers/map.ts index 8f06d25e..fac1345c 100644 --- a/app/controllers/map.ts +++ b/app/controllers/map.ts @@ -64,8 +64,7 @@ export default class MapComponent extends Controller { this.agendaData.forEach((agendaItem: any) => { const agendaItemLocation = agendaItem .get('session') - ?.get('governingBody') - ?.get('administrativeUnit')?.name; + ?.get('municipality'); if (name === agendaItemLocation) { const datenow = new Date(Date.now()).setHours(0, 0, 0, 0); const f = new Date(agendaItem.geplandeStart); diff --git a/app/models/agenda-item.ts b/app/models/agenda-item.ts index e6eec8fc..b037add1 100644 --- a/app/models/agenda-item.ts +++ b/app/models/agenda-item.ts @@ -1,4 +1,10 @@ -import Model, { attr, belongsTo } from '@ember-data/model'; +import Model, { + attr, + belongsTo, + hasMany, + AsyncHasMany, + AsyncBelongsTo, +} from '@ember-data/model'; import AgendaItemHandlingModel from './agenda-item-handling'; import SessionModel from './session'; @@ -8,9 +14,15 @@ export default class AgendaItemModel extends Model { @attr('string') declare alternateLink: string; @attr('boolean') declare plannedPublic: boolean; - @belongsTo('session', { async: true, inverse: 'agendaItems' }) - declare session?: SessionModel; + @hasMany('session', { async: true, inverse: 'agendaItems' }) + declare sessions?: AsyncHasMany; @belongsTo('agenda-item-handling', { async: true, inverse: null }) - declare handledBy?: AgendaItemHandlingModel; + declare handledBy?: AsyncBelongsTo; + + get session() { + return this.sessions?.slice().find((session) => { + return session.hasMunicipality; + }); + } } diff --git a/app/models/governing-body.ts b/app/models/governing-body.ts index 19dc350d..baf3b62b 100644 --- a/app/models/governing-body.ts +++ b/app/models/governing-body.ts @@ -1,4 +1,9 @@ -import Model, { attr, belongsTo, type AsyncBelongsTo } from '@ember-data/model'; +import Model, { + attr, + belongsTo, + hasMany, + AsyncHasMany, +} from '@ember-data/model'; import SessionModel from './session'; import AdministrativeUnitModel from './administrative-unit'; @@ -11,6 +16,18 @@ export default class GoverningBodyModel extends Model { }) declare administrativeUnit: AdministrativeUnitModel; - @belongsTo('session', { async: true, inverse: 'governingBody' }) - declare session: AsyncBelongsTo; + @hasMany('session', { async: true, inverse: 'governingBody' }) + declare sessions: AsyncHasMany; + + @belongsTo('governing-body', { + async: false, + inverse: 'hasTimeSpecializations', + }) + declare isTimeSpecializationOf: GoverningBodyModel; + + @hasMany('governing-body', { + async: true, + inverse: 'isTimeSpecializationOf', + }) + declare hasTimeSpecializations: AsyncHasMany; } diff --git a/app/models/session.ts b/app/models/session.ts index 0c931333..38dcc2a7 100644 --- a/app/models/session.ts +++ b/app/models/session.ts @@ -14,32 +14,28 @@ export default class SessionModel extends Model { @attr('date') declare startedAt?: Date; @attr('date') declare endedAt?: Date; - @hasMany('agenda-item', { async: true, inverse: 'session' }) + @hasMany('agenda-item', { async: true, inverse: 'sessions' }) declare agendaItems: AsyncHasMany; - @belongsTo('governing-body', { async: false, inverse: 'session' }) + @belongsTo('governing-body', { async: false, inverse: 'sessions' }) declare governingBody: GoverningBodyModel; get name() { - return this.governingBody?.name ?? 'Ontbrekend bestuursorgaan'; - } - - get municipality() { return ( - this.governingBody?.administrativeUnit?.name ?? - 'Ontbrekende bestuurseenheid' + this.governingBody?.isTimeSpecializationOf?.name ?? + 'Ontbrekend bestuursorgaan' ); } - get location() { + get municipality() { return ( - this.governingBody?.administrativeUnit?.location?.label ?? - 'Ontbrekende locatie' + this.governingBody?.isTimeSpecializationOf?.administrativeUnit?.location + ?.label ?? 'Ontbrekende bestuurseenheid' ); } get hasMunicipality() { - return !!this.governingBody?.administrativeUnit; + return !!this.governingBody?.isTimeSpecializationOf?.administrativeUnit; } get dateFormatted() { diff --git a/app/routes/agenda-items.ts b/app/routes/agenda-items.ts index 84a4402b..2dd0ca44 100644 --- a/app/routes/agenda-items.ts +++ b/app/routes/agenda-items.ts @@ -26,24 +26,22 @@ const getQuery = ({ }): AgendaItemsRequestInterface => ({ // exclude sessions without governing body and administrative unit //todo investigate why filtering is not working - include: [ - 'session', - 'session.governing-body', - 'session.governing-body.administrative-unit', - 'session.governing-body.administrative-unit.location', - ].join(','), - sort: '-session.planned-start', + include: + 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', + sort: '-sessions.planned-start', filter: { - session: { + sessions: { ':gt:planned-start': plannedStartMin ? plannedStartMin : undefined, ':lt:planned-start': plannedStartMax ? plannedStartMax : undefined, ':has:governing-body': true, 'governing-body': { - ':has:administrative-unit': true, - 'administrative-unit': { - ':has:name': true, - location: { - ':id:': locationIds ? locationIds : undefined, + ':has:is-time-specialization-of': true, + 'is-time-specialization-of': { + ':has:administrative-unit': true, + 'administrative-unit': { + location: { + ':id:': locationIds ? locationIds : undefined, + }, }, }, }, @@ -67,17 +65,18 @@ interface AgendaItemsRequestInterface { include: string; sort?: string; filter?: { - ':or:'?: {}; - session?: { - ':gt:planned-start'?: string | undefined; - ':lt:planned-start'?: string | undefined; + ':or:'?: unknown; + sessions?: { + ':gt:planned-start'?: string; + ':lt:planned-start'?: string; ':has:governing-body'?: boolean; 'governing-body'?: { - ':has:administrative-unit'?: boolean; - 'administrative-unit': { - ':has:name'?: boolean; - name?: any; - location?: {}; + ':has:is-time-specialization-of'?: boolean; + 'is-time-specialization-of'?: { + ':has:administrative-unit'?: boolean; + 'administrative-unit': { + location?: unknown; + }; }; }; }; diff --git a/app/routes/detail.ts b/app/routes/detail.ts index 9d3226a5..c41bbee5 100644 --- a/app/routes/detail.ts +++ b/app/routes/detail.ts @@ -5,25 +5,15 @@ import KeywordStoreService from 'frontend-burgernabije-besluitendatabank/service import { sortObjectsByTitle } from 'frontend-burgernabije-besluitendatabank/utils/array-utils'; const agendaItemIncludes = [ - 'session', - // "session.governing-body", - 'session.governing-body.administrative-unit', - // 'handled-by', 'handled-by.has-votes', 'handled-by.resolutions', - // 'session.governing-body', - 'session.governing-body.administrative-unit', - // 'handled-by.has-votes.has-presents', - // 'handled-by.has-votes.has-abstainers', 'handled-by.has-votes.has-abstainers.alias', 'handled-by.has-votes.has-abstainers.has-membership.inner-group', - // 'handled-by.has-votes.has-voters', - // 'handled-by.has-votes.has-opponents', 'handled-by.has-votes.has-opponents.alias', 'handled-by.has-votes.has-opponents.has-membership.inner-group', - // 'handled-by.has-votes.has-proponents', 'handled-by.has-votes.has-proponents.alias', 'handled-by.has-votes.has-proponents.has-membership.inner-group', + 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', ].join(','); export default class DetailRoute extends Route { @@ -40,7 +30,7 @@ export default class DetailRoute extends Route { ? await this.store.query('agenda-item', { include: agendaItemIncludes, filter: { - session: { + sessions: { [':id:']: sessionId, }, }, @@ -101,10 +91,14 @@ export default class DetailRoute extends Route { }, municipality: agendaItem.session?.get('municipality') || undefined, filter: { - session: { + sessions: { 'governing-body': { - 'administrative-unit': { - name: agendaItem.session?.get('municipality') || undefined, + 'is-time-specialization-of': { + 'administrative-unit': { + location: { + label: agendaItem.session?.get('municipality') || undefined, + }, + }, }, }, }, diff --git a/app/routes/map.ts b/app/routes/map.ts index e834ddd6..a5a7999a 100644 --- a/app/routes/map.ts +++ b/app/routes/map.ts @@ -10,16 +10,22 @@ interface AgendaItemsRequestInterface { include: string; municipality?: string; filter?: { - ':has:session'?: boolean; - session?: { + ':has:sessions'?: boolean; + sessions?: { ':gt:planned-start'?: string; ':has:governing-body'?: boolean; 'governing-body'?: { - ':has:administrative-unit'?: boolean; - 'administrative-unit': { - ':has:name'?: boolean; - name?: {}; + ':has:is-time-specialization-of': true; + 'is-time-specialization-of': { + ':has:administrative-unit'?: boolean; + 'administrative-unit': { + ':has:location'?: boolean; + location?: { + ':has:label'?: boolean; + label?: string; + }; + }; }; }; }; @@ -35,15 +41,11 @@ export default class MapRoute extends Route { page: { size: 600, }, - include: [ - 'session', - 'session.governing-body', - 'session.governing-body.administrative-unit', - 'session.governing-body.administrative-unit.location', - ].join(','), + include: + 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', filter: { - ':has:session': true, - session: { + ':has:sessions': true, + sessions: { ':gt:planned-start': new Date( new Date().setMonth(new Date().getMonth() - 3) ) @@ -51,9 +53,15 @@ export default class MapRoute extends Route { .split('T')[0], ':has:governing-body': true, 'governing-body': { - ':has:administrative-unit': true, - 'administrative-unit': { - ':has:name': true, + ':has:is-time-specialization-of': true, + 'is-time-specialization-of': { + ':has:administrative-unit': true, + 'administrative-unit': { + ':has:location': true, + location: { + ':has:label': true, + }, + }, }, }, }, @@ -64,8 +72,7 @@ export default class MapRoute extends Route { .query('agenda-item', req) .then((data) => { return data.filter((item) => { - return item.session?.get('governingBody')?.get('administrativeUnit') - ?.name; + return item.session?.hasMunicipality; }); }); diff --git a/app/routes/municipality.ts b/app/routes/municipality.ts index 6de8b0fb..614f5313 100644 --- a/app/routes/municipality.ts +++ b/app/routes/municipality.ts @@ -10,12 +10,13 @@ interface AgendaItemsRequestInterface { include: string; municipality?: string; filter?: { - ':or:'?: {}; - session?: { + ':or:'?: unknown; + sessions?: { 'governing-body'?: { - 'administrative-unit': { - name?: {}; - location?: {}; + 'is-time-specialization-of'?: { + 'administrative-unit': { + location?: unknown; + }; }; }; }; @@ -30,28 +31,31 @@ export default class MunicipalityRoute extends Route { }; async model(params: any) { - const { municipality, page } = params; + const { municipality } = params; const req: AgendaItemsRequestInterface = { page: { size: 10, }, municipality: municipality, - include: [ - 'session', - 'session.governing-body', - 'session.governing-body.administrative-unit', - ].join(','), + include: + 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', filter: {}, }; const sessionFilter: { [key: string]: any } = {}; sessionFilter['governing-body'] = { - 'administrative-unit': { name: municipality }, + 'is-time-specialization-of': { + 'administrative-unit': { + location: { + label: municipality, + }, + }, + }, }; req.filter = {}; - req.filter.session = sessionFilter; + req.filter.sessions = sessionFilter; const data = await hash({ gemeenteraadsleden: [], diff --git a/app/routes/sessions/index.ts b/app/routes/sessions/index.ts index 11a7814e..66f05196 100644 --- a/app/routes/sessions/index.ts +++ b/app/routes/sessions/index.ts @@ -18,18 +18,24 @@ const getQuery = ( filter: { ':has:governing-body': true, 'governing-body': { - ':has:administrative-unit': true, - 'administrative-unit': { - ':has:name': locationIds ? true : undefined, - location: { - ':id:': locationIds ? locationIds : undefined, + ':has:is-time-specialization-of': true, + 'is-time-specialization-of': { + ':has:administrative-unit': true, + 'administrative-unit': { + ':has:location': true, + location: { + ':id:': locationIds ? locationIds : undefined, + }, }, }, }, ':gt:planned-start': plannedStartMin ? plannedStartMin : undefined, ':lt:planned-start': plannedStartMax ? plannedStartMax : undefined, }, - include: ['governing-body.administrative-unit', 'agenda-items'].join(','), + include: [ + 'governing-body.is-time-specialization-of.administrative-unit.location', + 'agenda-items', + ].join(','), sort: '-planned-start', page: { number: page, diff --git a/app/routes/sessions/session.ts b/app/routes/sessions/session.ts index 0176d6df..32b60769 100644 --- a/app/routes/sessions/session.ts +++ b/app/routes/sessions/session.ts @@ -14,9 +14,10 @@ export default class SessionRoute extends Route { const session: SessionModel = sessionFromParent ?? (await this.store.findRecord('session', session_id, { - include: ['governing-body.administrative-unit', 'agenda-items'].join( - ',' - ), + include: [ + 'governing-body.is-time-specialization-of.administrative-unit.location', + 'agenda-items', + ].join(','), })); return session; From ba3d0b79bf2eabdb55087b780d3a1884ef2ff933 Mon Sep 17 00:00:00 2001 From: Laurent Deketelaere Date: Tue, 11 Jul 2023 16:37:25 +0200 Subject: [PATCH 3/8] Feat(Detail): Redirect municipality button to agenda-items/?gemeentes=municipality --- app/components/decision-card.hbs | 12 ++---------- app/controllers/detail.ts | 4 ++++ app/templates/detail.hbs | 9 ++++++--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/app/components/decision-card.hbs b/app/components/decision-card.hbs index ff659aaa..70cc271d 100644 --- a/app/components/decision-card.hbs +++ b/app/components/decision-card.hbs @@ -31,17 +31,9 @@ {{@item.session.dateFormatted}} - - {{#if @item.session.governingBody.name}} - {{@item.session.governingBody.name}} - {{else}} - Geen Bestuurorgaan - {{/if}} + {{@item.session.name}} - - {{#if @item.session.governingBody.administrativeUnit.name}} - {{@item.session.governingBody.administrativeUnit.name}} - {{else}} - Geen Bestuurseenheid - {{/if}} + {{@item.session.municipality}} diff --git a/app/controllers/detail.ts b/app/controllers/detail.ts index 195b5e7d..3c23291f 100644 --- a/app/controllers/detail.ts +++ b/app/controllers/detail.ts @@ -11,4 +11,8 @@ export default class DetailController extends Controller { get hasVotes() { return this.model.agendaItem?.handledBy?.get('hasVotes')?.length > 0; } + + get municipalityQuery() { + return { gemeentes: this.model.agendaItem.session.municipality }; + } } diff --git a/app/templates/detail.hbs b/app/templates/detail.hbs index 80779179..305e1ddb 100644 --- a/app/templates/detail.hbs +++ b/app/templates/detail.hbs @@ -42,8 +42,8 @@ /> {{#if this.model.agendaItem.session.hasMunicipality}}

Gemeente {{#if this.model.agendaItem.session.municipality}} @@ -222,7 +222,10 @@ {{/if}} {{this.keywordStore.keyword}} in - {{this.model.agendaItem.session.municipality}} + {{this.model.agendaItem.session.municipality}}

    {{#each this.model.similiarAgendaItems as |agendaItem|}}
  1. From 69dc02cf16e08675878003e41ddd841c3b5fd251 Mon Sep 17 00:00:00 2001 From: Laurent Deketelaere Date: Wed, 12 Jul 2023 10:26:04 +0200 Subject: [PATCH 4/8] Feat(Session): Add fallback to governing-body when is-time-specialization-of is empty --- app/models/session.ts | 12 +++++++++--- app/routes/agenda-items.ts | 4 +++- app/routes/detail.ts | 1 + app/routes/map.ts | 4 +++- app/routes/municipality.ts | 4 +++- app/routes/sessions/index.ts | 1 + app/routes/sessions/session.ts | 1 + 7 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/models/session.ts b/app/models/session.ts index 38dcc2a7..955b3441 100644 --- a/app/models/session.ts +++ b/app/models/session.ts @@ -22,7 +22,8 @@ export default class SessionModel extends Model { get name() { return ( - this.governingBody?.isTimeSpecializationOf?.name ?? + this.governingBody?.isTimeSpecializationOf?.name || + this.governingBody?.name || 'Ontbrekend bestuursorgaan' ); } @@ -30,12 +31,17 @@ export default class SessionModel extends Model { get municipality() { return ( this.governingBody?.isTimeSpecializationOf?.administrativeUnit?.location - ?.label ?? 'Ontbrekende bestuurseenheid' + ?.label || + this.governingBody?.administrativeUnit?.location?.label || + 'Ontbrekende bestuurseenheid' ); } get hasMunicipality() { - return !!this.governingBody?.isTimeSpecializationOf?.administrativeUnit; + return ( + !!this.governingBody?.isTimeSpecializationOf?.administrativeUnit || + !!this.governingBody?.administrativeUnit + ); } get dateFormatted() { diff --git a/app/routes/agenda-items.ts b/app/routes/agenda-items.ts index 2dd0ca44..d07270b5 100644 --- a/app/routes/agenda-items.ts +++ b/app/routes/agenda-items.ts @@ -26,8 +26,10 @@ const getQuery = ({ }): AgendaItemsRequestInterface => ({ // exclude sessions without governing body and administrative unit //todo investigate why filtering is not working - include: + include: [ 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', + 'sessions.governing-body.administrative-unit.location', + ].join(','), sort: '-sessions.planned-start', filter: { sessions: { diff --git a/app/routes/detail.ts b/app/routes/detail.ts index c41bbee5..164484d1 100644 --- a/app/routes/detail.ts +++ b/app/routes/detail.ts @@ -14,6 +14,7 @@ const agendaItemIncludes = [ 'handled-by.has-votes.has-proponents.alias', 'handled-by.has-votes.has-proponents.has-membership.inner-group', 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', + 'sessions.governing-body.administrative-unit.location', ].join(','); export default class DetailRoute extends Route { diff --git a/app/routes/map.ts b/app/routes/map.ts index a5a7999a..57208d56 100644 --- a/app/routes/map.ts +++ b/app/routes/map.ts @@ -41,8 +41,10 @@ export default class MapRoute extends Route { page: { size: 600, }, - include: + include: [ 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', + 'sessions.governing-body.administrative-unit.location', + ].join(','), filter: { ':has:sessions': true, sessions: { diff --git a/app/routes/municipality.ts b/app/routes/municipality.ts index 614f5313..e485d2b8 100644 --- a/app/routes/municipality.ts +++ b/app/routes/municipality.ts @@ -38,8 +38,10 @@ export default class MunicipalityRoute extends Route { size: 10, }, municipality: municipality, - include: + include: [ 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', + 'sessions.governing-body.administrative-unit.location', + ].join(','), filter: {}, }; diff --git a/app/routes/sessions/index.ts b/app/routes/sessions/index.ts index 66f05196..4b8bd5fb 100644 --- a/app/routes/sessions/index.ts +++ b/app/routes/sessions/index.ts @@ -34,6 +34,7 @@ const getQuery = ( }, include: [ 'governing-body.is-time-specialization-of.administrative-unit.location', + 'governing-body.administrative-unit.location', 'agenda-items', ].join(','), sort: '-planned-start', diff --git a/app/routes/sessions/session.ts b/app/routes/sessions/session.ts index 32b60769..006dfd18 100644 --- a/app/routes/sessions/session.ts +++ b/app/routes/sessions/session.ts @@ -16,6 +16,7 @@ export default class SessionRoute extends Route { (await this.store.findRecord('session', session_id, { include: [ 'governing-body.is-time-specialization-of.administrative-unit.location', + 'governing-body.administrative-unit.location', 'agenda-items', ].join(','), })); From fbfcd01d0bc970c1d3eb65c75a60f8e408c3af88 Mon Sep 17 00:00:00 2001 From: Laurent Deketelaere Date: Wed, 12 Jul 2023 10:36:25 +0200 Subject: [PATCH 5/8] Remove not working filtering --- app/routes/agenda-items.ts | 8 -------- app/routes/map.ts | 21 --------------------- app/routes/sessions/index.ts | 6 ------ 3 files changed, 35 deletions(-) diff --git a/app/routes/agenda-items.ts b/app/routes/agenda-items.ts index d07270b5..b74c8525 100644 --- a/app/routes/agenda-items.ts +++ b/app/routes/agenda-items.ts @@ -24,8 +24,6 @@ const getQuery = ({ plannedStartMin?: string; plannedStartMax?: string; }): AgendaItemsRequestInterface => ({ - // exclude sessions without governing body and administrative unit - //todo investigate why filtering is not working include: [ 'sessions.governing-body.is-time-specialization-of.administrative-unit.location', 'sessions.governing-body.administrative-unit.location', @@ -35,11 +33,8 @@ const getQuery = ({ sessions: { ':gt:planned-start': plannedStartMin ? plannedStartMin : undefined, ':lt:planned-start': plannedStartMax ? plannedStartMax : undefined, - ':has:governing-body': true, 'governing-body': { - ':has:is-time-specialization-of': true, 'is-time-specialization-of': { - ':has:administrative-unit': true, 'administrative-unit': { location: { ':id:': locationIds ? locationIds : undefined, @@ -71,11 +66,8 @@ interface AgendaItemsRequestInterface { sessions?: { ':gt:planned-start'?: string; ':lt:planned-start'?: string; - ':has:governing-body'?: boolean; 'governing-body'?: { - ':has:is-time-specialization-of'?: boolean; 'is-time-specialization-of'?: { - ':has:administrative-unit'?: boolean; 'administrative-unit': { location?: unknown; }; diff --git a/app/routes/map.ts b/app/routes/map.ts index 57208d56..5dad237c 100644 --- a/app/routes/map.ts +++ b/app/routes/map.ts @@ -10,19 +10,12 @@ interface AgendaItemsRequestInterface { include: string; municipality?: string; filter?: { - ':has:sessions'?: boolean; sessions?: { ':gt:planned-start'?: string; - - ':has:governing-body'?: boolean; 'governing-body'?: { - ':has:is-time-specialization-of': true; 'is-time-specialization-of': { - ':has:administrative-unit'?: boolean; 'administrative-unit': { - ':has:location'?: boolean; location?: { - ':has:label'?: boolean; label?: string; }; }; @@ -46,26 +39,12 @@ export default class MapRoute extends Route { 'sessions.governing-body.administrative-unit.location', ].join(','), filter: { - ':has:sessions': true, sessions: { ':gt:planned-start': new Date( new Date().setMonth(new Date().getMonth() - 3) ) .toISOString() .split('T')[0], - ':has:governing-body': true, - 'governing-body': { - ':has:is-time-specialization-of': true, - 'is-time-specialization-of': { - ':has:administrative-unit': true, - 'administrative-unit': { - ':has:location': true, - location: { - ':has:label': true, - }, - }, - }, - }, }, }, }; diff --git a/app/routes/sessions/index.ts b/app/routes/sessions/index.ts index 4b8bd5fb..602cabec 100644 --- a/app/routes/sessions/index.ts +++ b/app/routes/sessions/index.ts @@ -13,16 +13,10 @@ const getQuery = ( plannedStartMax?: string, locationIds?: string ) => ({ - // exclude sessions without governing body and administrative unit - //todo investigate why filtering is not working filter: { - ':has:governing-body': true, 'governing-body': { - ':has:is-time-specialization-of': true, 'is-time-specialization-of': { - ':has:administrative-unit': true, 'administrative-unit': { - ':has:location': true, location: { ':id:': locationIds ? locationIds : undefined, }, From 24d322b79c36f1e2ea56c20705c3678cea8a13b7 Mon Sep 17 00:00:00 2001 From: Laurent Deketelaere Date: Wed, 12 Jul 2023 10:54:22 +0200 Subject: [PATCH 6/8] Fix(detail): add missing include in similar agendaItems --- app/routes/detail.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/routes/detail.ts b/app/routes/detail.ts index 164484d1..5509174a 100644 --- a/app/routes/detail.ts +++ b/app/routes/detail.ts @@ -91,6 +91,7 @@ export default class DetailRoute extends Route { size: 4, }, municipality: agendaItem.session?.get('municipality') || undefined, + include: agendaItemIncludes, filter: { sessions: { 'governing-body': { From 9a133e0f695bbe825e08d62dfa9f22d8512fb5ed Mon Sep 17 00:00:00 2001 From: Denperidge Date: Wed, 12 Jul 2023 11:50:34 +0200 Subject: [PATCH 7/8] Added comments based on @aliokan 's explanations --- app/models/agenda-item.ts | 4 ++++ app/models/governing-body.ts | 15 +++++++++++++++ app/models/session.ts | 8 ++++++++ 3 files changed, 27 insertions(+) diff --git a/app/models/agenda-item.ts b/app/models/agenda-item.ts index b037add1..d5c9fa06 100644 --- a/app/models/agenda-item.ts +++ b/app/models/agenda-item.ts @@ -20,6 +20,10 @@ export default class AgendaItemModel extends Model { @belongsTo('agenda-item-handling', { async: true, inverse: null }) declare handledBy?: AsyncBelongsTo; + /** + * @returns the first session with hasMunicipality == true + * This is the session we want to use to resolve municipality & governingBody name + */ get session() { return this.sessions?.slice().find((session) => { return session.hasMunicipality; diff --git a/app/models/governing-body.ts b/app/models/governing-body.ts index baf3b62b..5d439659 100644 --- a/app/models/governing-body.ts +++ b/app/models/governing-body.ts @@ -7,6 +7,21 @@ import Model, { import SessionModel from './session'; import AdministrativeUnitModel from './administrative-unit'; +/** + * There are two types of governing bodies + * + * 1. The abstraction (undated) + * 2. With the time that they are active specified + * + * That time is called a timeSpecialisation (tijdspecialisatie), + * which is defined {@link https://themis.vlaanderen.be/docs/catalogs here (see 2.2.2.2)} + * as "the governing period where a *governing body* + * is appointed through direct elections" + * + * You can view the mandatendatabank specification + * on vlaanderen.be {@link https://data.vlaanderen.be/doc/applicatieprofiel/mandatendatabank here} + * + */ export default class GoverningBodyModel extends Model { @attr('string', { defaultValue: 'Ontbrekende naam' }) declare name: string; diff --git a/app/models/session.ts b/app/models/session.ts index 955b3441..aeeb26f4 100644 --- a/app/models/session.ts +++ b/app/models/session.ts @@ -20,6 +20,14 @@ export default class SessionModel extends Model { @belongsTo('governing-body', { async: false, inverse: 'sessions' }) declare governingBody: GoverningBodyModel; + /** + * @returns + * - ... the session's timeSpecialised governing body's name + * - ... if the above can't be found, the abstracted governing body's name + * - ... if the above can't be found, an error string + * + * This naming scheme is in relation to the app/back-end + */ get name() { return ( this.governingBody?.isTimeSpecializationOf?.name || From ef99f4a1c28404e59cf8eaf25749cd00c7412487 Mon Sep 17 00:00:00 2001 From: Laurent Deketelaere Date: Wed, 12 Jul 2023 23:21:21 +0200 Subject: [PATCH 8/8] Refactor: Improvements based on @Windvis remarks - Improve accessing async relationships in `AgendaItemModel` and `DetailRoute` - Improve typing in `AgendaItemModel` and `DetailRoute` - start vote code cleaning --- app/controllers/detail.ts | 4 +-- app/controllers/map.ts | 4 +-- app/models/agenda-item.ts | 7 ++++- app/routes/detail.ts | 64 ++++++++++++++++++--------------------- app/templates/detail.hbs | 14 +++------ 5 files changed, 44 insertions(+), 49 deletions(-) diff --git a/app/controllers/detail.ts b/app/controllers/detail.ts index 3c23291f..ed3f9679 100644 --- a/app/controllers/detail.ts +++ b/app/controllers/detail.ts @@ -9,10 +9,10 @@ export default class DetailController extends Controller { declare model: ModelFrom; get hasVotes() { - return this.model.agendaItem?.handledBy?.get('hasVotes')?.length > 0; + return !!this.model.vote; } get municipalityQuery() { - return { gemeentes: this.model.agendaItem.session.municipality }; + return { gemeentes: this.model.agendaItem.session?.municipality }; } } diff --git a/app/controllers/map.ts b/app/controllers/map.ts index d30fea60..f7433c37 100644 --- a/app/controllers/map.ts +++ b/app/controllers/map.ts @@ -76,9 +76,7 @@ export default class MapComponent extends Controller { .attr('fill', (locationDataItem: Feature) => { const name = locationDataItem.properties?.['name_nl']; this.agendaData.forEach((agendaItem: AgendaItemModel) => { - const agendaItemLocation = agendaItem - .get('session') - ?.get('municipality'); + const agendaItemLocation = agendaItem.session?.municipality; if (name === agendaItemLocation) { const datenow = new Date(Date.now()).setHours(0, 0, 0, 0); const f = agendaItem.session?.plannedStart; diff --git a/app/models/agenda-item.ts b/app/models/agenda-item.ts index d5c9fa06..ea88a1da 100644 --- a/app/models/agenda-item.ts +++ b/app/models/agenda-item.ts @@ -4,6 +4,7 @@ import Model, { hasMany, AsyncHasMany, AsyncBelongsTo, + SyncHasMany, } from '@ember-data/model'; import AgendaItemHandlingModel from './agenda-item-handling'; import SessionModel from './session'; @@ -25,7 +26,11 @@ export default class AgendaItemModel extends Model { * This is the session we want to use to resolve municipality & governingBody name */ get session() { - return this.sessions?.slice().find((session) => { + // cast this because of https://github.com/typed-ember/ember-cli-typescript/issues/1416 + const sessions: SyncHasMany | null = (this as AgendaItemModel) + .hasMany('sessions') + ?.value(); + return sessions?.find((session) => { return session.hasMunicipality; }); } diff --git a/app/routes/detail.ts b/app/routes/detail.ts index 238d1cca..ee00f7e5 100644 --- a/app/routes/detail.ts +++ b/app/routes/detail.ts @@ -1,6 +1,8 @@ import Store from '@ember-data/store'; import Route from '@ember/routing/route'; import { service } from '@ember/service'; +import AgendaItemModel from 'frontend-burgernabije-besluitendatabank/models/agenda-item'; +import MandataryModel from 'frontend-burgernabije-besluitendatabank/models/mandatary'; import KeywordStoreService from 'frontend-burgernabije-besluitendatabank/services/keyword-store'; import { sortObjectsByTitle } from 'frontend-burgernabije-besluitendatabank/utils/array-utils'; @@ -9,9 +11,9 @@ interface DetailParams { } interface FormattedTableVote { - proponent: Array; - opponent: Array; - abstainer: Array; + proponent: MandataryModel | null; + opponent: MandataryModel | null; + abstainer: MandataryModel | null; } const agendaItemIncludes = [ @@ -32,11 +34,15 @@ export default class DetailRoute extends Route { @service declare keywordStore: KeywordStoreService; async model(params: DetailParams) { - const agendaItem = await this.store.findRecord('agenda-item', params.id, { - include: agendaItemIncludes, - }); + const agendaItem: AgendaItemModel = await this.store.findRecord( + 'agenda-item', + params.id, + { + include: agendaItemIncludes, + } + ); - const sessionId = agendaItem.session?.get('id'); + const sessionId = agendaItem.session?.id; const agendaItemOnSameSessionRaw = sessionId ? await this.store.query('agenda-item', { include: agendaItemIncludes, @@ -60,39 +66,28 @@ export default class DetailRoute extends Route { // this means that the length of the formattedTableVote array will be the length of the longest array (proponent, opponent, abstainer) // if there is no voter, push an empty object + const agendaItemHandling = await agendaItem.handledBy; + const vote = (await agendaItemHandling?.hasVotes)?.toArray().shift(); + + //TODO: The vote formatting is not the responssibility of the route. Move this logic to the component. + // https://binnenland.atlassian.net/browse/BNB-246 + const proponents = (await vote?.hasProponents)?.toArray(); + const opponents = (await vote?.hasOpponents)?.toArray(); + const abstainers = (await vote?.hasAbstainers)?.toArray(); // get the length of the longest array (not the accumaleted length of all 3 arrays) const longestArrayLength = Math.max( - agendaItem.handledBy - .get('hasVotes') - ?.toArray()[0] - ?.hasProponents.toArray().length || 0, - agendaItem.handledBy.get('hasVotes')?.toArray()[0]?.hasOpponents.toArray() - .length || 0, - agendaItem.handledBy - .get('hasVotes') - ?.toArray()[0] - ?.hasAbstainers.toArray().length || 0 + proponents?.length || 0, + opponents?.length || 0, + abstainers?.length || 0 ); // iterate over the longest array for (let i = 0; i < longestArrayLength; i++) { // push the proponent, opponent and abstainer of the current iteration to the formattedTableVote array formattedTableVote.push({ - proponent: - agendaItem.handledBy - .get('hasVotes') - .toArray()[0] - ?.hasProponents?.toArray()[i] || undefined, - opponent: - agendaItem.handledBy - .get('hasVotes') - .toArray()[0] - ?.hasOpponents?.toArray()[i] || undefined, - abstainer: - agendaItem.handledBy - .get('hasVotes') - .toArray()[0] - ?.hasAbstainers?.toArray()[i] || undefined, + proponent: proponents?.[i] || null, + opponent: opponents?.[i] || null, + abstainer: abstainers?.[i] || null, }); } @@ -100,7 +95,7 @@ export default class DetailRoute extends Route { page: { size: 4, }, - municipality: agendaItem.session?.get('municipality') || undefined, + municipality: agendaItem.session?.municipality, include: agendaItemIncludes, filter: { sessions: { @@ -108,7 +103,7 @@ export default class DetailRoute extends Route { 'is-time-specialization-of': { 'administrative-unit': { location: { - label: agendaItem.session?.get('municipality') || undefined, + label: agendaItem.session?.municipality, }, }, }, @@ -127,6 +122,7 @@ export default class DetailRoute extends Route { return { agendaItem, + vote, agendaItemOnSameSession, formattedTableVote, similiarAgendaItems, diff --git a/app/templates/detail.hbs b/app/templates/detail.hbs index 305e1ddb..dee19e91 100644 --- a/app/templates/detail.hbs +++ b/app/templates/detail.hbs @@ -109,15 +109,11 @@ @iconClosed={{"nav-right"}} @buttonLabel={{"Stemming"}} > - {{#each this.model.agendaItem.handledBy.hasVotes as |vote|}} - - - - {{/each}} +