Browse Source

assembly symmetry: improved error and transform handling

Alexander Rose 5 years ago
parent
commit
9609dddd47

+ 24 - 8
src/mol-model-props/rcsb/assembly-symmetry.ts

@@ -9,26 +9,42 @@ import query from './graphql/symmetry.gql';
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
-import { Database as _Database } from '../../mol-data/db'
+import { Database as _Database, Column } from '../../mol-data/db'
 import { GraphQLClient } from '../../mol-util/graphql-client';
 import { CustomProperty } from '../common/custom-property';
 import { NonNullableArray } from '../../mol-util/type-helpers';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 
+const BiologicalAssemblyNames = new Set([
+    'author_and_software_defined_assembly',
+    'author_defined_assembly',
+    'complete icosahedral assembly',
+    'complete point assembly',
+    'representative helical assembly',
+    'software_defined_assembly'
+])
+
 export namespace AssemblySymmetry {
     export const DefaultServerUrl = 'http://data-beta.rcsb.org/graphql'
 
     export function isApplicable(structure?: Structure): boolean {
-        return (
-            !!structure &&
-            structure.models.length === 1 &&
-            structure.models[0].sourceData.kind === 'mmCIF' &&
-            (structure.models[0].sourceData.data.database_2.database_id.isDefined ||
-                structure.models[0].entryId.length === 4)
-        )
+        // check if structure is from pdb entry
+        if (!structure || structure.models.length !== 1 || structure.models[0].sourceData.kind !== 'mmCIF' || (!structure.models[0].sourceData.data.database_2.database_id.isDefined &&
+        structure.models[0].entryId.length !== 4)) return false
+
+        // check if assembly is 'biological'
+        const mmcif = structure.models[0].sourceData.data
+        if (!mmcif.pdbx_struct_assembly.details.isDefined) return false
+        const id = structure.units[0].conformation.operator.assembly.id
+        const indices = Column.indicesOf(mmcif.pdbx_struct_assembly.id, e => e === id)
+        if (indices.length !== 1) return false
+        const details = mmcif.pdbx_struct_assembly.details.value(indices[0])
+        return BiologicalAssemblyNames.has(details)
     }
 
     export async function fetch(ctx: CustomProperty.Context, structure: Structure, props: AssemblySymmetryProps): Promise<AssemblySymmetryValue> {
+        if (!isApplicable(structure)) return []
+
         const client = new GraphQLClient(props.serverUrl, ctx.fetch)
         const variables: AssemblySymmetryQueryVariables = {
             assembly_id: structure.units[0].conformation.operator.assembly.id,

+ 25 - 4
src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts

@@ -12,7 +12,7 @@ import { AssemblySymmetryClusterColorThemeProvider } from '../../../../../mol-mo
 import { PluginStateTransform, PluginStateObject } from '../../../../state/objects';
 import { Task } from '../../../../../mol-task';
 import { PluginContext } from '../../../../context';
-import { StateTransformer } from '../../../../../mol-state';
+import { StateTransformer, StateAction, StateObject } from '../../../../../mol-state';
 
 export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean }>({
     name: 'rcsb-assembly-symmetry-prop',
@@ -22,7 +22,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
         private provider = AssemblySymmetryProvider
 
         register(): void {
-            this.ctx.state.dataState.actions.add(AssemblySymmetry3D)
+            this.ctx.state.dataState.actions.add(InitAssemblySymmetry3D)
             this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('rcsb-assembly-symmetry-cluster', AssemblySymmetryClusterColorThemeProvider)
         }
@@ -35,7 +35,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
         }
 
         unregister() {
-            this.ctx.state.dataState.actions.remove(AssemblySymmetry3D)
+            this.ctx.state.dataState.actions.remove(InitAssemblySymmetry3D)
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('rcsb-assembly-symmetry-cluster')
         }
@@ -46,13 +46,28 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
     })
 });
 
+const InitAssemblySymmetry3D = StateAction.build({
+    display: { name: 'RCSB Assembly Symmetry' },
+    from: PluginStateObject.Molecule.Structure,
+    isApplicable: (a) => AssemblySymmetry.isApplicable(a.data)
+})(({ a, ref, state }, plugin: PluginContext) => Task.create('Init RCSB Assembly Symmetry', async ctx => {
+    try {
+        await AssemblySymmetryProvider.attach({ runtime: ctx, fetch: plugin.fetch }, a.data)
+    } catch(e) {
+        plugin.log.error(`RCSB Assembly Symmetry: ${e}`)
+        return
+    }
+    const tree = state.build().to(ref).apply(AssemblySymmetry3D);
+    await state.updateTree(tree).runInContext(ctx);
+}));
+
 type AssemblySymmetry3D = typeof AssemblySymmetry3D
 const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
     name: 'rcsb-assembly-symmetry-3d',
     display: 'RCSB Assembly Symmetry',
     from: PluginStateObject.Molecule.Structure,
     to: PluginStateObject.Shape.Representation3D,
-    params: (a, ctx: PluginContext) => {
+    params: (a) => {
         return {
             ...AssemblySymmetryParams,
             symmetryIndex: getSymmetrySelectParam(a?.data),
@@ -66,6 +81,9 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
         return Task.create('RCSB Assembly Symmetry', async ctx => {
             await AssemblySymmetryProvider.attach({ runtime: ctx, fetch: plugin.fetch }, a.data)
             const assemblySymmetry = AssemblySymmetryProvider.get(a.data).value
+            if (!assemblySymmetry || assemblySymmetry.length === 0) {
+                return StateObject.Null;
+            }
             const repr = AssemblySymmetryRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.structureRepresentation.themeCtx }, () => AssemblySymmetryParams)
             await repr.createOrUpdate(params, a.data).runInContext(ctx);
             const { type, kind, symbol } = assemblySymmetry![params.symmetryIndex]
@@ -76,6 +94,9 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
         return Task.create('RCSB Assembly Symmetry', async ctx => {
             await AssemblySymmetryProvider.attach({ runtime: ctx, fetch: plugin.fetch }, a.data)
             const assemblySymmetry = AssemblySymmetryProvider.get(a.data).value
+            if (!assemblySymmetry || assemblySymmetry.length === 0) {
+                return StateTransformer.UpdateResult.Recreate
+            }
             const props = { ...b.data.repr.props, ...newParams }
             await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
             const { type, kind, symbol } = assemblySymmetry![newParams.symmetryIndex]