Browse Source

wip: multiple model support

David Sehnal 6 years ago
parent
commit
5a82e46b7e

+ 2 - 2
src/servers/model/properties/pdbe.ts

@@ -8,10 +8,10 @@
 import { Model } from 'mol-model/structure';
 import { PDBe_structureQualityReport } from './providers/pdbe';
 
-export function attachModelProperties(model: Model): Promise<any>[] {
+export function attachModelProperties(model: Model, cache: object): Promise<any>[] {
     // return a list of promises that start attaching the props in parallel
     // (if there are downloads etc.)
     return [
-        PDBe_structureQualityReport(model)
+        PDBe_structureQualityReport(model, cache)
     ];
 }

+ 10 - 2
src/servers/model/properties/providers/pdbe.ts

@@ -7,12 +7,20 @@
  import { Model } from 'mol-model/structure';
 import { StructureQualityReport } from 'mol-model-props/pdbe/structure-quality-report';
 import { fetchRetry } from '../../utils/fetch-retry';
+import { UUID } from 'mol-util';
 
-export function PDBe_structureQualityReport(model: Model) {
+const cacheKey = UUID.create();
+export function PDBe_structureQualityReport(model: Model, cache: any) {
     return StructureQualityReport.attachFromCifOrApi(model, {
         PDBe_apiSourceJson: async model => {
+            if (cache[cacheKey]) {
+                console.log('cache hit');
+                return cache[cacheKey];
+            }
             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();
+            const json = await rawData.json();
+            cache[cacheKey] = json;
+            return json;
         }
     });
 }

+ 3 - 3
src/servers/model/property-provider.ts

@@ -7,7 +7,7 @@
 import { Model } from 'mol-model/structure';
 import Config from './config';
 
-export type ModelPropertiesProvider = (model: Model) => Promise<any>[]
+export type ModelPropertiesProvider = (model: Model, cache: object) => Promise<any>[]
 
 export function createModelPropertiesProviderFromConfig(): ModelPropertiesProvider {
     if (!Config.customPropertyProviders || Config.customPropertyProviders.length === 0) return () => [];
@@ -17,10 +17,10 @@ export function createModelPropertiesProviderFromConfig(): ModelPropertiesProvid
         ps.push(require(p).attachModelProperties);
     }
 
-    return model => {
+    return (model, cache) => {
         const ret: Promise<any>[] = [];
         for (const p of ps) {
-            for (const e of p(model)) ret.push(e);
+            for (const e of p(model, cache)) ret.push(e);
         }
         return ret;
     }

+ 20 - 10
src/servers/model/server/query.ts

@@ -6,7 +6,7 @@
 
 import { Column } from 'mol-data/db';
 import { CifWriter } from 'mol-io/writer/cif';
-import { StructureQuery, StructureSelection } from 'mol-model/structure';
+import { StructureQuery, StructureSelection, Structure } from 'mol-model/structure';
 import { encode_mmCIF_categories } from 'mol-model/structure/export/mmcif';
 import { now, Progress } from 'mol-task';
 import { ConsoleLogger } from 'mol-util/console-logger';
@@ -14,7 +14,7 @@ import { PerformanceMonitor } from 'mol-util/performance-monitor';
 import Config from '../config';
 import Version from '../version';
 import { Job } from './jobs';
-import { createStructureWrapperFromJob, StructureWrapper, resolveStructure } from './structure-wrapper';
+import { createStructureWrapperFromJob, StructureWrapper, resolveStructures } from './structure-wrapper';
 import CifField = CifWriter.Field
 import { createModelPropertiesProviderFromConfig } from '../property-provider';
 
@@ -35,13 +35,23 @@ export async function resolveJob(job: Job): Promise<CifWriter.Encoder<any>> {
 
     try {
         perf.start('query');
-        const sourceStructure = await resolveStructure(wrappedStructure);
-        if (!sourceStructure) throw new Error('Model not available');
-        const structure = job.queryDefinition.structureTransform
-            ? await job.queryDefinition.structureTransform(job.normalizedParams, sourceStructure)
-            : sourceStructure;
-        const query = job.queryDefinition.query(job.normalizedParams, structure);
-        const result = await StructureSelection.unionStructure(StructureQuery.run(query, structure, Config.maxQueryTimeInMs));
+        const sourceStructures = await resolveStructures(wrappedStructure);
+        if (!sourceStructures.length) throw new Error('Model not available');
+
+        let structures: Structure[] = sourceStructures;
+
+        if (job.queryDefinition.structureTransform) {
+            structures = [];
+            for (const s of sourceStructures) {
+                structures.push(await job.queryDefinition.structureTransform(job.normalizedParams, s));
+            }
+        }
+
+        const queries = structures.map(s => job.queryDefinition.query(job.normalizedParams, s));
+        const result: Structure[] = [];
+        for (let i = 0; i < structures.length; i++) {
+            result.push(await StructureSelection.unionStructure(StructureQuery.run(queries[i], structures[i], Config.maxQueryTimeInMs)));
+        }
         perf.end('query');
 
         const encoder = CifWriter.createEncoder({
@@ -54,7 +64,7 @@ export async function resolveJob(job: Job): Promise<CifWriter.Encoder<any>> {
         ConsoleLogger.logId(job.id, 'Query', 'Query finished.');
 
         perf.start('encode');
-        encoder.startDataBlock(structure.models[0].label.toUpperCase());
+        encoder.startDataBlock(sourceStructures[0].models[0].label.toUpperCase());
         encoder.writeCategory(_model_server_result, [job]);
         encoder.writeCategory(_model_server_params, [job]);
 

+ 14 - 12
src/servers/model/server/structure-wrapper.ts

@@ -43,7 +43,8 @@ export interface StructureWrapper {
     modelMap: Map<number, Model>,
     structureModelMap: Map<number, Structure>,
     propertyProvider: ModelPropertiesProvider | undefined,
-    cifFrame: CifFrame
+    cifFrame: CifFrame,
+    cache: object
 }
 
 export async function createStructureWrapperFromJob(job: Job, propertyProvider: ModelPropertiesProvider | undefined, allowCache = true): Promise<StructureWrapper> {
@@ -115,15 +116,6 @@ export async function readStructureWrapper(key: string, sourceId: string | '_loc
         modelMap.set(m.modelNum, m);
     }
 
-    perf.start('attachProps');
-    if (propertyProvider) {
-        const modelProps = propertyProvider(models[0]);
-        for (const p of modelProps) {
-            await tryAttach(key, p);
-        }
-    }
-    perf.end('attachProps');
-
     const ret: StructureWrapper = {
         info: {
             sourceType: StructureSourceType.File,
@@ -141,7 +133,8 @@ export async function readStructureWrapper(key: string, sourceId: string | '_loc
         modelMap,
         structureModelMap: new Map(),
         cifFrame: frame,
-        propertyProvider
+        propertyProvider,
+        cache: Object.create(null)
     };
 
     return ret;
@@ -157,7 +150,7 @@ export async function resolveStructure(wrapper: StructureWrapper, modelNum?: num
     const model = wrapper.modelMap.get(modelNum)!;
     const structure = Structure.ofModel(model);
     if (wrapper.propertyProvider) {
-        const modelProps = wrapper.propertyProvider(model);
+        const modelProps = wrapper.propertyProvider(model, wrapper.cache);
         for (const p of modelProps) {
             await tryAttach(wrapper.key, p);
         }
@@ -165,6 +158,15 @@ export async function resolveStructure(wrapper: StructureWrapper, modelNum?: num
     return structure;
 }
 
+export async function resolveStructures(wrapper: StructureWrapper, modelNums?: number[]) {
+    const ret: Structure[] = [];
+    for (const n of modelNums || (wrapper.models as Model[]).map(m => m.modelNum)) {
+        const s = await resolveStructure(wrapper, n);
+        if (s) ret.push(s);
+    }
+    return ret;
+}
+
 async function tryAttach(key: string, promise: Promise<any>) {
     try {
         await promise;