diff --git a/src/servers/model/properties/pdbe.ts b/src/servers/model/properties/pdbe.ts index 61d968f9c0ab694b93b9c9b4adb29b728f1c19bc..aafc6675c22ccd12fdd7e0ef9af03bb51f1ed228 100644 --- a/src/servers/model/properties/pdbe.ts +++ b/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) ]; } \ No newline at end of file diff --git a/src/servers/model/properties/providers/pdbe.ts b/src/servers/model/properties/providers/pdbe.ts index cbc60c8c7e328914f61110adc18cf2d8563ebf0b..14a2119aa7321ceaa21b4a26b096ccdfb63c821e 100644 --- a/src/servers/model/properties/providers/pdbe.ts +++ b/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; } }); } \ No newline at end of file diff --git a/src/servers/model/property-provider.ts b/src/servers/model/property-provider.ts index a2789b49055a4232f3273c0381f8e99e7cdbbc50..95f463e31ac90a3dd794cda97c36f8b62c739ca8 100644 --- a/src/servers/model/property-provider.ts +++ b/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; } diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts index 35159b5dc3f4754618aca8e7933b8fdc5a53d373..ad0a649e2466a6c70cf4756e74f2a4df7de8a775 100644 --- a/src/servers/model/server/query.ts +++ b/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]); diff --git a/src/servers/model/server/structure-wrapper.ts b/src/servers/model/server/structure-wrapper.ts index 60275ddc5f2a514cefd9243f536c0e752bc811cf..7c8bb1b255ddf845a0c86ac861332e39bd681755 100644 --- a/src/servers/model/server/structure-wrapper.ts +++ b/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;