diff --git a/README.md b/README.md index 29881c843b2f5e4d025d67af3d1129c341940fa1..bea25cde6c5ecf65b2286159027280754b09413c 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ The goal of **Mol\*** (*/'mol-star/*) is to provide a technology stack that will This particular project is the implementation of this technology (still under development). +*If you are looking for the "MOLeculAR structure annoTator", that package is now available on NPM as [MolArt](https://www.npmjs.com/package/molart).* + ## Project Overview The core of Mol* currently consists of these modules (see under `src/`): @@ -61,8 +63,7 @@ This project builds on experience from previous solutions: DEBUG=molstar npm run watch ### Build for production: - npm run build - NODE_ENV=production npm run build-webpack + NODE_ENV=production npm run build **Run** @@ -94,7 +95,7 @@ Install CIFTools `npm install ciftools -g` ### Other scripts **Create chem comp bond table** - export NODE_PATH="lib"; node --max-old-space-size=8192 build/src/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b + export NODE_PATH="lib"; node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b **Test model server** @@ -132,14 +133,17 @@ To get syntax highlighting for shader and graphql files add the following to Vis ## Publish -## Prerelease +### Prerelease npm version prerelease # asumes the current version ends with '-dev.X' npm publish --tag next -## Release +### Release npm version 0.X.0 # provide valid semver string npm publish +## Deploy + node ./scripts/deploy.js # currently updates the viewer on molstar.org/viewer + ## Contributing Just open an issue or make a pull request. All contributions are welcome. diff --git a/package.json b/package.json index d1f18d63a1792e98a6fdf73ab0824c7585e8d719..3c72052a1fe35f1638fae0ee923a33acdda51e80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "molstar", - "version": "0.2.1", + "version": "0.2.2", "description": "A comprehensive macromolecular library.", "homepage": "https://github.com/molstar/molstar#readme", "repository": { @@ -23,7 +23,6 @@ "watch-webpack": "webpack -w --mode development --display minimal", "model-server": "node lib/servers/model/server.js", "model-server-watch": "nodemon --watch lib lib/servers/model/server.js", - "postinstall": "npm run build", "preversion": "npm run test", "postversion": "git push && git push --tags", "prepublishOnly": "npm run test && npm run build" @@ -66,9 +65,10 @@ "circular-dependency-plugin": "^5.0.2", "concurrently": "^4.1.0", "cpx": "^1.5.0", - "css-loader": "^2.1.1", + "css-loader": "^3.0.0", "extra-watch-webpack-plugin": "^1.0.3", - "file-loader": "^3.0.1", + "file-loader": "^4.0.0", + "fs-extra": "^8.0.1", "graphql-code-generator": "^0.18.2", "graphql-codegen-time": "^0.18.2", "graphql-codegen-typescript-template": "^0.18.2", @@ -76,26 +76,25 @@ "jest-raw-loader": "^1.0.1", "mini-css-extract-plugin": "^0.7.0", "node-sass": "^4.12.0", - "raw-loader": "^2.0.0", + "raw-loader": "^3.0.0", "resolve-url-loader": "^3.1.0", "sass-loader": "^7.1.0", + "simple-git": "^1.113.0", "style-loader": "^0.23.1", "ts-jest": "^24.0.2", "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" + "webpack": "^4.33.0", + "webpack-cli": "^3.3.4" }, "dependencies": { "@types/argparse": "^1.0.36", "@types/benchmark": "^1.0.31", "@types/compression": "0.0.36", - "@types/express": "^4.16.1", + "@types/express": "^4.17.0", "@types/jest": "^24.0.13", - "@types/node": "^12.0.4", - "@types/node-fetch": "^2.3.4", + "@types/node": "^12.0.8", + "@types/node-fetch": "^2.3.5", "@types/react": "^16.8.19", "@types/react-dom": "^16.8.4", "@types/swagger-ui-dist": "3.0.0", @@ -109,7 +108,8 @@ "react": "^16.8.6", "react-dom": "^16.8.6", "rxjs": "^6.5.2", - "swagger-ui-dist": "^3.22.2", + "swagger-ui-dist": "^3.22.3", + "util.promisify": "^1.0.0", "xhr2": "^0.2.0" } } diff --git a/scripts/deploy.js b/scripts/deploy.js new file mode 100644 index 0000000000000000000000000000000000000000..c1d4fc30fdb5d60f0b9161ff7294b7926e975029 --- /dev/null +++ b/scripts/deploy.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +const git = require('simple-git') +const path = require('path') +const fs = require("fs") +const fse = require("fs-extra") + +const remoteUrl = "https://github.com/molstar/molstar.github.io.git" +const buildDir = path.resolve(__dirname, '../build/') +const deployDir = path.resolve(buildDir, 'deploy/') +const localPath = path.resolve(deployDir, 'molstar.github.io/') + +function log(command, stdout, stderr) { + if (command) { + console.log('\n###', command) + stdout.pipe(process.stdout) + stderr.pipe(process.stderr) + } +} + +function copyViewer() { + console.log('\n###', 'copy viewer files') + const viewerBuildPath = path.resolve(buildDir, '../build/viewer/') + const viewerDeployPath = path.resolve(localPath, 'viewer/') + fse.copySync(viewerBuildPath, viewerDeployPath, { overwrite: true }) +} + +if (!fs.existsSync(localPath)) { + console.log('\n###', 'create localPath') + fs.mkdirSync(localPath, { recursive: true }) +} + +process.chdir(localPath); + +if (!fs.existsSync(path.resolve(localPath, '.git/'))) { + console.log('\n###', 'clone repository') + git() + .outputHandler(log) + .clone(remoteUrl, localPath) + .fetch(['--all']) + .exec(copyViewer) + .add(['-A']) + .commit('updated viewer') + .push() +} else { + console.log('\n###', 'update repository') + git() + .outputHandler(log) + .fetch(['--all']) + .reset(['--hard', 'origin/master']) + .exec(copyViewer) + .add(['-A']) + .commit('updated viewer') + .push() +} \ No newline at end of file diff --git a/src/apps/chem-comp-bond/create-table.ts b/src/apps/chem-comp-bond/create-table.ts index cc29af24a60f6490751d22ec1b4101d68cb3665e..ba541008609cb3c9c61c6dd09ff192f8e3535ad9 100644 --- a/src/apps/chem-comp-bond/create-table.ts +++ b/src/apps/chem-comp-bond/create-table.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> */ @@ -15,12 +15,13 @@ const readFile = util.promisify(fs.readFile) const writeFile = util.promisify(fs.writeFile) import { Progress } from '../../mol-task' -import { Database, Table, DatabaseCollection, Column } from '../../mol-data/db' +import { Database, Table, DatabaseCollection } from '../../mol-data/db' import { CIF } from '../../mol-io/reader/cif' import { CifWriter } from '../../mol-io/writer/cif' import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd' import { SetUtils } from '../../mol-util/set' import { DefaultMap } from '../../mol-util/map' +import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras'; export async function ensureAvailable(path: string, url: string) { if (FORCE_DOWNLOAD || !fs.existsSync(path)) { @@ -74,16 +75,6 @@ export function getEncodedCif(name: string, database: Database<Database.Schema>, type CCB = Table<CCD_Schema['chem_comp_bond']> type CCA = Table<CCD_Schema['chem_comp_atom']> -const ChemCompBond_Schema = { - comp_id: CCD_Schema['chem_comp_bond'].comp_id, - atom_id_1: CCD_Schema['chem_comp_bond'].atom_id_1, - atom_id_2: CCD_Schema['chem_comp_bond'].atom_id_2, - value_order: CCD_Schema['chem_comp_bond'].value_order, - pdbx_aromatic_flag: CCD_Schema['chem_comp_bond'].pdbx_aromatic_flag, - pdbx_stereo_config: CCD_Schema['chem_comp_bond'].pdbx_stereo_config, - molstar_protonation_variant: Column.Schema.Str() -} - function ccbKey(compId: string, atomId1: string, atomId2: string) { return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}` } @@ -202,14 +193,14 @@ async function createBonds() { } } - const bondTable = Table.ofArrays(ChemCompBond_Schema, { + const bondTable = Table.ofArrays(mmCIF_chemCompBond_schema, { comp_id, atom_id_1, atom_id_2, value_order, pdbx_aromatic_flag, pdbx_stereo_config, molstar_protonation_variant }) const bondDatabase = Database.ofTables( TABLE_NAME, - { chem_comp_bond: ChemCompBond_Schema }, + { chem_comp_bond: mmCIF_chemCompBond_schema }, { chem_comp_bond: bondTable } ) @@ -220,12 +211,15 @@ async function run(out: string, binary = false) { const bonds = await createBonds() const cif = getEncodedCif(TABLE_NAME, bonds, binary) + if (!fs.existsSync(path.dirname(out))) { + fs.mkdirSync(path.dirname(out)); + } writeFile(out, cif) } const TABLE_NAME = 'CHEM_COMP_BONDS' -const DATA_DIR = path.join(__dirname, '..', '..', '..', 'data') +const DATA_DIR = path.join(__dirname, '..', '..', '..', 'build/data') const CCD_PATH = path.join(DATA_DIR, 'components.cif') const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif') const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif' diff --git a/src/examples/proteopedia-wrapper/helpers.ts b/src/examples/proteopedia-wrapper/helpers.ts index a15bcca86c8d9c3d20d16417680c583f3cc5f4a1..7d1360c3e31e095ea745b9afb91d16e54cb21131 100644 --- a/src/examples/proteopedia-wrapper/helpers.ts +++ b/src/examples/proteopedia-wrapper/helpers.ts @@ -105,6 +105,8 @@ export enum StateElements { ModelProps = 'model-props', Assembly = 'assembly', + VolumeStreaming = 'volume-streaming', + Sequence = 'sequence', SequenceVisual = 'sequence-visual', Het = 'het', diff --git a/src/examples/proteopedia-wrapper/index.html b/src/examples/proteopedia-wrapper/index.html index 288db037a5d4f2406c2a3b486fd2417dde62e592..5fd7d7bbc083c8f9e1637603fd524e2021e13a8d 100644 --- a/src/examples/proteopedia-wrapper/index.html +++ b/src/examples/proteopedia-wrapper/index.html @@ -40,6 +40,13 @@ width: 100%; display: block; } + + #volume-streaming-wrapper { + position: absolute; + top: 100px; + left: 780px; + width: 300px; + } </style> <link rel="stylesheet" type="text/css" href="app.css" /> <script type="text/javascript" src="./index.js"></script> @@ -55,6 +62,7 @@ </select> </div> <div id="app"></div> + <div id="volume-streaming-wrapper"></div> <script> // it might be a good idea to define these colors in a separate script file var CustomColors = [0x00ff00, 0x0000ff]; @@ -66,7 +74,7 @@ function $(id) { return document.getElementById(id); } - var pdbId = '1eve', assemblyId= 'preferred'; + var pdbId = '1cbs', assemblyId= 'preferred'; var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif'; var format = 'cif'; @@ -151,6 +159,11 @@ addHetGroupsContainer(); addSeparator(); + addHeader('Exp. Data'); + addControl('Init', () => PluginWrapper.experimentalData.init($('volume-streaming-wrapper'))); + addControl('Remove', () => PluginWrapper.experimentalData.remove()); + + addSeparator(); addHeader('State'); var snapshot; diff --git a/src/examples/proteopedia-wrapper/index.ts b/src/examples/proteopedia-wrapper/index.ts index bd9d4447f5a85563525caff904748792d1fdaa98..d444fed9d4c138a0488ff63cb1d0a8cd19dc1b15 100644 --- a/src/examples/proteopedia-wrapper/index.ts +++ b/src/examples/proteopedia-wrapper/index.ts @@ -4,6 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import * as ReactDOM from 'react-dom'; import { createPlugin, DefaultPluginSpec } from '../../mol-plugin'; import './index.html' import { PluginContext } from '../../mol-plugin/context'; @@ -13,11 +14,11 @@ import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transfo import { Color } from '../../mol-util/color'; import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects'; import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in'; -import { StateBuilder, StateObject } from '../../mol-state'; +import { StateBuilder, StateObject, StateSelection } from '../../mol-state'; import { EvolutionaryConservation } from './annotation'; import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers'; import { RxEventHelper } from '../../mol-util/rx-event-helper'; -import { ControlsWrapper } from './ui/controls'; +import { ControlsWrapper, volumeStreamingControls } from './ui/controls'; import { PluginState } from '../../mol-plugin/state'; import { Scheduler } from '../../mol-task'; import { createProteopediaCustomTheme } from './coloring'; @@ -26,6 +27,8 @@ import { BuiltInStructureRepresentations } from '../../mol-repr/structure/regist import { BuiltInColorThemes } from '../../mol-theme/color'; import { BuiltInSizeThemes } from '../../mol-theme/size'; import { ColorNames } from '../../mol-util/color/tables'; +import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers'; +import { ParamDefinition } from '../../mol-util/param-definition'; // import { Vec3 } from 'mol-math/linear-algebra'; // import { ParamDefinition } from 'mol-util/param-definition'; // import { Text } from 'mol-geo/geometry/text/text'; @@ -264,6 +267,28 @@ class MolStarProteopediaWrapper { } } + private experimentalDataElement?: Element = void 0; + experimentalData = { + init: async (parent: Element) => { + const asm = this.state.select(StateElements.Assembly)[0].obj!; + const params = ParamDefinition.getDefaultValues(InitVolumeStreaming.definition.params!(asm, this.plugin)); + params.behaviorRef = StateElements.VolumeStreaming; + params.defaultView = 'box'; + await this.plugin.runTask(this.state.applyAction(InitVolumeStreaming, params, StateElements.Assembly)); + this.experimentalDataElement = parent; + volumeStreamingControls(this.plugin, parent); + }, + remove: () => { + const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0]; + if (!r) return; + PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref }); + if (this.experimentalDataElement) { + ReactDOM.unmountComponentAtNode(this.experimentalDataElement); + this.experimentalDataElement = void 0; + } + } + } + hetGroups = { reset: () => { const update = this.state.build().delete(StateElements.HetGroupFocus); diff --git a/src/examples/proteopedia-wrapper/ui/controls.tsx b/src/examples/proteopedia-wrapper/ui/controls.tsx index 3de24ed90c969ad5902bea4443149ef48901e9a2..df7938cc3c9b1fa92fc1b9ddf2bc4d74c769fe79 100644 --- a/src/examples/proteopedia-wrapper/ui/controls.tsx +++ b/src/examples/proteopedia-wrapper/ui/controls.tsx @@ -5,10 +5,14 @@ */ import * as React from 'react'; +import * as ReactDOM from 'react-dom'; import { PluginUIComponent } from '../../../mol-plugin/ui/base'; -import { CurrentObject } from '../../../mol-plugin/ui/plugin'; +import { CurrentObject, PluginContextContainer } from '../../../mol-plugin/ui/plugin'; import { AnimationControls } from '../../../mol-plugin/ui/state/animation'; import { CameraSnapshots } from '../../../mol-plugin/ui/camera'; +import { PluginContext } from '../../../mol-plugin/context'; +import { TransformUpdaterControl } from '../../../mol-plugin/ui/state/update-transform'; +import { StateElements } from '../helpers'; export class ControlsWrapper extends PluginUIComponent { render() { @@ -18,4 +22,11 @@ export class ControlsWrapper extends PluginUIComponent { <CameraSnapshots /> </div>; } +} + +export function volumeStreamingControls(plugin: PluginContext, parent: Element) { + ReactDOM.render(<PluginContextContainer plugin={plugin}> + <TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} /> + </PluginContextContainer>, + parent); } \ No newline at end of file diff --git a/src/mol-app/component/parameter/boolean.tsx b/src/mol-app/component/parameter/boolean.tsx index d5ec7529ed8a225b07904a37d7cbf54c64856b68..bb93409e4f24b135c5de0efe6db297d6e92bfdd7 100644 --- a/src/mol-app/component/parameter/boolean.tsx +++ b/src/mol-app/component/parameter/boolean.tsx @@ -9,7 +9,7 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition'; export interface BooleanParamComponentProps { label: string - param: PD.Boolean + param: PD.BooleanParam value: boolean onChange(v: boolean): void } diff --git a/src/mol-geo/geometry/overpaint-data.ts b/src/mol-geo/geometry/overpaint-data.ts index c256cc8dc9feae640093a59f1945af66c79634aa..b97e19a4abfcc6f8f09cb8c9df01cf58b8082781 100644 --- a/src/mol-geo/geometry/overpaint-data.ts +++ b/src/mol-geo/geometry/overpaint-data.ts @@ -24,7 +24,7 @@ export function applyOverpaintColor(array: Uint8Array, start: number, end: numbe } export function clearOverpaint(array: Uint8Array, start: number, end: number) { - array.fill(0, start, end) + array.fill(0, start * 4, end * 4) } export function createOverpaint(count: number, overpaintData?: OverpaintData): OverpaintData { diff --git a/src/mol-io/reader/cif/schema/mmcif-extras.ts b/src/mol-io/reader/cif/schema/mmcif-extras.ts index 10b5e32f74df25b17eaf76414e89f7cd9d98b74b..cb35f4879008bbffd1cc4fbd6c4f577aae29a53a 100644 --- a/src/mol-io/reader/cif/schema/mmcif-extras.ts +++ b/src/mol-io/reader/cif/schema/mmcif-extras.ts @@ -1,10 +1,12 @@ /** - * 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 David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ - import { mmCIF_Schema } from './mmcif'; +import { mmCIF_Schema } from './mmcif'; +import { Column } from '../../../../mol-data/db'; export const mmCIF_residueId_schema = { label_comp_id: mmCIF_Schema.atom_site.label_comp_id, @@ -15,4 +17,10 @@ export const mmCIF_residueId_schema = { auth_comp_id: mmCIF_Schema.atom_site.auth_atom_id, auth_seq_id: mmCIF_Schema.atom_site.auth_seq_id, auth_asym_id: mmCIF_Schema.atom_site.auth_asym_id +} + +export const mmCIF_chemCompBond_schema = { + ...mmCIF_Schema.chem_comp_bond, + /** Indicates if the bond entry was taken from the protonation variant dictionary */ + molstar_protonation_variant: Column.Schema.Str() } \ No newline at end of file diff --git a/src/mol-io/reader/mol2/parser.ts b/src/mol-io/reader/mol2/parser.ts index 1f727b61fc3982636874e200ac44297234ebf8b0..4e9429f31c8937bb4b4ffa0cafa5d87027aca480 100644 --- a/src/mol-io/reader/mol2/parser.ts +++ b/src/mol-io/reader/mol2/parser.ts @@ -84,7 +84,7 @@ function handleMolecule(state: State) { molecule.mol_comment = getTokenString(tokenizer) } -function isStatus_bit(aString: String): Boolean { +function isStatus_bit(aString: string): boolean { if (aString.includes('DSPMOD') || aString.includes('TYPECOL') || aString.includes('CAP') || aString.includes('BACKBONE') || aString.includes('DICT') || aString.includes('ESSENTIAL') || aString.includes('WATER') || aString.includes('DIRECT')) { 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/mol-model-props/wwpdb/chem-comp-bond.ts b/src/mol-model-props/wwpdb/chem-comp-bond.ts new file mode 100644 index 0000000000000000000000000000000000000000..2395d4832d341d32eb8aa32d44dfbbaab79aafc5 --- /dev/null +++ b/src/mol-model-props/wwpdb/chem-comp-bond.ts @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Column, Table } from '../../mol-data/db'; +import { toTable } from '../../mol-io/reader/cif/schema'; +import { Model, CustomPropertyDescriptor } from '../../mol-model/structure'; +import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras'; +import { CifWriter } from '../../mol-io/writer/cif'; + +export namespace ChemCompBond { + export type Property = Table<Schema['chem_comp_bond']> + + export function getFromModel(model: Model): Property { + if (model.sourceData.kind !== 'mmCIF') return Table.ofUndefinedColumns(Schema.chem_comp_bond, 0); + const { chem_comp_bond } = model.sourceData.data + return Table.ofColumns(Schema.chem_comp_bond, { + ...chem_comp_bond, + molstar_protonation_variant: Column.Undefined(chem_comp_bond._rowCount, Column.Schema.Str()) + }); + } + + export function get(model: Model): Property { + return model._staticPropertyData.__ChemCompBond__ || getFromModel(model); + } + function set(model: Model, prop: Property) { + (model._staticPropertyData.__ChemCompBond__ as Property) = prop; + } + + export const Schema = { chem_comp_bond: mmCIF_chemCompBond_schema }; + export type Schema = typeof Schema + + export const Descriptor = CustomPropertyDescriptor({ + isStatic: true, + name: 'chem_comp_bond', + cifExport: { + prefix: '', + context(ctx): Property { return get(ctx.firstModel); }, + categories: [{ + name: 'chem_comp_bond', + instance(ctx: Property) { + return CifWriter.Category.ofTable(ctx); + } + }] + } + }); + + function fromCifData(model: Model): Table<Schema['chem_comp_bond']> | undefined { + if (model.sourceData.kind !== 'mmCIF') return void 0; + const cat = model.sourceData.frame.categories.chem_comp_bond; + if (!cat) return void 0; + return toTable(Schema.chem_comp_bond, cat); + } + + export async function attachFromCifOrTable(model: Model, params: { + // optional Table source + wwPDB_apiSourceTable?: (model: Model) => Promise<Table<Schema['chem_comp_bond']>> + }) { + if (model.customProperties.has(Descriptor)) return true; + + let chemCompBond: Table<Schema['chem_comp_bond']> | undefined = fromCifData(model); + if (chemCompBond === void 0 && params.wwPDB_apiSourceTable) { + const data = await params.wwPDB_apiSourceTable(model); + if (!data) return false; + chemCompBond = chemCompBondFromTable(model, data); + } else { + return false; + } + + if (!chemCompBond) return false; + + model.customProperties.add(Descriptor); + set(model, chemCompBond); + return true; + } +} + +function chemCompBondFromTable(model: Model, table: Table<ChemCompBond.Schema['chem_comp_bond']>): Table<ChemCompBond.Schema['chem_comp_bond']> { + return Table.pick(table, ChemCompBond.Schema.chem_comp_bond, (i: number) => { + return model.properties.chemicalComponentMap.has(table.comp_id.value(i)) + }) +} \ No newline at end of file diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts index 50a524740a6dbcf4b59baac01b946519fedada9c..5388a1c473912fb8a279a8a5f125e9a35545087e 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts @@ -43,13 +43,13 @@ export namespace VolumeStreaming { valuesInfo: [{ mean: 0, min: -1, max: 1, sigma: 0.1 }, { mean: 0, min: -1, max: 1, sigma: 0.1 }] }; - export function createParams(data?: VolumeServerInfo.Data) { + export function createParams(data?: VolumeServerInfo.Data, defaultView?: ViewTypes) { // fake the info const info = data || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: VolumeIsoValue.relative(0) }; const box = (data && data.structure.boundary.box) || Box3D.empty(); return { - view: PD.MappedStatic(info.kind === 'em' ? 'cell' : 'selection-box', { + view: PD.MappedStatic(defaultView || (info.kind === 'em' ? 'cell' : 'selection-box'), { 'box': PD.Group({ bottomLeft: PD.Vec3(box.min), topRight: PD.Vec3(box.max), @@ -76,6 +76,8 @@ export namespace VolumeStreaming { }; } + export type ViewTypes = 'box' | 'selection-box' | 'cell' + export type ParamDefinition = typeof createParams extends (...args: any[]) => (infer T) ? T : never export type Params = ParamDefinition extends PD.Params ? PD.Values<ParamDefinition> : {} diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts index 97b25f4ebc995eb8e4bea2c4aa0459019211c272..bc42398ae2ff2cc4e2137ba8442e4576b40102dc 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts @@ -26,10 +26,13 @@ export const InitVolumeStreaming = StateAction.build({ display: { name: 'Volume Streaming' }, from: SO.Molecule.Structure, params(a) { + const method = getStreamingMethod(a && a.data); return { - method: PD.Select<VolumeServerInfo.Kind>(getStreamingMethod(a && a.data), [['em', 'EM'], ['x-ray', 'X-Ray']]), + method: PD.Select<VolumeServerInfo.Kind>(method, [['em', 'EM'], ['x-ray', 'X-Ray']]), id: PD.Text((a && a.data.models.length > 0 && a.data.models[0].label) || ''), - serverUrl: PD.Text('https://ds.litemol.org') + serverUrl: PD.Text('https://ds.litemol.org'), + defaultView: PD.Text<VolumeStreaming.ViewTypes>(method === 'em' ? 'cell' : 'selection-box'), + behaviorRef: PD.Text('', { isHidden: true }) }; }, isApplicable: (a) => a.data.models.length === 1 @@ -56,7 +59,8 @@ export const InitVolumeStreaming = StateAction.build({ const infoObj = await state.updateTree(infoTree).runInContext(taskCtx); const behTree = state.build().to(infoTree.ref).apply(CreateVolumeStreamingBehavior, - PD.getDefaultValues(VolumeStreaming.createParams(infoObj.data))); + PD.getDefaultValues(VolumeStreaming.createParams(infoObj.data, params.defaultView)), + { ref: params.behaviorRef ? params.behaviorRef : void 0 }); if (params.method === 'em') { behTree.apply(VolumeStreamingVisual, { channel: 'em' }, { state: { isGhost: true } }); diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx index 19323297adbb3af4c45b2fa741d8851a590750fc..84f9b0742dbd920a11b3f12a6115d9548ffe23d5 100644 --- a/src/mol-plugin/ui/controls/parameters.tsx +++ b/src/mol-plugin/ui/controls/parameters.tsx @@ -15,7 +15,7 @@ import { camelCaseToWords } from '../../../mol-util/string'; import * as React from 'react'; import LineGraphComponent from './line-graph/line-graph-component'; import { Slider, Slider2 } from './slider'; -import { NumericInput, IconButton } from './common'; +import { NumericInput, IconButton, ControlGroup } from './common'; import { _Props, _State } from '../base'; export interface ParameterControlsProps<P extends PD.Params = PD.Params> { @@ -97,7 +97,7 @@ export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent< } } -export class BoolControl extends SimpleParam<PD.Boolean> { +export class BoolControl extends SimpleParam<PD.BooleanParam> { onClick = (e: React.MouseEvent<HTMLButtonElement>) => { this.update(!this.props.value); e.currentTarget.blur(); } renderControl() { return <button onClick={this.onClick} disabled={this.props.isDisabled}> @@ -461,8 +461,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>, </div> {this.state.isExpanded && <div className='msp-control-offset' style={{ display: this.state.isExpanded ? 'block' : 'none' }}> {controls} - </div> - } + </div>} </div> } } @@ -655,9 +654,12 @@ export class ObjectListControl extends React.PureComponent<ParamProps<PD.ObjectL <button onClick={this.toggleExpanded}>{value}</button> </div> </div> + {this.state.isExpanded && <div className='msp-control-offset'> {this.props.value.map((v, i) => <ObjectListItem key={i} param={this.props.param} value={v} index={i} actions={this.actions} />)} - <ObjectListEditor params={this.props.param.element} apply={this.add} value={this.props.param.ctor()} isDisabled={this.props.isDisabled} /> + <ControlGroup header='New Item'> + <ObjectListEditor params={this.props.param.element} apply={this.add} value={this.props.param.ctor()} isDisabled={this.props.isDisabled} /> + </ControlGroup> </div>} </>; } diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx index dad27abdbb59757cf1e9fc0a89f24da62b426538..d911bd492272b30235fb3328bf2a34e9c2d5811e 100644 --- a/src/mol-plugin/ui/plugin.tsx +++ b/src/mol-plugin/ui/plugin.tsx @@ -37,6 +37,16 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> { } } +export class PluginContextContainer extends React.Component<{ plugin: PluginContext }> { + render() { + return <PluginReactContext.Provider value={this.props.plugin}> + <div className='msp-plugin'> + {this.props.children} + </div> + </PluginReactContext.Provider>; + } +} + class Layout extends PluginUIComponent { componentDidMount() { this.subscribe(this.plugin.layout.events.updated, () => this.forceUpdate()); diff --git a/src/mol-plugin/ui/state/update-transform.tsx b/src/mol-plugin/ui/state/update-transform.tsx index 1e6cd3ba90a9514e94cff73e318b036bad9b6ade..7aafc25eaeffdb5b0feef2a746537fbadf263f15 100644 --- a/src/mol-plugin/ui/state/update-transform.tsx +++ b/src/mol-plugin/ui/state/update-transform.tsx @@ -101,7 +101,6 @@ class TransformUpdaterControl extends PluginUIComponent<{ nodeRef: string, initi if (!cell || (cell.status !== 'ok' && cell.status !== 'error')) return null; const transform = cell.transform; - return <UpdateTransformContol state={state} transform={transform} initiallyCollapsed={this.props.initiallyCollapsed} customHeader={this.props.header} />; } } \ No newline at end of file diff --git a/src/mol-util/console-logger.ts b/src/mol-util/console-logger.ts index c09f08b9aaa412e2f8a9e155fdbb3c01cfe7c8bc..8d04c03fb835ef96ee3bd36d85e8ede19ff35b8c 100644 --- a/src/mol-util/console-logger.ts +++ b/src/mol-util/console-logger.ts @@ -25,7 +25,7 @@ export namespace ConsoleLogger { console.log(`[${tag}] ${msg}`); } - export function logId(guid: string | String, tag: string, msg: string) { + export function logId(guid: string, tag: string, msg: string) { console.log(`[${guid}][${tag}] ${msg}`); } @@ -38,7 +38,7 @@ export namespace ConsoleLogger { console.error(`[Warn] (${ctx}) ${e}`); } - export function errorId(guid: string | String, e: any) { + export function errorId(guid: string, e: any) { console.error(`[${guid}][Error] ${e}`); if (e.stack) console.error(e.stack); } diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index 92496fc63e303412c2ebbaada2a99df030b2cb8a..d94f32864c051da74ed8261c21b74f34cbe65ceb 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -76,11 +76,11 @@ export namespace ParamDefinition { return setInfo<MultiSelect<E, T>>({ type: 'multi-select', defaultValue, options }, info) } - export interface Boolean extends Base<boolean> { + export interface BooleanParam extends Base<boolean> { type: 'boolean' } - export function Boolean(defaultValue: boolean, info?: Info): Boolean { - return setInfo<Boolean>({ type: 'boolean', defaultValue }, info) + export function Boolean(defaultValue: boolean, info?: Info): BooleanParam { + return setInfo<BooleanParam>({ type: 'boolean', defaultValue }, info) } export interface Text<T extends string = string> extends Base<T> { @@ -237,7 +237,7 @@ export namespace ParamDefinition { } export type Any = - | Value<any> | Select<any> | MultiSelect<any> | Boolean | Text | Color | Vec3 | Numeric | FileParam | Interval | LineGraph + | Value<any> | Select<any> | MultiSelect<any> | BooleanParam | Text | Color | Vec3 | Numeric | FileParam | Interval | LineGraph | ColorScale<any> | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | ScriptExpression | ObjectList export type Params = { [k: string]: Any } 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/tslint.json b/tslint.json index 1c88592ea6631a73315ede0d282296f9626b68c6..ecf0639ee5c4ae7ba5c27ed412efe60d3867995c 100644 --- a/tslint.json +++ b/tslint.json @@ -6,6 +6,7 @@ ], "arrow-parens": false, "no-var-keyword": true, + "no-construct": true, "ordered-imports": [false], "trailing-comma": [false], "class-name": false, @@ -17,6 +18,12 @@ true, "spaces" ], + "ban-types": [ + true, + ["String", "Use primitive 'string' instead."], + ["Boolean", "Use primitive 'boolean' instead."], + ["Number", "Use primitive 'number' instead."] + ], "no-eval": true, "no-internal-module": true, "no-trailing-whitespace": true, diff --git a/webpack.config.js b/webpack.config.js index 3de9e1565111c706a5f326ff84f7f9f143cc4e28..9c4d98c40544b636d4b16b027b416707bbaff62d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -8,16 +8,19 @@ const sharedConfig = { module: { rules: [ { - loader: 'file-loader', test: /\.(woff2?|ttf|otf|eot|svg|html)$/, - include: [path.resolve(__dirname, 'lib/')], - options: { - name: '[name].[ext]' - } + include: [path.resolve(__dirname, 'build/src/')], + use: [{ + loader: 'file-loader', + options: { name: '[name].[ext]' } + }] }, { test: /\.(s*)css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader', 'resolve-url-loader', 'sass-loader'] + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', 'resolve-url-loader', 'sass-loader' + ] } ] }, @@ -73,6 +76,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 } }