From 4dfafb531b0c39ab2b0ab2ef63190fa1e67e0383 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Tue, 18 Sep 2018 13:12:35 +0200 Subject: [PATCH] ModelServer: custom property providers defined by config file --- src/servers/model/config.ts | 8 ++++++ src/servers/model/preprocess/preprocess.ts | 3 +- src/servers/model/properties.ts | 19 ------------- src/servers/model/properties/pdbe.ts | 19 ++++++------- .../model/properties/providers/pdbe.ts | 18 ++++++++++++ .../model/properties/providers/rcsb.ts | 12 ++++++++ src/servers/model/properties/rcsb.ts | 11 ++++++-- src/servers/model/provider.ts | 28 +++++++++++++++++++ src/servers/model/server/query.ts | 5 +++- src/servers/model/server/structure-wrapper.ts | 16 ++++++----- 10 files changed, 98 insertions(+), 41 deletions(-) delete mode 100644 src/servers/model/properties.ts create mode 100644 src/servers/model/properties/providers/pdbe.ts create mode 100644 src/servers/model/properties/providers/rcsb.ts create mode 100644 src/servers/model/provider.ts diff --git a/src/servers/model/config.ts b/src/servers/model/config.ts index b53d12de2..8ae2425e7 100644 --- a/src/servers/model/config.ts +++ b/src/servers/model/config.ts @@ -47,6 +47,14 @@ const config = { /** Maximum number of requests before "server busy" */ maxQueueLength: 30, + /** + * Paths (relative to the root directory of the model server) to JavaScript files that specify custom properties + */ + customPropertyProviders: [ + './properties/pdbe', + './properties/rcsb' + ], + /** * Maps a request identifier to a filename. * diff --git a/src/servers/model/preprocess/preprocess.ts b/src/servers/model/preprocess/preprocess.ts index bc6b5abca..cf45825df 100644 --- a/src/servers/model/preprocess/preprocess.ts +++ b/src/servers/model/preprocess/preprocess.ts @@ -23,7 +23,8 @@ export async function preprocessFile(filename: string, outputCif?: string, outpu //const started = now(); //ConsoleLogger.log(`${linearId}`, `Reading '${filename}'...`); - const input = await readStructure('entry', '_local_', filename); + // TODO: support the custom prop provider list here. + const input = await readStructure('entry', '_local_', filename, void 0); //ConsoleLogger.log(`${linearId}`, `Classifying CIF categories...`); const categories = await classifyCif(input.cifFrame); //clearLine(); diff --git a/src/servers/model/properties.ts b/src/servers/model/properties.ts deleted file mode 100644 index 5f2922238..000000000 --- a/src/servers/model/properties.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import { Model } from 'mol-model/structure'; -//import { PDBe_structureQualityReport } from './properties/pdbe'; -//import { RCSB_assemblySymmetry } from './properties/rcsb'; - -export function attachModelProperties(model: Model): Promise<any>[] { - // return a list of promises that start attaching the props in parallel - // (if there are downloads etc.) - return [ - //PDBe_structureQualityReport(model), - //RCSB_assemblySymmetry(model) - ]; -} \ No newline at end of file diff --git a/src/servers/model/properties/pdbe.ts b/src/servers/model/properties/pdbe.ts index acda123fe..61d968f9c 100644 --- a/src/servers/model/properties/pdbe.ts +++ b/src/servers/model/properties/pdbe.ts @@ -2,17 +2,16 @@ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ - import { Model } from 'mol-model/structure'; -import { StructureQualityReport } from 'mol-model-props/pdbe/structure-quality-report'; -import { fetchRetry } from '../utils/fetch-retry'; +import { Model } from 'mol-model/structure'; +import { PDBe_structureQualityReport } from './providers/pdbe'; -export function PDBe_structureQualityReport(model: Model) { - return StructureQualityReport.attachFromCifOrApi(model, { - PDBe_apiSourceJson: async model => { - const rawData = await fetchRetry(`https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/${model.label.toLowerCase()}`, 1500, 5); - return await rawData.json(); - } - }); +export function attachModelProperties(model: Model): Promise<any>[] { + // return a list of promises that start attaching the props in parallel + // (if there are downloads etc.) + return [ + PDBe_structureQualityReport(model) + ]; } \ No newline at end of file diff --git a/src/servers/model/properties/providers/pdbe.ts b/src/servers/model/properties/providers/pdbe.ts new file mode 100644 index 000000000..cbc60c8c7 --- /dev/null +++ b/src/servers/model/properties/providers/pdbe.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + + import { Model } from 'mol-model/structure'; +import { StructureQualityReport } from 'mol-model-props/pdbe/structure-quality-report'; +import { fetchRetry } from '../../utils/fetch-retry'; + +export function PDBe_structureQualityReport(model: Model) { + return StructureQualityReport.attachFromCifOrApi(model, { + PDBe_apiSourceJson: async model => { + const rawData = await fetchRetry(`https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/${model.label.toLowerCase()}`, 1500, 5); + return await rawData.json(); + } + }); +} \ No newline at end of file diff --git a/src/servers/model/properties/providers/rcsb.ts b/src/servers/model/properties/providers/rcsb.ts new file mode 100644 index 000000000..fbc3347d3 --- /dev/null +++ b/src/servers/model/properties/providers/rcsb.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Model } from 'mol-model/structure'; +import { AssemblySymmetry } from 'mol-model-props/rcsb/symmetry'; + +export function RCSB_assemblySymmetry(model: Model) { + return AssemblySymmetry.attachFromCifOrAPI(model) +} \ No newline at end of file diff --git a/src/servers/model/properties/rcsb.ts b/src/servers/model/properties/rcsb.ts index fbc3347d3..9cfaaebcd 100644 --- a/src/servers/model/properties/rcsb.ts +++ b/src/servers/model/properties/rcsb.ts @@ -1,12 +1,17 @@ /** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * + * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { Model } from 'mol-model/structure'; -import { AssemblySymmetry } from 'mol-model-props/rcsb/symmetry'; +import { RCSB_assemblySymmetry } from './providers/rcsb'; -export function RCSB_assemblySymmetry(model: Model) { - return AssemblySymmetry.attachFromCifOrAPI(model) +export function attachModelProperties(model: Model): Promise<any>[] { + // return a list of promises that start attaching the props in parallel + // (if there are downloads etc.) + return [ + RCSB_assemblySymmetry(model) + ]; } \ No newline at end of file diff --git a/src/servers/model/provider.ts b/src/servers/model/provider.ts new file mode 100644 index 000000000..a2789b490 --- /dev/null +++ b/src/servers/model/provider.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Model } from 'mol-model/structure'; +import Config from './config'; + +export type ModelPropertiesProvider = (model: Model) => Promise<any>[] + +export function createModelPropertiesProviderFromConfig(): ModelPropertiesProvider { + if (!Config.customPropertyProviders || Config.customPropertyProviders.length === 0) return () => []; + + const ps: ModelPropertiesProvider[] = []; + for (const p of Config.customPropertyProviders) { + ps.push(require(p).attachModelProperties); + } + + return model => { + const ret: Promise<any>[] = []; + for (const p of ps) { + for (const e of p(model)) ret.push(e); + } + return ret; + } +} + diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts index d71200a0c..f4b699b99 100644 --- a/src/servers/model/server/query.ts +++ b/src/servers/model/server/query.ts @@ -16,6 +16,7 @@ import Version from '../version'; import { Job } from './jobs'; import { getStructure, StructureWrapper } from './structure-wrapper'; import CifField = CifWriter.Field +import { createModelPropertiesProviderFromConfig } from '../provider'; export interface Stats { structure: StructureWrapper, @@ -25,10 +26,12 @@ export interface Stats { const perf = new PerformanceMonitor(); +const propertyProvider = createModelPropertiesProviderFromConfig(); + export async function resolveJob(job: Job): Promise<CifWriter.Encoder<any>> { ConsoleLogger.logId(job.id, 'Query', 'Starting.'); - const wrappedStructure = await getStructure(job); + const wrappedStructure = await getStructure(job, propertyProvider); try { perf.start('query'); diff --git a/src/servers/model/server/structure-wrapper.ts b/src/servers/model/server/structure-wrapper.ts index a24aaffd9..cf3506a13 100644 --- a/src/servers/model/server/structure-wrapper.ts +++ b/src/servers/model/server/structure-wrapper.ts @@ -14,7 +14,7 @@ import * as fs from 'fs' import * as zlib from 'zlib' import { Job } from './jobs'; import { ConsoleLogger } from 'mol-util/console-logger'; -import { attachModelProperties } from '../properties'; +import { ModelPropertiesProvider } from '../provider'; require('util.promisify').shim(); @@ -44,12 +44,12 @@ export interface StructureWrapper { cifFrame: CifFrame } -export async function getStructure(job: Job, allowCache = true): Promise<StructureWrapper> { +export async function getStructure(job: Job, propertyProvider: ModelPropertiesProvider | undefined, allowCache = true): Promise<StructureWrapper> { if (allowCache && Config.cacheParams.useCache) { const ret = StructureCache.get(job.key); if (ret) return ret; } - const ret = await readStructure(job.key, job.sourceId, job.entryId); + const ret = await readStructure(job.key, job.sourceId, job.entryId, propertyProvider); if (allowCache && Config.cacheParams.useCache) { StructureCache.add(ret); } @@ -86,7 +86,7 @@ async function parseCif(data: string|Uint8Array) { return parsed.result; } -export async function readStructure(key: string, sourceId: string | '_local_', entryId: string) { +export async function readStructure(key: string, sourceId: string | '_local_', entryId: string, propertyProvider: ModelPropertiesProvider | undefined) { const filename = sourceId === '_local_' ? entryId : Config.mapFile(sourceId, entryId); if (!filename) throw new Error(`Cound not map '${key}' to a valid filename.`); if (!fs.existsSync(filename)) throw new Error(`Could not find source file for '${key}'.`); @@ -109,9 +109,11 @@ export async function readStructure(key: string, sourceId: string | '_local_', e perf.end('createModel'); perf.start('attachProps'); - const modelProps = attachModelProperties(models[0]); - for (const p of modelProps) { - await tryAttach(key, p); + if (propertyProvider) { + const modelProps = propertyProvider(models[0]); + for (const p of modelProps) { + await tryAttach(key, p); + } } perf.end('attachProps'); -- GitLab