From 8802f518d7a16ad6296e52840678329e4177125a Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 3 Aug 2018 18:26:57 -0700 Subject: [PATCH] wip, symmetry annotation, graphql schema codegen --- data/rcsb-graphql/codegen.js | 18 ++ data/rcsb-graphql/codegen.json | 8 + package-lock.json | Bin 399947 -> 418525 bytes package.json | 9 +- src/servers/model/properties.ts | 5 +- .../properties/rcsb/graphql/symmetry.gql.ts | 28 +++ .../model/properties/rcsb/graphql/types.ts | 197 ++++++++++++++++++ src/servers/model/properties/rcsb/symmetry.ts | 150 +++++++++++++ src/servers/model/test.ts | 17 +- 9 files changed, 427 insertions(+), 5 deletions(-) create mode 100644 data/rcsb-graphql/codegen.js create mode 100644 data/rcsb-graphql/codegen.json create mode 100644 src/servers/model/properties/rcsb/graphql/symmetry.gql.ts create mode 100644 src/servers/model/properties/rcsb/graphql/types.ts create mode 100644 src/servers/model/properties/rcsb/symmetry.ts diff --git a/data/rcsb-graphql/codegen.js b/data/rcsb-graphql/codegen.js new file mode 100644 index 000000000..e66716d8e --- /dev/null +++ b/data/rcsb-graphql/codegen.js @@ -0,0 +1,18 @@ +const { generate } = require('graphql-code-generator') +const path = require('path') + +const basePath = path.join(__dirname, '..', '..', 'src', 'servers', 'model', 'properties', 'rcsb', 'graphql') + +generate({ + args: [ + path.join(basePath, 'symmetry.gql.ts') + ], + schema: 'http://rest-experimental.rcsb.org/graphql', + template: 'typescript', + out: path.join(basePath), + skipSchema: true, + overwrite: true, + config: path.join(__dirname, 'codegen.json') +}, true).then( + console.log('done') +).catch(e => console.error(e)) \ No newline at end of file diff --git a/data/rcsb-graphql/codegen.json b/data/rcsb-graphql/codegen.json new file mode 100644 index 000000000..a005e9369 --- /dev/null +++ b/data/rcsb-graphql/codegen.json @@ -0,0 +1,8 @@ +{ + "flattenTypes": false, + "generatorConfig": { + "printTime": true, + "immutableTypes": true, + "resolvers": false + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d150b46ca731750dc9d2ed16995cbadd4e52fed2..7c984dd0b938e852ed5cd265f4bf59b79e191c9b 100644 GIT binary patch delta 8462 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 F|3A{#_E-P_ delta 317 zcmV-D0mA;>#Tm=A7_cz}v!({X3A3*gdIXa=5D%BXA_N$d4Qw)#?-B=-I1mi8t{M3R zlkh|ilOQn$vzH%IOtbfHD|NF><ueMh^SBtdeGCCI0=K~r0mclsy&VCY8Ml2p0i_VP z<Vyhy7`NVJ0n`Y$wr&AaK)1-R0cDc6c2xp98MoeV0v9ir`4$EdmyZ1c4VN&+1T~kf zXaW|ujiCbQ2$zei0>`&)tpdasw<N{_`*ODrI|IBdll0vZm;NFF7?*%f12ebpXafWe zw>5YJ&0V()*8~3rw?W<mumQLD-~%BYmx|H?3%4)-0~xTFHHifkx0bL3dlk1E#{{1s zw~*)rUo*EE9R;N`x9Cv?a7(vHp9Lljw>+%{LYkMB$paFXT{8p>m!NL}1eec012VUi PN(RFf2MQn{eF}XF<)C~? diff --git a/package.json b/package.json index dead84e32..0627484b0 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ }, "scripts": { "lint": "tslint src/**/*.ts", - "build": "cpx \"src/**/*.{vert,frag,glsl,scss,woff,woff2,ttf,otf,eot,svg,html}\" build/node_modules/ && tsc", + "build": "cpx \"src/**/*.{vert,frag,glsl,scss,woff,woff2,ttf,otf,eot,svg,html,gql}\" build/node_modules/ && tsc", "watch": "tsc -watch", - "watch-extra": "cpx \"src/**/*.{vert,frag,glsl,scss,woff,woff2,ttf,otf,eot,svg,html}\" build/node_modules/ --watch", + "watch-extra": "cpx \"src/**/*.{vert,frag,glsl,scss,woff,woff2,ttf,otf,eot,svg,html,gql}\" build/node_modules/ --watch", "watch-all-win": "start cmd /K npm run watch & start cmd /K npm run watch-extra & start cmd /K npm run watch-viewer & start http-server -p 1338", "test": "jest", "build-viewer": "webpack build/node_modules/apps/viewer/index.js --mode development -o build/viewer/index.js", @@ -84,6 +84,9 @@ "file-loader": "^1.1.11", "glslify-import": "^3.1.0", "glslify-loader": "^1.0.2", + "graphql-code-generator": "^0.10.5", + "graphql-codegen-typescript-template": "^0.10.5", + "graphql-tag": "^2.9.2", "jest": "^23.4.2", "jest-raw-loader": "^1.0.1", "mini-css-extract-plugin": "^0.4.1", @@ -104,6 +107,8 @@ "argparse": "^1.0.10", "compression": "^1.7.3", "express": "^4.16.3", + "graphql": "^0.13.2", + "graphql-request": "^1.8.0", "immutable": "^4.0.0-rc.9", "node-fetch": "^2.2.0", "react": "^16.4.2", diff --git a/src/servers/model/properties.ts b/src/servers/model/properties.ts index b3486a48d..eafa41955 100644 --- a/src/servers/model/properties.ts +++ b/src/servers/model/properties.ts @@ -2,15 +2,18 @@ * 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 './properties/structure-quality-report'; +import { SymmetryAnnotation } from './properties/rcsb/symmetry'; 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 [ - StructureQualityReport.attachFromPDBeApi(model) + StructureQualityReport.attachFromPDBeApi(model), + SymmetryAnnotation.attachFromRCSB(model) ]; } \ No newline at end of file diff --git a/src/servers/model/properties/rcsb/graphql/symmetry.gql.ts b/src/servers/model/properties/rcsb/graphql/symmetry.gql.ts new file mode 100644 index 000000000..0a01a456f --- /dev/null +++ b/src/servers/model/properties/rcsb/graphql/symmetry.gql.ts @@ -0,0 +1,28 @@ + // workaround so the query gets found by the codegen +function gql (strs: TemplateStringsArray) { return strs.raw.join('') } + +export default +gql`query RcsbSymmetry($pdbId: String) { + assemblies(pdbId: $pdbId) { + assembly_id + rcsb_annotation_symmetry { + source + symmetry_features { + type + clusters { + avg_rmsd + members + } + stoichiometry { + description + value + } + symmetry_axes { + order + start + end + } + } + } + } +}` \ No newline at end of file diff --git a/src/servers/model/properties/rcsb/graphql/types.ts b/src/servers/model/properties/rcsb/graphql/types.ts new file mode 100644 index 000000000..5283fa12d --- /dev/null +++ b/src/servers/model/properties/rcsb/graphql/types.ts @@ -0,0 +1,197 @@ +/* tslint:disable */ +/** Generated in 2018-08-03T15:19:31-07:00 */ + +export enum PdbxLeavingAtomFlag { + N = "N", + Y = "Y" +} + +export enum PdbxStereoConfig { + N = "N", + R = "R", + S = "S" +} + +export enum ExperimentalSupport { + ASSAY_FOR_OLIGOMERIZATION = "ASSAY_FOR_OLIGOMERIZATION", + CROSS_LINKING = "CROSS_LINKING", + EQUILIBRIUM_CENTRIFUGATION = "EQUILIBRIUM_CENTRIFUGATION", + FLUORESCENCE_RESONANCE_ENERGY_TRANSFER = "FLUORESCENCE_RESONANCE_ENERGY_TRANSFER", + GEL_FILTRATION = "GEL_FILTRATION", + HOMOLOGY = "HOMOLOGY", + IMMUNOPRECIPITATION = "IMMUNOPRECIPITATION", + ISOTHERMAL_TITRATION_CALORIMETRY = "ISOTHERMAL_TITRATION_CALORIMETRY", + LIGHT_SCATTERING = "LIGHT_SCATTERING", + MASS_SPECTROMETRY = "MASS_SPECTROMETRY", + MICROSCOPY = "MICROSCOPY", + NATIVE_GEL_ELECTROPHORESIS = "NATIVE_GEL_ELECTROPHORESIS", + NONE = "NONE", + SAXS = "SAXS", + SCANNING_TRANSMISSION_ELECTRON_MICROSCOPY = "SCANNING_TRANSMISSION_ELECTRON_MICROSCOPY", + SURFACE_PLASMON_RESONANCE = "SURFACE_PLASMON_RESONANCE" +} + +export enum Type { + GLOBAL = "GLOBAL", + LOCAL = "LOCAL", + PSEUDO = "PSEUDO" +} + +export enum UnpublishedFlag { + N = "N", + Y = "Y" +} + +export enum PdbxDiffrnProtocol { + LAUE = "LAUE", + MAD = "MAD", + SINGLE_WAVELENGTH = "SINGLE_WAVELENGTH" +} + +export enum PdbxMonochromaticOrLaueML { + L = "L", + M = "M" +} + +export enum PdbxScatteringType { + ELECTRON = "ELECTRON", + NEUTRON = "NEUTRON", + X_RAY = "X_RAY" +} + +export enum Source { + ELECTRON_MICROSCOPE = "ELECTRON_MICROSCOPE", + FREE_ELECTRON_LASER = "FREE_ELECTRON_LASER", + LIQUID_ANODE = "LIQUID_ANODE", + NUCLEAR_REACTOR = "NUCLEAR_REACTOR", + ROTATING_ANODE = "ROTATING_ANODE", + SEALED_TUBE = "SEALED_TUBE", + SPALLATION_SOURCE = "SPALLATION_SOURCE", + SYNCHROTRON = "SYNCHROTRON" +} + +export enum RefSpace { + REAL = "REAL", + RECIPROCAL = "RECIPROCAL" +} + +export enum SymmetryType { + HELICAL = "HELICAL", + POINT = "POINT", + _2_D_CRYSTAL = "_2_D_CRYSTAL", + _3_D_CRYSTAL = "_3_D_CRYSTAL" +} + +export enum AggregationState { + CELL = "CELL", + FILAMENT = "FILAMENT", + HELICAL_ARRAY = "HELICAL_ARRAY", + PARTICLE = "PARTICLE", + TISSUE = "TISSUE", + _2_D_ARRAY = "_2_D_ARRAY", + _3_D_ARRAY = "_3_D_ARRAY" +} + +export enum ReconstructionMethod { + CRYSTALLOGRAPHY = "CRYSTALLOGRAPHY", + HELICAL = "HELICAL", + SINGLE_PARTICLE = "SINGLE_PARTICLE", + SUBTOMOGRAM_AVERAGING = "SUBTOMOGRAM_AVERAGING", + TOMOGRAPHY = "TOMOGRAPHY" +} + +export enum EmbeddingApplied { + NO = "NO", + YES = "YES" +} + +export enum StainingApplied { + NO = "NO", + YES = "YES" +} + +export enum VitrificationApplied { + NO = "NO", + YES = "YES" +} + +export enum SrcMethod { + MAN = "MAN", + NAT = "NAT", + SYN = "SYN" +} + +export enum RcsbType { + DNA = "DNA", + HYBRID = "HYBRID", + OTHER = "OTHER", + POLYPEPTIDE = "POLYPEPTIDE", + RNA = "RNA" +} + +export enum Level { + _100 = "_100", + _30 = "_30", + _40 = "_40", + _50 = "_50", + _60 = "_60", + _70 = "_70", + _80 = "_80", + _90 = "_90", + _95 = "_95" +} + +export enum PdbFormatCompatible { + N = "N", + Y = "Y" +} + +export namespace RcsbSymmetry { + export type Variables = { + readonly pdbId?: string | null; + }; + + export type Query = { + readonly __typename?: "Query"; + readonly assemblies?: ReadonlyArray<Assemblies | null> | null; + }; + + export type Assemblies = { + readonly __typename?: "CoreAssembly"; + readonly assembly_id?: number | null; + readonly rcsb_annotation_symmetry?: RcsbAnnotationSymmetry | null; + }; + + export type RcsbAnnotationSymmetry = { + readonly __typename?: "RcsbAnnotationSymmetry"; + readonly source?: string | null; + readonly symmetry_features?: ReadonlyArray<SymmetryFeatures | null> | null; + }; + + export type SymmetryFeatures = { + readonly __typename?: "SymmetryFeature"; + readonly type?: Type | null; + readonly clusters?: ReadonlyArray<Clusters | null> | null; + readonly stoichiometry?: Stoichiometry | null; + readonly symmetry_axes?: ReadonlyArray<SymmetryAxes | null> | null; + }; + + export type Clusters = { + readonly __typename?: "Cluster"; + readonly avg_rmsd?: number | null; + readonly members?: ReadonlyArray<string | null> | null; + }; + + export type Stoichiometry = { + readonly __typename?: "Stoichiometry"; + readonly description?: string | null; + readonly value?: ReadonlyArray<string | null> | null; + }; + + export type SymmetryAxes = { + readonly __typename?: "SymmetryAxis"; + readonly order?: number | null; + readonly start?: ReadonlyArray<number | null> | null; + readonly end?: ReadonlyArray<number | null> | null; + }; +} diff --git a/src/servers/model/properties/rcsb/symmetry.ts b/src/servers/model/properties/rcsb/symmetry.ts new file mode 100644 index 000000000..e34934c8d --- /dev/null +++ b/src/servers/model/properties/rcsb/symmetry.ts @@ -0,0 +1,150 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { GraphQLClient } from 'graphql-request' + +import { RcsbSymmetry } from './graphql/types'; +import query from './graphql/symmetry.gql'; + +import { Model, ModelPropertyDescriptor } from 'mol-model/structure'; +import { CifWriter } from 'mol-io/writer/cif'; +import { Database as _Database, Column, Table } from 'mol-data/db' +import { Category } from 'mol-io/writer/cif/encoder'; +import { Tensor } from 'mol-math/linear-algebra'; +import { CifExportContext } from 'mol-model/structure/export/mmcif'; + +const { str, int, float, Aliased, Vector, List } = Column.Schema; + +function getInstance(name: keyof SymmetryAnnotation.Schema): (ctx: CifExportContext) => CifWriter.Category.Instance<any, any> { + return function(ctx: CifExportContext) { + const db = SymmetryAnnotation.get(ctx.model); + return db ? Category.ofTable(db[name]) : CifWriter.Category.Empty; + } +} + +function getCategory(name: keyof SymmetryAnnotation.Schema) { + return { name, instance: getInstance(name) } +} + +function createDatabase(assemblies: ReadonlyArray<RcsbSymmetry.Assemblies>): SymmetryAnnotation.Database { + const Schema = SymmetryAnnotation.Schema + + const featureRows: Table.Row<typeof Schema.symmetry_annotation_feature>[] = [] + const clusterRows: Table.Row<typeof Schema.symmetry_annotation_cluster>[] = [] + const axisRows: Table.Row<typeof Schema.symmetry_annotation_axis>[] = [] + + let id = 0 + for (let i = 0, il = assemblies.length; i < il; ++i) { + const a = assemblies[i] + const assembly_id = (a.assembly_id!).toString() + const source = a.rcsb_annotation_symmetry!.source! + const symmetry_features = a.rcsb_annotation_symmetry!.symmetry_features! + for (let j = 0, jl = symmetry_features.length; j < jl; ++j) { + const f = symmetry_features[j]! + featureRows.push({ + id, + assembly_id, + source, + type: f.type!, + stoichiometry_value: (f.stoichiometry!.value!) as string[], + stoichiometry_description: f.stoichiometry!.description! + }) + + const clusters = f.clusters + if (clusters) { + for (let k = 0, kl = clusters.length; k < kl; ++k) { + const c = clusters[k]! + clusterRows.push({ + feature_id: id, + avg_rmsd: c.avg_rmsd!, + members: c.members as string[] + }) + } + } + + const axes = f.symmetry_axes + if (axes) { + for (let k = 0, kl = axes.length; k < kl; ++k) { + const a = axes[k]! + axisRows.push({ + feature_id: id, + order: a.order!, + start: a.start as Tensor.Data, + end: a.end as Tensor.Data + }) + } + } + + id += 1 + } + } + + return _Database.ofTables('symmetry_annotation', Schema, { + symmetry_annotation_feature: Table.ofRows(Schema.symmetry_annotation_feature, featureRows), + symmetry_annotation_cluster: Table.ofRows(Schema.symmetry_annotation_cluster, clusterRows), + symmetry_annotation_axis: Table.ofRows(Schema.symmetry_annotation_axis, axisRows) + }) +} + +const _Descriptor: ModelPropertyDescriptor = { + isStatic: true, + name: 'symmetry_annotation', + cifExport: { + categories: [ + getCategory('symmetry_annotation_feature'), + getCategory('symmetry_annotation_cluster'), + getCategory('symmetry_annotation_axis') + ] + } +} + +const client = new GraphQLClient('http://rest-experimental.rcsb.org/graphql') + +export namespace SymmetryAnnotation { + export const Schema = { + symmetry_annotation_feature: { + id: int, + assembly_id: str, + source: str, + type: Aliased<'GLOBAL' | 'LOCAL' | 'PSEUDO'>(str), + stoichiometry_value: List(',', x => x), + stoichiometry_description: str + }, + symmetry_annotation_cluster: { + feature_id: int, + avg_rmsd: float, + members: List(',', x => x) + }, + symmetry_annotation_axis: { + feature_id: int, + order: int, + start: Vector(3), + end: Vector(3) + } + } + export type Schema = typeof Schema + export interface Database extends _Database<Schema> {} + + export const Descriptor = _Descriptor; + + export async function attachFromRCSB(model: Model) { + if (model.customProperties.has(Descriptor)) return true; + + const variables: RcsbSymmetry.Variables = { pdbId: model.label.toLowerCase() }; + const result = await client.request<RcsbSymmetry.Query>(query, variables); + if (!result || !result.assemblies) return false; + + const db: Database = createDatabase(result.assemblies as ReadonlyArray<RcsbSymmetry.Assemblies>) + model.customProperties.add(Descriptor); + model._staticPropertyData.__SymmetryAnnotation__ = db; + + return true; + } + + export function get(model: Model): Database | undefined { + return model._staticPropertyData.__SymmetryAnnotation__; + } +} \ No newline at end of file diff --git a/src/servers/model/test.ts b/src/servers/model/test.ts index 882c18b6a..14bafb1ba 100644 --- a/src/servers/model/test.ts +++ b/src/servers/model/test.ts @@ -1,5 +1,6 @@ import { resolveJob } from './server/query'; import * as fs from 'fs' +import * as path from 'path' import { StructureCache } from './server/structure-wrapper'; import { createJob } from './server/jobs'; @@ -33,13 +34,25 @@ function wrapFile(fn: string) { return w; } +const basePath = path.join(__dirname, '..', '..', '..', '..') +const examplesPath = path.join(basePath, 'examples') +const outPath = path.join(basePath, 'build', 'test') +if (!fs.existsSync(outPath)) fs.mkdirSync(outPath); + async function run() { try { - const request = createJob('_local_', 'e:/test/quick/1cbs_updated.cif', 'residueInteraction', { label_comp_id: 'REA' }); + // const request = createJob('_local_', 'e:/test/quick/1cbs_updated.cif', 'residueInteraction', { label_comp_id: 'REA' }); + // const encoder = await resolveJob(request); + // const writer = wrapFile('e:/test/mol-star/1cbs_full.cif'); + // const testFile = '1crn.cif' + const testFile = '1grm_updated.cif' + const request = createJob('_local_', path.join(examplesPath, testFile), 'full', {}); const encoder = await resolveJob(request); - const writer = wrapFile('e:/test/mol-star/1cbs_full.cif'); + const writer = wrapFile(path.join(outPath, testFile)); encoder.writeTo(writer); writer.end(); + } catch (e) { + console.error(e) } finally { StructureCache.expireAll(); } -- GitLab