/**
 * Copyright 2017 - 2019  The Hyve B.V.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */ import { Injectable } from '@angular/core' ; import { ResourceService } from './resource.service' ; import { Cohort } from '../models/cohort-models/cohort' ; import { ConstraintService } from './constraint.service' ; import { AppConfig } from '../config/app.config' ; import { CohortDiffRecord } from '../models/cohort-models/cohort-diff-record' ; import { CohortSetType } from '../models/cohort-models/cohort-set-type' ; import { CohortDiffItem } from '../models/cohort-models/cohort-diff-item' ; import { CohortDiffType } from '../models/cohort-models/cohort-diff-type' ; import { CohortSubscriptionFrequency } from '../models/cohort-models/cohort-subscription-frequency' ; import { MessageHelper } from '../utilities/message-helper' ; import { ErrorHelper } from '../utilities/error-helper' ; import { TrueConstraint } from '../models/constraint-models/true-constraint' ; import { CombinationConstraint } from '../models/constraint-models/combination-constraint' ; import { CombinationState } from '../models/constraint-models/combination-state' ; import { Subject } from 'rxjs' ; import { CountService } from './count.service' ; import { SubjectSetConstraint } from '../models/constraint-models/subject-set-constraint' ; import { SubjectSet } from '../models/constraint-models/subject-set' ; import { Constraint } from '../models/constraint-models/constraint' ; /**
 * This service concerns with
 * Saving / Updating / Restoring / Deleting cohorts in the 'Cohorts' panel on the left
 *
 * workflow:
 * - when the user changes the constraint(s) inside gb-cohort-selection:
 *          updateCountsWithCurrentCohort() -> updateCountsWithAllCohorts()
 * - when the user changes cohort selection inside gb-cohorts:
 *          updateCountsWithAllCohorts()
 */ @ Injectable ( { providedIn : 'root' , } ) export class CohortService { private _currentCohort : Cohort ; private _cohorts : Cohort [ ] = [ ] ; private _isUpdatingCurrent = false ; private _isUpdatingAll = false ; private _isDirty = true ; private _cohortsUpdated : Subject < Cohort [ ] > = new Subject < Cohort [ ] > ( ) ; private _instantCohortCountsUpdate : boolean ; private _saveSubjectSetBeforeUpdatingCounts : boolean ; private _isCohortSubscriptionIncluded = false ; private _isSavingCohortCompleted = true ; constructor ( private appConfig , private resourceService , private constraintService , private countService ) { this . isCohortSubscriptionIncluded = this . appConfig . getConfig ( 'include-cohort-subscription' ) ; this . instantCohortCountsUpdate = this . appConfig . getConfig ( 'instant-cohort-counts-update' ) ; this . saveSubjectSetBeforeUpdatingCounts = this . appConfig . getConfig ( 'autosave-subject-sets' ) ; this . createCurrentCohort ( ) ; this . loadCohorts ( ) ; this . updateCountsWithCurrentCohort ( ) ; } /**
     * ----------------------------- Update the current cohort panel -----------------------------
     */ public updateCountsWithCurrentCohort ( ) { return new Promise ( ( resolve , reject ) => { if ( this . isDirty ) { this . isUpdatingCurrent = true ; let constraint = this . constraintService . cohortSelectionConstraint ; this . countService . updateCurrentSelectionCount ( constraint ) . then ( ( ) => { this . currentCohort . constraint = constraint . clone ( ) ; this . currentCohort . updateDate = new Date ( ) . toISOString ( ) ; if ( constraint . className === 'CombinationConstraint' ) { this . currentCohort . type = ( < CombinationConstraint > constraint ) . dimension ; } this . isUpdatingCurrent = false ; this . isDirty = false ; if ( this . currentCohort . selected ) { this . updateCountsWithAllCohorts ( ) . then ( ( ) => { resolve ( true ) ; } ) . catch ( err => { reject ( err ) ; } ) ; } else { resolve ( true ) ; } } ) . catch ( err => { reject ( err ) ; } ) ; } else { resolve ( true ) ; } } ) ; } /**
     * ----------------------------- Update the queries on the left-side panel -----------------------------
     */ loadCohorts ( ) { this . resourceService . getCohorts ( ) . subscribe ( ( cohorts ) => { this . handleLoadedCohorts ( cohorts ) ; } ) ; } handleLoadedCohorts ( cohorts ) { this . cohorts . length = 0 ; let bookmarkedCohorts = [ ] ; cohorts . forEach ( c => { if ( c . subscribed ) { this . resourceService . diffCohort ( c . id ) . subscribe ( ( records ) => { c . diffRecords = this . parseCohortDiffRecords ( records ) ; } ) ; } if ( c . bookmarked ) { bookmarkedCohorts . push ( c ) ; } else { this . cohorts . push ( c ) ; } } ) ; this . cohorts = [ this . currentCohort ] . concat ( bookmarkedCohorts ) . concat ( this . cohorts ) ; } private createCurrentCohort ( ) { let current = new Cohort ( '' , 'Unsaved cohort (currently editing)' ) ; current . createDate = new Date ( ) . toISOString ( ) ; current . updateDate = new Date ( ) . toISOString ( ) ; current . selected = true ; current . controlsEnabled = false ; current . constraint = new TrueConstraint ( ) ; this . currentCohort = current ; } public updateCountsWithAllCohorts ( ) { return new Promise ( ( resolve , reject ) => { console . log ( 'Updating counts from all cohorts...' ) ; this . isUpdatingAll = true ; if ( this . saveSubjectSetBeforeUpdatingCounts ) { this . resourceService . saveSubjectSet ( 'temp' , this . allSelectedCohortsConstraint ) . subscribe ( ( subjectSet ) => { return this . updateAllCounts ( new SubjectSetConstraint ( subjectSet ) ) . then ( ( ) => resolve ( true ) ) . catch ( err => { reject ( err ) ; } ) ; } ) ; } else { return this . updateAllCounts ( this . allSelectedCohortsConstraint ) . then ( ( ) => resolve ( true ) ) . catch ( err => { reject ( err ) ; } ) ; } } ) ; } private updateAllCounts ( constraint ) { return new Promise ( ( resolve , reject ) => { return this . countService . updateAllCounts ( constraint ) . then ( ( ) => { this . isUpdatingAll = false ; this . cohortsUpdated . next ( this . cohorts ) ; resolve ( true ) ; } ) . catch ( err => { reject ( err ) ; } ) ; } ) ; } public saveCohortByName ( name ) { let result = new Cohort ( '' , name ) ; let constraint = this . constraintService . cohortSelectionConstraint ; result . constraint = constraint ; if ( constraint . className === 'CombinationConstraint' ) { result . type = ( < CombinationConstraint > constraint ) . dimension ; } this . saveCohort ( result ) ; } public saveCohort ( target ) { this . isSavingCohortCompleted = false ; this . resourceService . saveCohort ( target ) . subscribe ( ( newlySaved ) => { newlySaved . collapsed = true ; newlySaved . visible = true ; this . cohorts . push ( newlySaved ) ; this . isSavingCohortCompleted = true ; const summary = 'Cohort "' + newlySaved . name + '" is added.' ; MessageHelper . alert ( 'success' , summary ) ; } , ( err ) => { console . error ( err ) ; this . isSavingCohortCompleted = true ; const summary = 'Could not add the query "' + target . name + '".' ; MessageHelper . alert ( 'error' , summary ) ; } ) ; } param { Cohort } cohort public restoreCohort ( cohort ) { return new Promise ( ( resolve , reject ) => { MessageHelper . alert ( 'info' , `Start importing cohort "${ cohort . name }".` ) ; if ( cohort . constraint ) { this . currentCohort . constraint = cohort . constraint ; this . constraintService . clearCohortConstraint ( ) ; this . constraintService . restoreCohortConstraint ( cohort . constraint ) ; this . isDirty = true ; this . updateCountsWithCurrentCohort ( ) . then ( ( ) => { MessageHelper . alert ( 'info' , 'Success' , `Cohort ${ cohort . name } is successfully imported.` ) ; this . currentCohort . selected = true ; resolve ( true ) ; } ) . catch ( err => { MessageHelper . alert ( 'error' , 'Fail to restore query ' , cohort . name ) ; reject ( err ) ; } ) ; } else { reject ( `Cohort "${ cohort . name }" does not have valid constraint to restore.` ) ; } } ) ; } public editCohort ( target , obj ) { this . resourceService . editCohort ( target . id , obj ) . subscribe ( ( ) => { if ( target . subscribed ) { this . resourceService . diffCohort ( target . id ) . subscribe ( records => { target . diffRecords = this . parseCohortDiffRecords ( records ) ; } ) ; } } , err => ErrorHelper . handleError ( err ) ) ; } public deleteCohort ( target ) { this . resourceService . deleteCohort ( target [ 'id' ] ) . subscribe ( ( ) => { const index = this . cohorts . indexOf ( target ) ; if ( index > - 1 ) { this . cohorts . splice ( index , 1 ) ; } } , err => ErrorHelper . handleError ( err ) ) ; } public parseCohortDiffRecords ( records ) { let diffRecords = [ ] ; for ( let record of records ) { let items = [ ] ; if ( record [ 'objectsAdded' ] ) { for ( let objectId of record [ 'objectsAdded' ] ) { let item = new CohortDiffItem ( ) ; item . objectId = objectId ; item . diffType = CohortDiffType . ADDED ; items . push ( item ) ; } } if ( record [ 'objectsRemoved' ] ) { for ( let objectId of record [ 'objectsRemoved' ] ) { let item = new CohortDiffItem ( ) ; item . objectId = objectId ; item . diffType = CohortDiffType . REMOVED ; items . push ( item ) ; } } if ( items . length > 0 ) { let diffRecord = new CohortDiffRecord ( ) ; diffRecord . id = record [ 'id' ] ; diffRecord . queryName = record [ 'queryName' ] ; diffRecord . queryUsername = record [ 'queryUsername' ] ; diffRecord . setId = record [ 'setId' ] ; diffRecord . setType = record [ 'setType' ] === 'PATIENT' ? CohortSetType . PATIENT : CohortSetType . SAMPLE ; diffRecord . createDate = record [ 'createDate' ] ; diffRecord . diffItems = items ; diffRecords . push ( diffRecord ) ; } } return diffRecords ; } public toggleCohortSubscription ( target ) { target . subscribed = ! target . subscribed ; let obj = { subscribed : target . subscribed } ; if ( target . subscribed ) { obj [ 'subscriptionFreq' ] = target . subscriptionFreq ? target . subscriptionFreq : CohortSubscriptionFrequency . WEEKLY ; target . subscriptionFreq = obj [ 'subscriptionFreq' ] ; } this . editCohort ( target , obj ) ; } public toggleCohortBookmark ( target ) { target . bookmarked = ! target . bookmarked ; let obj = { bookmarked : target . bookmarked } ; this . editCohort ( target , obj ) ; } get allSelectedCohortsConstraint ( ) : Constraint { if ( this . selectedCohorts . length === 1 ) { let constraint = this . selectedCohorts [ 0 ] . constraint . clone ( ) ; constraint . parentConstraint = null ; return constraint ; } else { const combination = new CombinationConstraint ( ) ; combination . combinationState = CombinationState . Or ; const dimensions = new Set < string > ( ) ; this . cohorts . forEach ( ( cohort ) => { if ( cohort . selected ) { combination . addChild ( cohort . constraint ) ; if ( cohort . constraint . className === 'CombinationConstraint' ) { dimensions . add ( ( < CombinationConstraint > cohort . constraint ) . dimension ) ; } else { dimensions . add ( CombinationConstraint . TOP_LEVEL_DIMENSION ) ; } } } ) ; if ( dimensions . size === 1 ) { combination . dimension = dimensions . values ( ) . next ( ) . value ; } return combination ; } } get cohorts ( ) : Cohort [ ] { return this . _cohorts ; } set cohorts ( value ) { this . _cohorts = value ; } get selectedCohorts ( ) : Cohort [ ] { return this . cohorts . filter ( cohort => cohort . selected ) ; } get currentCohort ( ) : Cohort { return this . _currentCohort ; } set currentCohort ( value ) { this . _currentCohort = value ; } get isDirty ( ) : boolean { return this . _isDirty ; } set isDirty ( value ) { this . _isDirty = value ; } get isUpdatingCurrent ( ) : boolean { return this . _isUpdatingCurrent ; } set isUpdatingCurrent ( value ) { this . _isUpdatingCurrent = value ; } get isUpdatingAll ( ) : boolean { return this . _isUpdatingAll ; } set isUpdatingAll ( value ) { this . _isUpdatingAll = value ; } get isSavingCohortCompleted ( ) : boolean { return this . _isSavingCohortCompleted ; } set isSavingCohortCompleted ( value ) { this . _isSavingCohortCompleted = value ; } get isCohortSubscriptionIncluded ( ) : boolean { return this . _isCohortSubscriptionIncluded ; } set isCohortSubscriptionIncluded ( value ) { this . _isCohortSubscriptionIncluded = value ; } get cohortsUpdated ( ) : Subject < Cohort [ ] > { return this . _cohortsUpdated ; } set cohortsUpdated ( value ) { this . _cohortsUpdated = value ; } get instantCohortCountsUpdate ( ) : boolean { return this . _instantCohortCountsUpdate ; } set instantCohortCountsUpdate ( value ) { this . _instantCohortCountsUpdate = value ; } get saveSubjectSetBeforeUpdatingCounts ( ) : boolean { return this . _saveSubjectSetBeforeUpdatingCounts ; } set saveSubjectSetBeforeUpdatingCounts ( value ) { this . _saveSubjectSetBeforeUpdatingCounts = value ; } }