Przeglądaj źródła

added PDBe preferred assembly prop

David Sehnal 6 lat temu
rodzic
commit
e03d771fdb

+ 89 - 0
src/mol-model-props/pdbe/preferred-assembly.ts

@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Column, Table } from 'mol-data/db';
+import { toTable } from 'mol-io/reader/cif/schema';
+import { CifWriter } from 'mol-io/writer/cif';
+import { Model, ModelPropertyDescriptor } from 'mol-model/structure';
+
+export namespace PDBePreferredAssembly {
+    export type Property = string
+
+    export function getFirstFromModel(model: Model): Property {
+        const asm = model.symmetry.assemblies;
+        return asm.length ? asm[0].id : '';
+    }
+
+    export function get(model: Model): Property {
+        return model._staticPropertyData.__PDBePreferredAssebly__ || getFirstFromModel(model);
+    }
+    function set(model: Model, prop: Property) {
+        (model._staticPropertyData.__PDBePreferredAssebly__ as Property) = prop;
+    }
+
+    export const Schema = {
+        pdbe_preferred_assembly: {
+            assembly_id: Column.Schema.str
+        }
+    };
+    export type Schema = typeof Schema
+
+    export const Descriptor = ModelPropertyDescriptor({
+        isStatic: true,
+        name: 'pdbe_preferred_assembly',
+        cifExport: {
+            prefix: 'pdbe',
+            context(ctx): Property { return get(ctx.firstModel); },
+            categories: [{
+                name: 'pdbe_preferred_assembly',
+                instance(ctx: Property) {
+                    return CifWriter.Category.ofTable(Table.ofArrays(Schema.pdbe_preferred_assembly, { assembly_id: [ctx] }));
+                }
+            }]
+        }
+    });
+
+    function fromCifData(model: Model): string | undefined {
+        if (model.sourceData.kind !== 'mmCIF') return void 0;
+        const cat = model.sourceData.frame.categories.pdbe_preferred_assembly;
+        if (!cat) return void 0;
+        return toTable(Schema.pdbe_preferred_assembly, cat).assembly_id.value(0) || getFirstFromModel(model);
+    }
+
+    export async function attachFromCifOrApi(model: Model, params: {
+        // optional JSON source
+        PDBe_apiSourceJson?: (model: Model) => Promise<any>
+    }) {
+        if (model.customProperties.has(Descriptor)) return true;
+
+        let asmName: string | undefined = fromCifData(model);
+        if (asmName === void 0 &&  params.PDBe_apiSourceJson) {
+            const data = await params.PDBe_apiSourceJson(model);
+            if (!data) return false;
+            asmName = asmNameFromJson(model, data);
+        } else {
+            return false;
+        }
+
+        if (!asmName) return false;
+
+        model.customProperties.add(Descriptor);
+        set(model, asmName);
+        return true;
+    }
+}
+
+function asmNameFromJson(modelData: Model, data: any): string {
+    const assemblies = data[0] && data[0].assemblies;
+    if (!assemblies || !assemblies.length) return PDBePreferredAssembly.getFirstFromModel(modelData);
+
+    for (const asm of assemblies) {
+        if (asm.preferred) {
+            return asm.assembly_id;
+        }
+    }
+    return assemblies[0].assembly_id;
+}

+ 3 - 3
src/mol-model-props/pdbe/structure-quality-report.ts

@@ -22,7 +22,7 @@ export namespace StructureQualityReport {
 
     export function get(model: Model): Property | undefined {
         // must be defined before the descriptor so it's not undefined.
-        return model._dynamicPropertyData.__StructureQualityReport__;
+        return model._dynamicPropertyData.__PDBeStructureQualityReport__;
     }
 
     export const Schema = {
@@ -44,7 +44,7 @@ export namespace StructureQualityReport {
 
     export const Descriptor = ModelPropertyDescriptor({
         isStatic: false,
-        name: 'structure_quality_report',
+        name: 'pdbe_structure_quality_report',
         cifExport: {
             prefix: 'pdbe',
             context(ctx) {
@@ -108,7 +108,7 @@ export namespace StructureQualityReport {
     }
 
     function set(model: Model, prop: Property) {
-        (model._dynamicPropertyData.__StructureQualityReport__ as Property) = prop;
+        (model._dynamicPropertyData.__PDBeStructureQualityReport__ as Property) = prop;
     }
 
     export function getIssueMap(model: Model): IssueMap | undefined {

+ 1 - 1
src/mol-model/structure/model/properties/custom/collection.ts

@@ -21,7 +21,7 @@ export class CustomProperties {
         this._set.add(desc);
     }
 
-    has(desc: ModelPropertyDescriptor): boolean {
+    has(desc: ModelPropertyDescriptor<any>): boolean {
         return this._set.has(desc);
     }
 }

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

@@ -6,12 +6,13 @@
  */
 
 import { Model } from 'mol-model/structure';
-import { PDBe_structureQualityReport } from './providers/pdbe';
+import { PDBe_structureQualityReport, PDBe_preferredAssembly } from './providers/pdbe';
 
 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, cache)
+        PDBe_structureQualityReport(model, cache),
+        PDBe_preferredAssembly(model, cache)
     ];
 }

+ 22 - 4
src/servers/model/properties/providers/pdbe.ts

@@ -9,35 +9,53 @@ 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';
+import { PDBePreferredAssembly } from 'mol-model-props/pdbe/preferred-assembly';
 
 const USE_FILE_SOURCE = false;
 
 export function PDBe_structureQualityReport(model: Model, cache: any) {
     return StructureQualityReport.attachFromCifOrApi(model, {
         PDBe_apiSourceJson: USE_FILE_SOURCE
-            ? residuewise_outlier_summary.getDataFromFile
+            ? residuewise_outlier_summary.getDataFromAggregateFile
             : residuewise_outlier_summary.getDataFromApiProvider(cache)
     });
 }
 
+export function PDBe_preferredAssembly(model: Model, cache: any) {
+    return PDBePreferredAssembly.attachFromCifOrApi(model, {
+        PDBe_apiSourceJson: USE_FILE_SOURCE
+            ? void 0
+            : preferred_assembly.getDataFromApiProvider(cache)
+    });
+}
+
+namespace preferred_assembly {
+    export const getDataFromApiProvider = apiQueryProvider('https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary');
+}
+
 namespace residuewise_outlier_summary {
     const json = new Map<string, any>();
 
-    export async function getDataFromFile(model: Model) {
+    export async function getDataFromAggregateFile(model: Model) {
         const key = `${model.label[1]}${model.label[2]}`;
         if (!json.has(key)) {
             const fn = `e:/test/mol-star/model/props/${key}.json`;
             if (!fs.existsSync(fn)) json.set(key, { });
+            // TODO: use async readFile?
             else json.set(key, JSON.parse(fs.readFileSync(fn, 'utf8')));
         }
         return json.get(key)![model.label.toLowerCase()] || { };
     }
 
-    export function getDataFromApiProvider(cache: any) {
+    export const getDataFromApiProvider = apiQueryProvider('https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry');
+}
+
+function apiQueryProvider(urlPrefix: string) {
+    return (cache: any) => {
         const cacheKey = UUID.create();
         return async (model: Model) => {
             if (cache[cacheKey]) return cache[cacheKey];
-            const rawData = await fetchRetry(`https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/${model.label.toLowerCase()}`, 1500, 5);
+            const rawData = await fetchRetry(`${urlPrefix}/${model.label.toLowerCase()}`, 1500, 5);
             const json = (await rawData.json())[model.label.toLowerCase()] || { };
             cache[cacheKey] = json;
             return json;