/**
 * 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 { AsyncSubject , from as observableFrom , Observable } from 'rxjs' ; import { Injectable } from '@angular/core' ; import { Constraint } from '../models/constraint-models/constraint' ; import { Pedigree } from '../models/constraint-models/pedigree' ; import { ExportJob } from '../models/export-models/export-job' ; import { SubjectSet } from '../models/constraint-models/subject-set' ; import { TransmartTableState } from '../models/transmart-models/transmart-table-state' ; import { TransmartDataTable } from '../models/transmart-models/transmart-data-table' ; import { AppConfig } from '../config/app.config' ; import { TransmartCrossTable } from '../models/transmart-models/transmart-cross-table' ; import { TransmartCountItem } from '../models/transmart-models/transmart-count-item' ; import { SubjectSetConstraint } from '../models/constraint-models/subject-set-constraint' ; import { TransmartStudy } from '../models/transmart-models/transmart-study' ; import { CombinationConstraint } from '../models/constraint-models/combination-constraint' ; import { flatMap , map } from 'rxjs/operators' ; import { TransmartTrialVisit } from '../models/transmart-models/transmart-trial-visit' ; import { ExportDataType } from '../models/export-models/export-data-type' ; import { switchMap } from 'rxjs/internal/operators' ; import { TransmartMapper } from '../utilities/transmart-utilities/transmart-mapper' ; import { TransmartPackerMapper } from '../utilities/transmart-utilities/transmart-packer-mapper' ; import { TransmartPackerJob } from '../models/transmart-packer-models/transmart-packer-job' ; import { TransmartDataTableMapper } from '../utilities/transmart-utilities/transmart-data-table-mapper' ; import { DataTable } from '../models/table-models/data-table' ; import { TransmartStudyDimensions } from '../models/transmart-models/transmart-study-dimensions' ; import { TransmartHttpService } from './http/transmart-http.service' ; import { TransmartPackerHttpService } from './http/transmart-packer-http.service' ; import { Study } from '../models/constraint-models/study' ; import { TransmartExportJob } from '../models/transmart-models/transmart-export-job' ; import { TransmartPatient } from '../models/transmart-models/transmart-patient' ; import { TransmartDimension } from '../models/transmart-models/transmart-dimension' ; import { ServerStatus } from '../models/server-status' ; @ Injectable ( { providedIn : 'root' , } ) export class TransmartResourceService { static readonly SUBJECT_DIMENSION_TYPE = 'subject' ; private _exportDataView : string ; private _autosaveSubjectSets : boolean ; private _useExternalExportJob : boolean ; private _subjectSetConstraint : SubjectSetConstraint ; private _counts : TransmartCountItem ; private _status : AsyncSubject < ServerStatus > = new AsyncSubject < ServerStatus > ( ) ; constructor ( private appConfig , private transmartHttpService , private transmartPackerHttpService ) { if ( this . appConfig . isLoaded ) { this . exportDataView = appConfig . getConfig ( 'export-mode' ) [ 'data-view' ] ; this . autosaveSubjectSets = appConfig . getConfig ( 'autosave-subject-sets' ) ; this . useExternalExportJob = appConfig . getConfig ( 'export-mode' ) [ 'name' ] !== 'transmart' ; } this . subjectSetConstraint = new SubjectSetConstraint ( ) ; this . counts = new TransmartCountItem ( ) ; } init ( ) { this . transmartHttpService . getStatus ( ) . subscribe ( status => { if ( status . toLowerCase ( ) === 'up' ) { this . _status . next ( ServerStatus . UP ) ; } else { this . _status . next ( ServerStatus . DOWN ) ; } this . _status . complete ( ) ; } , error => { console . error ( error ) ; this . _status . next ( ServerStatus . ERROR ) ; this . _status . complete ( ) ; } ) ; } get status ( ) : AsyncSubject < ServerStatus > { return this . _status ; } get exportDataView ( ) : string { return this . _exportDataView ; } set exportDataView ( value ) { this . _exportDataView = value ; } get subjectSetConstraint ( ) : SubjectSetConstraint { return this . _subjectSetConstraint ; } set subjectSetConstraint ( value ) { this . _subjectSetConstraint = value ; } get useExternalExportJob ( ) : boolean { return this . _useExternalExportJob ; } set useExternalExportJob ( value ) { this . _useExternalExportJob = value ; } get autosaveSubjectSets ( ) : boolean { return this . _autosaveSubjectSets ; } set autosaveSubjectSets ( value ) { this . _autosaveSubjectSets = value ; } get counts ( ) : TransmartCountItem { return this . _counts ; } set counts ( value ) { this . _counts = value ; } returns { Observable < Study [ ] > } getStudies ( ) { return observableFrom ( this . transmartHttpService . studies ) . pipe ( map ( ( tmStudies ) => { return TransmartMapper . mapTransmartStudies ( tmStudies ) ; } ) ) ; } param { string } root param { number } depth param { boolean } hasCounts param { boolean } hasTags returns { Observable < Object > } getTreeNodes ( root , depth , hasCounts , hasTags ) { return this . transmartHttpService . getTreeNodes ( root , depth , hasCounts , hasTags ) ; } param { Constraint } constraint returns { Promise < any > } updateCohortSelectionCounts ( constraint ) { return new Promise < any > ( ( resolve , reject ) => { if ( this . autosaveSubjectSets ) { this . savePatientSet ( 'temp' , constraint ) . subscribe ( ( subjectSet ) => { this . subjectSetConstraint . id = subjectSet . id ; this . subjectSetConstraint . setSize = subjectSet . setSize ; this . updateStudyConceptCounts ( this . subjectSetConstraint ) . then ( ( ) => { resolve ( true ) ; } ) . catch ( err => { reject ( err ) ; } ) ; } , err => { reject ( err ) ; } ) ; } else { this . updateStudyConceptCounts ( constraint ) . then ( ( ) => { resolve ( true ) ; } ) . catch ( err => { reject ( err ) ; } ) ; } } ) ; } updateStudyConceptCounts ( constraint ) { return new Promise < any > ( ( resolve , reject ) => { this . getCountsPerStudyAndConcept ( constraint ) . subscribe ( ( studyConceptCountObj ) => { let totalCountItem = null ; if ( this . autosaveSubjectSets ) { totalCountItem = new TransmartCountItem ( ) ; let totalObservationCount = 0 ; for ( let studyId in studyConceptCountObj ) { let conceptCount = studyConceptCountObj [ studyId ] ; for ( let conceptCode in conceptCount ) { let countItem = conceptCount [ conceptCode ] ; totalObservationCount += countItem . observationCount ; } } totalCountItem . patientCount = this . subjectSetConstraint . setSize ; totalCountItem . observationCount = totalObservationCount ; } this . updateCounts ( constraint , totalCountItem ) . then ( ( ) => { resolve ( true ) ; } ) . catch ( err => { reject ( 'Fail to update transmart counts.' ) ; } ) ; } , err => { reject ( 'Fail to retrieve study-concept-count object from transmart.' ) ; } ) ; } ) ; } updateCounts ( constraint , totalCountItem ? ) { return new Promise < any > ( ( resolve , reject ) => { if ( this . autosaveSubjectSets ) { if ( ! totalCountItem ) { return reject ( 'Missing transmart total-count item to calculate counts.' ) ; } this . counts . patientCount = totalCountItem . patientCount ; this . counts . observationCount = totalCountItem . observationCount ; resolve ( true ) ; } else { this . getCounts ( constraint ) . subscribe ( ( countItem ) => { this . counts . patientCount = countItem . patientCount ; this . counts . observationCount = countItem . observationCount ; resolve ( true ) ; } , err => { reject ( err ) ; } ) ; } } ) ; } param { Constraint } constraint returns { Observable < Object > } getCountsPerStudyAndConcept ( constraint ) { return this . transmartHttpService . getCountsPerStudyAndConcept ( constraint ) ; } param { Constraint } constraint returns { Observable < Object > } getCountsPerStudy ( constraint ) { return this . transmartHttpService . getCountsPerStudy ( constraint ) ; } param { Constraint } constraint returns { Observable < any > } getCountsPerConcept ( constraint ) { return this . transmartHttpService . getCountsPerConcept ( constraint ) ; } param { Constraint } constraint returns { Observable < Object > } getCounts ( constraint ) { return this . transmartHttpService . getCounts ( constraint ) ; } param { Constraint } constraint returns { Observable < object > } getAggregate ( constraint ) { return this . transmartHttpService . getAggregate ( constraint ) ; } getCategoricalAggregate ( constraint ) { return this . transmartHttpService . getCategoricalAggregate ( constraint ) ; } param constraint returns { Observable < R | T > } getTrialVisits ( constraint ) { return this . transmartHttpService . getTrialVisits ( constraint ) ; } returns { Observable < Object [ ] > } getPedigrees ( ) { return this . transmartHttpService . getPedigrees ( ) ; } param constraint returns { Observable < TransmartPatient [ ] > } getPatients ( constraint ) { return this . transmartHttpService . getPatients ( constraint ) ; } getExportDataTypes ( constraint ) { if ( this . useExternalExportJob ) { return this . transmartPackerHttpService . getExportDataTypes ( ) ; } else { return this . transmartHttpService . getExportFileFormats ( this . exportDataView ) . pipe ( switchMap ( fileFormatNames => { return this . transmartHttpService . getExportDataFormats ( constraint ) ; } , ( fileFormatNames , dataFormatNames ) => { return TransmartMapper . mapTransmartExportFormats ( fileFormatNames , dataFormatNames ) ; } ) ) ; } } returns { Observable < TransmartExportJob [ ] > } getExportJobs ( ) { if ( this . useExternalExportJob ) { return this . transmartPackerHttpService . getAllJobs ( ) . pipe ( map ( ( tmExJobs ) => { return TransmartPackerMapper . mapCustomExportJobs ( tmExJobs ) ; } ) ) ; } else { return this . transmartHttpService . getExportJobs ( ) ; } } param name returns { Observable < ExportJob > } createExportJob ( name ) { if ( this . useExternalExportJob ) { return this . transmartPackerHttpService . createExportJob ( name ) ; } else { return this . transmartHttpService . createExportJob ( name ) ; } } param { string } jobId param { string } jobName param { Constraint } subjectConstraint param { Constraint } variableConstraint param { ExportDataType [ ] } dataTypes param { DataTable } dataTable param { boolean } dateColumnsIncluded returns { Observable < TransmartExportJob > } runExportJob ( jobId , jobName , subjectConstraint , variableConstraint , dataTypes , dataTable , dateColumnsIncluded ) { let targetConstraint = new CombinationConstraint ( ) ; let filterConstraint = subjectConstraint . clone ( ) ; if ( this . autosaveSubjectSets ) { targetConstraint . addChild ( this . subjectSetConstraint ) ; targetConstraint . addChild ( variableConstraint ) ; } else { targetConstraint . addChild ( subjectConstraint ) ; targetConstraint . addChild ( variableConstraint ) ; } if ( this . useExternalExportJob ) { return this . transmartPackerHttpService . runJob ( jobName , targetConstraint , filterConstraint ) . pipe ( map ( ( tmExJob ) => { return TransmartPackerMapper . mapCustomExportJob ( tmExJob ) ; } ) ) ; } else { const elements = TransmartMapper . mapExportDataTypes ( dataTypes , this . exportDataView ) ; if ( this . exportDataView === 'surveyTable' ) { return this . transmartHttpService . runSurveyTableExportJob ( jobId , targetConstraint , elements , dateColumnsIncluded ) ; } else { const includeDataTable = elements . some ( element => element . dataType === 'clinical' && element . format === 'TSV' ) ; if ( includeDataTable ) { if ( dataTable . rowDimensions . length === 0 && dataTable . columnDimensions . length === 0 ) { return this . getDimensions ( targetConstraint ) . pipe ( flatMap ( dimensions => { const transmartTableState = TransmartDataTableMapper . mapStudyDimensionsToTableState ( dimensions , dataTable ) ; return this . transmartHttpService . runExportJob ( jobId , targetConstraint , elements , transmartTableState ) ; } ) ) ; } else { const transmartTableState = TransmartDataTableMapper . mapDataTableToTableState ( dataTable ) ; return this . transmartHttpService . runExportJob ( jobId , targetConstraint , elements , transmartTableState ) ; } } else { return this . transmartHttpService . runExportJob ( jobId , targetConstraint , elements , null ) ; } } } } param jobId returns { Observable < blob > } downloadExportJob ( jobId ) { if ( this . useExternalExportJob ) { return this . transmartPackerHttpService . downloadJobData ( jobId ) ; } else { return this . transmartHttpService . downloadExportJob ( jobId ) ; } } param jobId returns { Observable < blob > } cancelExportJob ( jobId ) { if ( this . useExternalExportJob ) { return this . transmartPackerHttpService . cancelJob ( jobId ) ; } else { return this . transmartHttpService . cancelExportJob ( jobId ) ; } } param jobId returns { Observable < blob > } archiveExportJob ( jobId ) { if ( this . useExternalExportJob ) { return this . transmartPackerHttpService . archiveJob ( jobId ) ; } else { return this . transmartHttpService . archiveExportJob ( jobId ) ; } } savePatientSet ( name , constraint ) { return this . transmartHttpService . savePatientSet ( name , constraint ) ; } getDataTable ( dataTable ) { let offset = dataTable . offset ; let limit = dataTable . limit ; return this . getDimensions ( dataTable . constraint ) . pipe ( switchMap ( ( transmartStudyDimensions ) => { let tableState = TransmartDataTableMapper . mapStudyDimensionsToTableState ( transmartStudyDimensions , dataTable ) ; const constraint = dataTable . constraint ; return this . transmartHttpService . getDataTable ( tableState , constraint , offset , limit ) ; } , ( transmartStudyDimensions , transmartTable ) => { let newDataTable = TransmartDataTableMapper . mapTransmartDataTable ( transmartTable , offset , limit ) ; newDataTable . constraint = dataTable . constraint ; return newDataTable ; } ) ) ; } param { Constraint } constraint returns { Observable < TableDimension [ ] > } getDimensions ( constraint ) { return this . getStudyIds ( constraint ) . pipe ( switchMap ( ( ) => { return this . transmartHttpService . studies ; } , ( studyIds , studies ) => { if ( studyIds && studies ) { let selectedStudies = studies . filter ( study => studyIds . includes ( study . studyId ) ) ; return TransmartMapper . mergeStudyDimensions ( selectedStudies ) ; } else { return new TransmartStudyDimensions ( ) ; } } ) ) ; } getStudyIds ( constraint ) { return this . transmartHttpService . getStudyIds ( constraint ) ; } getCrossTable ( baseConstraint , rowConstraints , columnConstraints ) { return this . transmartHttpService . getCrossTable ( baseConstraint , rowConstraints , columnConstraints ) ; } get sortableDimensions ( ) : Set < string > { return TransmartHttpService . sortableDimensions ; } getSubjectDimensions ( ) { return this . transmartHttpService . getDimensions ( ) . pipe ( map ( ( transmartDimensions ) => { return transmartDimensions . filter ( transmartDimension => transmartDimension . dimensionType && transmartDimension . dimensionType . toLowerCase ( ) === TransmartResourceService . SUBJECT_DIMENSION_TYPE ) ; } ) ) ; } }