/**
 * 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 { CombinationConstraint } from '../models/constraint-models/combination-constraint' ; import { Constraint } from '../models/constraint-models/constraint' ; import { TrueConstraint } from '../models/constraint-models/true-constraint' ; import { StudyConstraint } from '../models/constraint-models/study-constraint' ; import { Concept } from '../models/constraint-models/concept' ; import { ConceptConstraint } from '../models/constraint-models/concept-constraint' ; import { CombinationState } from '../models/constraint-models/combination-state' ; import { TreeNodeService } from './tree-node.service' ; import { PedigreeConstraint } from '../models/constraint-models/pedigree-constraint' ; import { ResourceService } from './resource.service' ; import { ConstraintHelper } from '../utilities/constraint-utilities/constraint-helper' ; import { Pedigree } from '../models/constraint-models/pedigree' ; import { MessageHelper } from '../utilities/message-helper' ; import { StudyService } from './study.service' ; import { CountService } from './count.service' ; import { Dimension } from '../models/constraint-models/dimension' ; import { Subject } from 'rxjs' ; /**
 * This service concerns with
 * (1) translating string or JSON objects into Constraint class instances
 * (2) clear or restore constraints
 */ @ Injectable ( { providedIn : 'root' , } ) export class ConstraintService { private _rootConstraint : CombinationConstraint ; private _allConstraints : Constraint [ ] = [ ] ; private _studyConstraints : Constraint [ ] = [ ] ; private _validPedigreeTypes : object [ ] = [ ] ; private _concepts : Concept [ ] = [ ] ; private _conceptConstraints : Constraint [ ] = [ ] ; private _allSubjectDimensions : Dimension [ ] = [ ] ; private _validSubjectDimensionsUpdated : Subject < boolean > = new Subject < boolean > ( ) ; private _maxNumSearchResults = 100 ; constructor ( private treeNodeService , private studyService , private countService , private resourceService ) { this . rootConstraint = new CombinationConstraint ( ) ; this . rootConstraint . isRoot = true ; this . loadEmptyConstraints ( ) ; this . loadAvailableSubjectDimensions ( ) ; this . loadStudiesConstraints ( ) ; this . loadPedigrees ( ) ; this . countService . loadCountMaps ( ) . then ( ( ) => { this . treeNodeService . load ( ) ; } ) . catch ( err => { console . error ( err ) ; } ) ; } private loadEmptyConstraints ( ) { this . allConstraints . push ( new CombinationConstraint ( ) ) ; this . allConstraints . push ( new StudyConstraint ( ) ) ; this . allConstraints . push ( new ConceptConstraint ( ) ) ; } private loadAvailableSubjectDimensions ( ) { this . resourceService . subjectDimensions . subscribe ( ( allSubjectDimensions ) => { this . allSubjectDimensions = allSubjectDimensions ; } ) ; } private loadStudiesConstraints ( ) { this . studyService . studiesLoaded . asObservable ( ) . subscribe ( ( studiesLoaded ) => { if ( studiesLoaded ) { this . studyConstraints = [ ] ; this . studyService . studies . forEach ( study => { let constraint = new StudyConstraint ( ) ; constraint . studies . push ( study ) ; this . studyConstraints . push ( constraint ) ; this . allConstraints . push ( constraint ) ; } ) ; } else { MessageHelper . alert ( 'info' , 'No studies found' ) ; } } , err => console . error ( err ) ) ; } private loadPedigrees ( ) { this . resourceService . getPedigrees ( ) . subscribe ( ( pedigrees ) => { for ( let p of pedigrees ) { let pedigreeConstraint = new PedigreeConstraint ( p . label ) ; this . allConstraints . push ( pedigreeConstraint ) ; this . validPedigreeTypes . push ( { type : pedigreeConstraint . relationType , text : pedigreeConstraint . textRepresentation } ) ; } } , err => console . error ( err ) ) ; } param searchWord returns { Array } public searchAllConstraints ( searchWord ) { searchWord = searchWord . toLowerCase ( ) ; let results = [ ] ; if ( searchWord === '' ) { results = [ ] . concat ( this . allConstraints . slice ( 0 , this . maxNumSearchResults ) ) ; } else if ( searchWord && searchWord . length > 0 ) { let count = 0 ; this . allConstraints . forEach ( ( constraint ) => { let text = constraint . textRepresentation . toLowerCase ( ) ; if ( text . indexOf ( searchWord ) > - 1 ) { results . push ( constraint ) ; count ++ ; if ( count >= this . maxNumSearchResults ) { return results ; } } } ) ; } return results ; } returns { Constraint } get cohortSelectionConstraint ( ) : Constraint { return this . rootConstraint ; } returns { Constraint } public variableConstraint ( variables ) { const hasUnselected = variables . some ( ( variable ) => { return ! variable . selected ; } ) ; if ( hasUnselected ) { let result = new CombinationConstraint ( ) ; result . combinationState = CombinationState . Or ; result . dimension = 'observation' ; variables . filter ( ( variable ) => { return variable . selected ; } ) . forEach ( ( variable ) => { let c = new ConceptConstraint ( ) ; c . concept = variable ; result . addChild ( c ) ; } ) ; return result ; } else { return new TrueConstraint ( ) ; } } /**
     * Clear the patient constraints
     */ public clearCohortConstraint ( ) { this . rootConstraint . children . length = 0 ; } public restoreCohortConstraint ( cohortConstraint ) { const constraint = cohortConstraint . clone ( ) ; this . restoreSubjectDimensions ( constraint ) ; if ( constraint . className === 'CombinationConstraint' && ! constraint . negated ) { this . rootConstraint . dimension = ( < CombinationConstraint > constraint ) . dimension ; const children = ( < CombinationConstraint > constraint ) . children ; for ( let child of children ) { this . rootConstraint . addChild ( child ) ; } this . rootConstraint . combinationState = ( < CombinationConstraint > constraint ) . combinationState ; } else if ( constraint . className !== 'TrueConstraint' ) { this . rootConstraint . dimension = CombinationConstraint . TOP_LEVEL_DIMENSION ; this . rootConstraint . addChild ( constraint ) ; } this . validSubjectDimensionsUpdated . next ( ) ; } private restoreSubjectDimensions ( constraint ) { if ( constraint . className === 'ConceptConstraint' ) { let concept = this . concepts . find ( c => c . code === ( < ConceptConstraint > constraint ) . concept . code ) ; if ( concept ) { ( < ConceptConstraint > constraint ) . concept . subjectDimensions = concept . subjectDimensions ; } } else if ( constraint . className === 'CombinationConstraint' ) { for ( let child of ( < CombinationConstraint > constraint ) . children ) { this . restoreSubjectDimensions ( child ) ; } } } get isTreeNodesLoading ( ) : boolean { return ! this . treeNodeService . isTreeNodesLoadingCompleted ; } get rootConstraint ( ) : CombinationConstraint { return this . _rootConstraint ; } set rootConstraint ( value ) { this . _rootConstraint = value ; } get allConstraints ( ) : Constraint [ ] { return this . _allConstraints ; } set allConstraints ( value ) { this . _allConstraints = value ; } get studyConstraints ( ) : Constraint [ ] { return this . _studyConstraints ; } set studyConstraints ( value ) { this . _studyConstraints = value ; } get validPedigreeTypes ( ) : object [ ] { return this . _validPedigreeTypes ; } set validPedigreeTypes ( value ) { this . _validPedigreeTypes = value ; } get conceptConstraints ( ) : Constraint [ ] { return this . _conceptConstraints ; } set conceptConstraints ( value ) { this . _conceptConstraints = value ; } get concepts ( ) : Concept [ ] { return this . _concepts ; } set concepts ( value ) { this . _concepts = value ; } get maxNumSearchResults ( ) : number { return this . _maxNumSearchResults ; } set maxNumSearchResults ( value ) { this . _maxNumSearchResults = value ; } get allSubjectDimensions ( ) : Dimension [ ] { return this . _allSubjectDimensions ; } set allSubjectDimensions ( values ) { this . _allSubjectDimensions = values ; this . validSubjectDimensionsUpdated . next ( ) ; } get validSubjectDimensionsUpdated ( ) : Subject < boolean > { return this . _validSubjectDimensionsUpdated ; } }