import { Injectable } from "@angular/core" ; import { Entity , EntityConstructor } from "../../core/entity/model/entity" ; import { Child } from "../../child-dev-project/children/model/child" ; import { School } from "../../child-dev-project/schools/model/school" ; import { RecurringActivity } from "../../child-dev-project/attendance/model/recurring-activity" ; import { Note } from "../../child-dev-project/notes/model/note" ; import { EventNote } from "../../child-dev-project/attendance/model/event-note" ; import { EntityMapperService } from "../../core/entity/entity-mapper.service" ; import { ChildSchoolRelation } from "../../child-dev-project/children/model/childSchoolRelation" ; import { ChildrenService } from "../../child-dev-project/children/children.service" ; import { AttendanceService } from "../../child-dev-project/attendance/attendance.service" ; import { EventAttendance } from "../../child-dev-project/attendance/model/event-attendance" ; const jsonQuery = require ( "json-query" ) ; /**
 * A query service which uses the json-query library (https://github.com/auditassistant/json-query).
 */ @ Injectable ( { providedIn : "root" , } ) export class QueryService { private entities : { [ type ] : { [ id ] : Entity ; } ; } ; private dataAvailableFrom : Date ; constructor ( private entityMapper , private childrenService , private attendanceService ) { } param query param from param to param data returns public async queryData ( query , from = null , to = null , data ? ) { if ( from === null ) { from = new Date ( 0 ) ; } if ( from < this . dataAvailableFrom || ! this . dataAvailableFrom ) { await this . loadData ( from ) ; } if ( ! data ) { data = this . entities ; } return jsonQuery ( [ query , from , to ] , { data : data , locals : { toArray : this . toArray , unique : this . unique , count : this . count , addPrefix : this . addPrefix , toEntities : this . toEntities . bind ( this ) , getRelated : this . getRelated . bind ( this ) , filterByObjectAttribute : this . filterByObjectAttribute , getIds : this . getIds , getParticipantsWithAttendance : this . getParticipantsWithAttendance , getAttendanceArray : this . getAttendanceArray , addEntities : this . addEntities . bind ( this ) , } , } ) . value ; } private async loadData ( from ) { const entityClasses = [ [ Child , ( ) => this . entityMapper . loadType ( Child ) ] , [ School , ( ) => this . entityMapper . loadType ( School ) ] , [ RecurringActivity , ( ) => this . entityMapper . loadType ( RecurringActivity ) ] , [ ChildSchoolRelation , ( ) => this . entityMapper . loadType ( ChildSchoolRelation ) , ] , [ Note , ( ) => this . childrenService . getNotesInTimespan ( from ) ] , [ EventNote , ( ) => this . attendanceService . getEventsOnDate ( from , new Date ( ) ) , ] , ] ; this . entities = { } ; const dataPromises = [ ] ; for ( const entityLoader of entityClasses ) { const promise = entityLoader [ 1 ] ( ) . then ( ( loadedEntities ) => this . setEntities ( entityLoader [ 0 ] , loadedEntities ) ) ; dataPromises . push ( promise ) ; } await Promise . all ( dataPromises ) ; this . dataAvailableFrom = from ; } private setEntities < T extends Entity > ( entityClass , entities ) { this . entities [ entityClass . ENTITY_TYPE ] = { } ; entities . forEach ( ( entity ) => ( this . entities [ entityClass . ENTITY_TYPE ] [ entity . _id ] = entity ) ) ; } param ids param prefix returns addPrefix ( ids , prefix ) { return ids . map ( ( id ) => ( id . startsWith ( prefix ) ? id : prefix + ":" + id ) ) ; } param obj returns toArray ( obj ) { return Object . values ( obj ) ; } param data returns unique ( data ) { return new Array ( ... new Set ( data ) ) ; } param data returns count ( data ) { return data ? data . length : 0 ; } param ids param entityPrefix returns toEntities ( ids , entityPrefix ? ) { if ( ! ids ) { return [ ] ; } if ( entityPrefix ) { ids = this . addPrefix ( ids , entityPrefix ) ; } return ids . map ( ( id ) => { const prefix = id . split ( ":" ) [ 0 ] ; return this . entities [ prefix ] [ id ] ; } ) ; } param srcEntities param entityType param relationKey returns getRelated ( srcEntities , entityType , relationKey ) { const targetEntities = this . toArray ( this . entities [ entityType ] ) ; const srcIds = srcEntities . map ( ( entity ) => entity . getId ( ) ) ; if ( targetEntities . length > 0 && Array . isArray ( targetEntities [ 0 ] [ relationKey ] ) ) { return targetEntities . filter ( ( entity ) => ( entity [ relationKey ] as Array < string > ) . some ( ( id ) => srcIds . includes ( id . split ( ":" ) . pop ( ) ) ) ) ; } else { return targetEntities . filter ( ( entity ) => entity [ relationKey ] ? srcIds . includes ( entity [ relationKey ] . split ( ":" ) . pop ( ) ) : false ) ; } } param objs param attr param key param value returns filterByObjectAttribute ( objs , attr , key , value ) { const values = value . replace ( new RegExp ( " " , "g" ) , "" ) . split ( "|" ) ; return objs . filter ( ( obj ) => { if ( obj ?. hasOwnProperty ( attr ) ) { return values . includes ( obj [ attr ] [ key ] ?. toString ( ) ) ; } return false ; } ) ; } param objs param key returns getIds ( objs , key ) { const ids = [ ] ; objs . forEach ( ( obj ) => { if ( obj . hasOwnProperty ( key ) ) { ids . push ( ... obj [ key ] ) ; } } ) ; return ids ; } param events param attendanceStatus returns getParticipantsWithAttendance ( events , attendanceStatus ) { const attendedChildren = [ ] ; events . forEach ( ( e ) => e . children . forEach ( ( childId ) => { if ( e . getAttendance ( childId ) . status . countAs === attendanceStatus ) { attendedChildren . push ( childId ) ; } } ) ) ; return attendedChildren ; } getAttendanceArray ( events ) { return events . map ( ( e ) => e . children . map ( ( childId ) => ( { participant : childId , status : e . getAttendance ( childId ) , } ) ) ) . reduce ( ( acc , val ) => acc . concat ( val ) , [ ] ) ; } param entities param entityType returns addEntities ( entities , entityType ) { return entities . concat ( ... this . toArray ( this . entities [ entityType ] ) ) ; } }