diff --git a/package.json b/package.json index 44be598d223bc8ea086be627f13ad82fbf2ee40c..9d02b0b613685585a09cac3c10b91071313bf896 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,6 @@ "tslint": "^5.17.0", "typescript": "^3.5.1", "uglify-js": "^3.6.0", - "util.promisify": "^1.0.0", "webpack": "^4.32.2", "webpack-cli": "^3.3.2" }, @@ -111,6 +110,7 @@ "react-dom": "^16.8.6", "rxjs": "^6.5.2", "swagger-ui-dist": "^3.22.2", + "util.promisify": "^1.0.0", "xhr2": "^0.2.0" } } diff --git a/src/mol-model-props/rcsb/assembly-symmetry.ts b/src/mol-model-props/rcsb/assembly-symmetry.ts index 150a93ef4766560f6e76e3e3a2c875766e57c7a5..bdf36072cb34acacc21b0eb9a5da5ff30c894363 100644 --- a/src/mol-model-props/rcsb/assembly-symmetry.ts +++ b/src/mol-model-props/rcsb/assembly-symmetry.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -18,7 +18,6 @@ import { CifCategory } from '../../mol-io/reader/cif'; import { PropertyWrapper } from '../../mol-model-props/common/wrapper'; import { Task, RuntimeContext } from '../../mol-task'; import { GraphQLClient } from '../../mol-util/graphql-client'; -import { ajaxGet } from '../../mol-util/data-source'; const { str, int, float, Aliased, Vector, List } = Column.Schema; @@ -183,13 +182,12 @@ export function AssemblySymmetry(db: AssemblySymmetry.Database): AssemblySymmetr type SymmetryKind = 'GLOBAL' | 'LOCAL' | 'PSEUDO' type SymmetryType = 'ASYMMETRIC' | 'CYCLIC' | 'DIHEDRAL' | 'HELICAL' | 'ICOSAHEDRAL' | 'OCTAHEDRAL' | 'TETRAHEDRAL' -const Client = new GraphQLClient(AssemblySymmetry.GraphQLEndpointURL, ajaxGet) - export namespace AssemblySymmetry { export function is(x: any): x is AssemblySymmetry { return x['@type'] === 'rcsb_assembly_symmetry' } - export const GraphQLEndpointURL = '//rest-dev.rcsb.org/graphql' + export const GraphQLEndpointURL = '//rest-staging.rcsb.org/graphql' + export const Schema = { rcsb_assembly_symmetry_info: { updated_datetime_utc: Column.Schema.str @@ -257,7 +255,7 @@ export namespace AssemblySymmetry { export const Descriptor = _Descriptor; - export async function attachFromCifOrAPI(model: Model, client: GraphQLClient = Client, ctx?: RuntimeContext) { + export async function attachFromCifOrAPI(model: Model, client: GraphQLClient, ctx?: RuntimeContext) { if (model.customProperties.has(Descriptor)) return true; let db: Database @@ -266,8 +264,10 @@ export namespace AssemblySymmetry { db = createDatabaseFromCif(model) } else { let result: AssemblySymmetryGraphQL.Query + console.log('model.label.toLowerCase()', model.label.toLowerCase()) const variables: AssemblySymmetryGraphQL.Variables = { pdbId: model.label.toLowerCase() }; try { + console.log('foo', client) result = await client.request<AssemblySymmetryGraphQL.Query>(ctx || RuntimeContext.Synchronous, query, variables); } catch (e) { console.error(e) diff --git a/src/servers/common/util.ts b/src/servers/common/util.ts new file mode 100644 index 0000000000000000000000000000000000000000..3086ffb1f4ee0b69ec51a1a86b15e5326388444e --- /dev/null +++ b/src/servers/common/util.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2018-2019 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 { ConsoleLogger } from '../../mol-util/console-logger'; + +export function getParam<T>(params: any, ...path: string[]): T | undefined { + try { + let current = params; + for (const p of path) { + if (typeof current === 'undefined') return; + current = current[p]; + } + return current; + } catch (e) { + ConsoleLogger.error('Config', `Unable to retrieve property ${path.join('.')} from ${JSON.stringify(params)}`); + } +} \ No newline at end of file diff --git a/src/servers/model/config.ts b/src/servers/model/config.ts index ac963eadf6789115e96101880ee7e61449d655bd..860f6a0a436547e5923ffdb8d233b76b90d04e42 100644 --- a/src/servers/model/config.ts +++ b/src/servers/model/config.ts @@ -54,8 +54,9 @@ const config = { */ customProperties: <ModelPropertyProviderConfig | string>{ sources: [ - './properties/pdbe', - './properties/rcsb' + 'pdbe', + 'rcsb', + 'wwpdb' ], params: { PDBe: { @@ -68,6 +69,14 @@ const config = { File: { residuewise_outlier_summary: 'e:/test/mol-star/model/props/' } + }, + RCSB: { + API: { + assembly_symmetry: 'https://rest-staging.rcsb.org/graphql' + } + }, + wwPDB: { + chemCompBondTablePath: '' } } }, diff --git a/src/servers/model/preprocess/master.ts b/src/servers/model/preprocess/master.ts index 9ff8b8e1fbb321579029f40963ae563aba7f502a..4c7068208838bd82a9e09ea9c00f9ad23255ad57 100644 --- a/src/servers/model/preprocess/master.ts +++ b/src/servers/model/preprocess/master.ts @@ -10,9 +10,27 @@ import * as argparse from 'argparse' import { runMaster, PreprocessEntry } from './parallel'; import { ModelPropertyProviderConfig } from '../property-provider'; +function description() { + const exampleCfg = { + numProcesses: 1, + customProperties: { + sources: [ + 'wwpdb' + ], + params: { + wwPDB: { + chemCompBondTablePath: './build/data/ccb.bcif' + } + } + } + } + + return `Preprocess CIF files to include custom properties and convert them to BinaryCIF format.\n\nExample cfg.json: ${JSON.stringify(exampleCfg, null, 2)}` +} + const cmdParser = new argparse.ArgumentParser({ addHelp: true, - description: 'Preprocess CIF files to include custom properties and convert them to BinaryCIF format.' + description: description() }); cmdParser.addArgument(['--input', '-i'], { help: 'Input filename', required: false }); cmdParser.addArgument(['--outCIF', '-oc'], { help: 'Output CIF filename', required: false }); diff --git a/src/servers/model/preprocess/preprocess.ts b/src/servers/model/preprocess/preprocess.ts index 727da1d02398ccfab086b9db97bca3d221fdeeb8..3976748265ff4517f9fbc52df0a4b5e9faf0be56 100644 --- a/src/servers/model/preprocess/preprocess.ts +++ b/src/servers/model/preprocess/preprocess.ts @@ -21,7 +21,6 @@ export function preprocessFile(filename: string, propertyProvider?: ModelPropert : convert(filename, outputCif, outputBcif); } - async function preprocess(filename: string, propertyProvider?: ModelPropertiesProvider, outputCif?: string, outputBcif?: string) { const input = await readStructureWrapper('entry', '_local_', filename, propertyProvider); const categories = await classifyCif(input.cifFrame); diff --git a/src/servers/model/properties/providers/pdbe.ts b/src/servers/model/properties/providers/pdbe.ts index f0b254dd0aa98000cb0d7aeb84865f0359fe9664..7935d4a49bd1f971f84240e778d32459e61b7fd4 100644 --- a/src/servers/model/properties/providers/pdbe.ts +++ b/src/servers/model/properties/providers/pdbe.ts @@ -14,6 +14,7 @@ import { PDBePreferredAssembly } from '../../../../mol-model-props/pdbe/preferre import { PDBeStructRefDomain } from '../../../../mol-model-props/pdbe/struct-ref-domain'; import { AttachModelProperty } from '../../property-provider'; import { ConsoleLogger } from '../../../../mol-util/console-logger'; +import { getParam } from '../../../common/util'; export const PDBe_structureQualityReport: AttachModelProperty = ({ model, params, cache }) => { const PDBe_apiSourceJson = useFileSource(params) @@ -68,20 +69,6 @@ function useFileSource(params: any) { return !!getParam<boolean>(params, 'PDBe', 'UseFileSource') } -function getParam<T>(params: any, ...path: string[]): T | undefined { - try { - let current = params; - for (const p of path) { - if (typeof current === 'undefined') return; - current = current[p]; - } - return current; - } catch (e) { - ConsoleLogger.error('Config', `Unable to retrieve property ${path.join('.')} from ${JSON.stringify(params)}`); - } -} - - function apiQueryProvider(urlPrefix: string, cache: any) { const cacheKey = UUID.create22(); return async (model: Model) => { diff --git a/src/servers/model/properties/providers/rcsb.ts b/src/servers/model/properties/providers/rcsb.ts index e2ae21e66dd80fe2487b687600a708524b7fcf7d..011ad4b3cb367457b2c749e723628be487509c76 100644 --- a/src/servers/model/properties/providers/rcsb.ts +++ b/src/servers/model/properties/providers/rcsb.ts @@ -1,12 +1,23 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { AssemblySymmetry } from '../../../../mol-model-props/rcsb/assembly-symmetry'; import { AttachModelProperty } from '../../property-provider'; +import { ajaxGet } from '../../../../mol-util/data-source'; +import { GraphQLClient } from '../../../../mol-util/graphql-client'; +import { getParam } from '../../../common/util'; -export const RCSB_assemblySymmetry: AttachModelProperty = ({ model }) => { - return AssemblySymmetry.attachFromCifOrAPI(model) +export const RCSB_assemblySymmetry: AttachModelProperty = ({ model, params }) => { + const url = getApiUrl(params, 'assembly_symmetry', `https:${AssemblySymmetry.GraphQLEndpointURL}`) + const client = new GraphQLClient(url, ajaxGet) + return AssemblySymmetry.attachFromCifOrAPI(model, client) +} + +function getApiUrl(params: any, name: string, fallback: string) { + const path = getParam<string>(params, 'RCSB', 'API', name); + if (!path) return fallback; + return path; } \ No newline at end of file diff --git a/src/servers/model/properties/providers/wwpdb.ts b/src/servers/model/properties/providers/wwpdb.ts new file mode 100644 index 0000000000000000000000000000000000000000..d5ea7136e5a082ff8b7140f74fd9e7a2d21e6b62 --- /dev/null +++ b/src/servers/model/properties/providers/wwpdb.ts @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as fs from 'fs' +import * as util from 'util' +import { AttachModelProperty } from '../../property-provider'; +import { ChemCompBond } from '../../../../mol-model-props/wwpdb/chem-comp-bond'; +import { Table } from '../../../../mol-data/db'; +import { CIF } from '../../../../mol-io/reader/cif'; +import { getParam } from '../../../common/util'; + +require('util.promisify').shim() +const readFile = util.promisify(fs.readFile) + +export const wwPDB_chemCompBond: AttachModelProperty = ({ model, params }) => { + const wwPDB_apiSourceTable = getChemCompBondTableProvider(getTablePath(params)) + return ChemCompBond.attachFromCifOrTable(model, { wwPDB_apiSourceTable }); +} + +async function read(path: string) { + return path.endsWith('.bcif') ? new Uint8Array(await readFile(path)) : readFile(path, 'utf8'); +} + +function getChemCompBondTableProvider(path: string): () => Promise<Table<ChemCompBond.Schema['chem_comp_bond']>> { + let chemCompBondTable: Table<ChemCompBond.Schema['chem_comp_bond']> + return async function() { + if (chemCompBondTable === undefined) { + const parsed = await CIF.parse(await read(path)).run() + if (parsed.isError) throw new Error(parsed.toString()) + const table = CIF.toDatabase(ChemCompBond.Schema, parsed.result.blocks[0]) + chemCompBondTable = table.chem_comp_bond + } + return chemCompBondTable + } +} + +function getTablePath(params: any) { + const path = getParam<string>(params, 'wwPDB', 'chemCompBondTablePath'); + if (!path) throw new Error(`wwPDB 'chemCompBondTablePath' not set!`); + return path; +} \ No newline at end of file diff --git a/src/servers/model/properties/wwpdb.ts b/src/servers/model/properties/wwpdb.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb4040db1f7ac0ac78899f42d472e24113019bd9 --- /dev/null +++ b/src/servers/model/properties/wwpdb.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { AttachModelProperties } from '../property-provider'; +import { wwPDB_chemCompBond } from './providers/wwpdb'; + +export const attachModelProperties: AttachModelProperties = (args) => { + // return a list of promises that start attaching the props in parallel + // (if there are downloads etc.) + return [ + wwPDB_chemCompBond(args) + ]; +} \ No newline at end of file diff --git a/src/servers/model/property-provider.ts b/src/servers/model/property-provider.ts index d3b4ad00a0d836192f02361b408ee31ad3f189a4..d929c30c7320093b85d5b4677fe889fcb7c3b81f 100644 --- a/src/servers/model/property-provider.ts +++ b/src/servers/model/property-provider.ts @@ -9,6 +9,17 @@ import { Model } from '../../mol-model/structure'; import Config from './config'; import { ConsoleLogger } from '../../mol-util/console-logger'; +// TODO enable dynamic imports again +import * as pdbeProps from './properties/pdbe' +import * as rcsbProps from './properties/rcsb' +import * as wwpdbProps from './properties/wwpdb' + +const attachModelProperties: { [k: string]: AttachModelProperties } = { + pdbe: pdbeProps.attachModelProperties, + rcsb: rcsbProps.attachModelProperties, + wwpdb: wwpdbProps.attachModelProperties +} + export interface ModelPropertyProviderConfig { sources: string[], params?: { [name: string]: any } @@ -39,7 +50,11 @@ export function createModelPropertiesProvider(configOrPath: ModelPropertyProvide const ps: AttachModelProperties[] = []; for (const p of config.sources) { - ps.push(require(p).attachModelProperties); + if (p in attachModelProperties) { + ps.push(attachModelProperties[p]); + } else { + ConsoleLogger.error('Config', `Could not find property provider '${p}', ignoring.`); + } } return (model, cache) => { diff --git a/src/servers/volume/local.ts b/src/servers/volume/local.ts index 99993cfbcfa10612463f83325ff959db7cd93f43..f2145c2badfc82440a9a4ed0ed1c2cc68bc2aa76 100644 --- a/src/servers/volume/local.ts +++ b/src/servers/volume/local.ts @@ -16,7 +16,7 @@ import { LimitsConfig, addLimitsArgs, setLimitsConfig } from './config'; console.log(`VolumeServer Local ${VERSION}, (c) 2018-2019, Mol* contributors`); console.log(); -function help() { +function description() { const exampleJobs: LocalApi.JobEntry[] = [{ source: { filename: `g:/test/mdb/xray-1tqn.mdb`, @@ -55,7 +55,7 @@ function help() { const parser = new argparse.ArgumentParser({ addHelp: true, - description: help() + description: description() }); addLimitsArgs(parser) parser.addArgument(['jobs'], { diff --git a/webpack.config.js b/webpack.config.js index 3de9e1565111c706a5f326ff84f7f9f143cc4e28..44062a2aef1a21101ae8febec3e63759806bec4f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -73,6 +73,12 @@ function createNodeEntryPoint(name, dir, out) { target: 'node', entry: path.resolve(__dirname, `lib/${dir}/${name}.js`), output: { filename: `${name}.js`, path: path.resolve(__dirname, `build/${out}`) }, + externals: { + argparse: 'require("argparse")', + 'node-fetch': 'require("node-fetch")', + 'util.promisify': 'require("util.promisify")', + xhr2: 'require("xhr2")', + }, ...sharedConfig } }