diff --git a/src/servers/model/config.ts b/src/servers/model/config.ts index b53d12de25b3f5c51ecc5b870d9a412a511dad00..8ae2425e76e98e9dd8e0f4d7c4903802b0e55f2c 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 bc6b5abca1b3d748e6d2b06d0dcd0691d61ab225..cf45825dfd51fe76379a5a1d37695f7f37c02d6c 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 5f292223862c4d3266daaf8e53333e8911241811..0000000000000000000000000000000000000000 --- 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 acda123fe369344a804c4edfb3f4a8d7abfe9200..61d968f9c0ab694b93b9c9b4adb29b728f1c19bc 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 0000000000000000000000000000000000000000..cbc60c8c7e328914f61110adc18cf2d8563ebf0b --- /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 0000000000000000000000000000000000000000..fbc3347d3f2812891f9fc36e4a1f0387bda80a31 --- /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 fbc3347d3f2812891f9fc36e4a1f0387bda80a31..9cfaaebcd032270827e765ffe7840a2df1cac499 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 0000000000000000000000000000000000000000..a2789b49055a4232f3273c0381f8e99e7cdbbc50 --- /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 d71200a0cb6b962f3258982d36ce84c5e4819e52..f4b699b997fe205d03f3a6290398db1b4ff3d5fe 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 a24aaffd9019e639ab7989bd3fcafd545cb17e20..cf3506a13d9d3a0ee859da8f00d5c666323f63fa 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');