Browse Source

ModelServer: custom property providers defined by config file

David Sehnal 6 years ago
parent
commit
4dfafb531b

+ 8 - 0
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.
      *

+ 2 - 1
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();

+ 0 - 19
src/servers/model/properties.ts

@@ -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)
-    ];
-}

+ 9 - 10
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)
+    ];
 }

+ 18 - 0
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();
+        }
+    });
+}

+ 12 - 0
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)
+}

+ 8 - 3
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)
+    ];
 }

+ 28 - 0
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;
+    }
+}
+

+ 4 - 1
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');

+ 9 - 7
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');