ソースを参照

ModelServer: ability to query specific model number; added simple query builder app

David Sehnal 6 年 前
コミット
a192d3177a

+ 2 - 0
package.json

@@ -21,6 +21,8 @@
     "watch-viewer": "webpack build/node_modules/apps/viewer/index.js -w --mode development -o build/viewer/index.js",
     "build-canvas": "webpack build/node_modules/apps/canvas/index.js --mode development -o build/canvas/index.js",
     "watch-canvas": "webpack build/node_modules/apps/canvas/index.js -w --mode development -o build/canvas/index.js",
+    "build-ms-query": "webpack build/node_modules/apps/model-server-query/index.js --mode development -o build/model-server-query/index.js",
+    "watch-ms-query": "webpack build/node_modules/apps/model-server-query/index.js -w --mode development -o build/model-server-query/index.js",
     "model-server": "node build/node_modules/servers/model/server.js",
     "model-server-watch": "nodemon --watch build/node_modules build/node_modules/servers/model/server.js"
   },

+ 12 - 0
src/apps/model-server-query/index.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8" />
+        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+        <title>Mol* ModelServer Query Builder</title>
+    </head>
+    <body>
+        <div id="app"></div>
+        <script type="text/javascript" src="./index.js"></script>
+    </body>
+</html>

+ 113 - 0
src/apps/model-server-query/index.tsx

@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import * as React from 'react'
+import * as ReactDOM from 'react-dom'
+import * as Rx from 'rxjs'
+
+import { QueryDefinition, QueryList } from '../../servers/model/server/api'
+
+import './index.html'
+
+interface State {
+    query: Rx.BehaviorSubject<QueryDefinition>,
+    id: Rx.BehaviorSubject<string>,
+    params: Rx.BehaviorSubject<any>,
+    isBinary: Rx.BehaviorSubject<boolean>,
+    models: Rx.BehaviorSubject<number[]>,
+    url: Rx.Subject<string>
+}
+
+class Root extends React.Component<{ state: State }, {  }> {
+    parseParams(str: string) {
+        try {
+            const params = JSON.parse(str);
+            this.props.state.params.next(params);
+        } catch {
+            this.props.state.params.next({});
+        }
+    }
+
+    render() {
+        return <div>
+            <div>
+                Query: <QuerySelect state={this.props.state} />
+            </div>
+            <div>
+                ID: <input type='text' onChange={t => this.props.state.id.next(t.currentTarget.value)} />
+            </div>
+            <div>
+                Params:<br/>
+                <textarea style={{height: '300px'}} cols={80} onChange={t => this.parseParams(t.currentTarget.value)} />
+            </div>
+            <div>
+                Model numbers (empty for all): <ModelNums state={this.props.state} />
+            </div>
+            <div>
+                <input type='checkbox' onChange={t => this.props.state.isBinary.next(!!t.currentTarget.checked)} /> Binary
+            </div>
+            <div>
+                Query string:
+                <QueryUrl state={this.props.state} />
+            </div>
+        </div>
+    }
+}
+
+class QuerySelect extends React.Component<{ state: State }> {
+    render() {
+        return <select onChange={s => this.props.state.query.next(QueryList[+s.currentTarget.value].definition)}>
+            { QueryList.map((q, i) => <option value={i} key={i} selected={i === 1}>{q.definition.niceName}</option>) }
+        </select>
+    }
+}
+
+class QueryUrl extends React.Component<{ state: State }, { queryString: string }> {
+    state = { queryString: '' };
+
+    componentDidMount() {
+        this.props.state.url.subscribe(url => this.setState({ queryString: url }))
+    }
+
+    render() {
+        return <input type='text' value={this.state.queryString} style={{ width: '800px' }} />
+    }
+}
+
+class ModelNums extends React.Component<{ state: State }> {
+    render() {
+        return <input type='text' defaultValue='1' style={{ width: '300px' }} onChange={t =>
+            this.props.state.models.next(t.currentTarget.value.split(',')
+                .map(v => v.trim())
+                .filter(v => !!v)
+                .map(v => +v)
+                )} />
+    }
+}
+
+const state: State = {
+    query: new Rx.BehaviorSubject(QueryList[1].definition),
+    id: new Rx.BehaviorSubject('1cbs'),
+    params: new Rx.BehaviorSubject({ }),
+    isBinary: new Rx.BehaviorSubject(false),
+    models: new Rx.BehaviorSubject<number[]>([]),
+    url: new Rx.Subject()
+}
+
+function formatUrl() {
+    const json = JSON.stringify({
+        name: state.query.value.name,
+        id: state.id.value,
+        modelNums: state.models.value.length ? state.models.value : void 0,
+        binary: state.isBinary.value,
+        params: state.params.value
+    });
+    state.url.next(encodeURIComponent(json));
+}
+
+Rx.merge(state.query, state.id, state.params, state.isBinary, state.models).subscribe(s => formatUrl());
+
+ReactDOM.render(<Root state={state} />, document.getElementById('app'));

+ 2 - 1
src/servers/model/server/api-local.ts

@@ -17,6 +17,7 @@ export type LocalInput = {
     input: string,
     output: string,
     query: string,
+    modelNums?: number[],
     params?: any
 }[];
 
@@ -27,7 +28,7 @@ export async function runLocal(input: LocalInput) {
     }
 
     for (const job of input) {
-        JobManager.add('_local_', job.input, job.query, job.params || { }, job.output);
+        JobManager.add('_local_', job.input, job.query, job.params || { }, job.modelNums, job.output);
     }
     JobManager.sort();
 

+ 1 - 1
src/servers/model/server/api-web.ts

@@ -103,7 +103,7 @@ export function initWebApi(app: express.Express) {
         const name = args.name;
         const entryId = args.id;
         const params = args.params || { };
-        const jobId = JobManager.add('pdb', entryId, name, params);
+        const jobId = JobManager.add('pdb', entryId, name, params, args.modelNums);
         responseMap.set(jobId, res);
         if (JobManager.size === 1) processNextJob();
     });

+ 1 - 1
src/servers/model/server/api.ts

@@ -88,7 +88,7 @@ const QueryMap: { [id: string]: Partial<QueryDefinition> } = {
         description: 'Computes structural assembly.',
         query: () => Queries.generators.all,
         structureTransform(p, s) {
-            return StructureSymmetry.buildAssembly(s, '' + p.name).run();
+            return StructureSymmetry.buildAssembly(s, '' + (p.name || '1')).run();
         },
         params: [{
             name: 'name',

+ 5 - 3
src/servers/model/server/jobs.ts

@@ -23,11 +23,12 @@ export interface Job {
     queryDefinition: QueryDefinition,
     normalizedParams: any,
     responseFormat: ResponseFormat,
+    modelNums?: number[],
 
     outputFilename?: string
 }
 
-export function createJob(sourceId: '_local_' | string, entryId: string, queryName: string, params: any, outputFilename?: string): Job {
+export function createJob(sourceId: '_local_' | string, entryId: string, queryName: string, params: any, modelNums?: number[], outputFilename?: string): Job {
     const queryDefinition = getQueryByName(queryName);
     if (!queryDefinition) throw new Error(`Query '${queryName}' is not supported.`);
 
@@ -42,6 +43,7 @@ export function createJob(sourceId: '_local_' | string, entryId: string, queryNa
         queryDefinition,
         normalizedParams,
         responseFormat: { isBinary: !!params.binary },
+        modelNums,
         outputFilename
     };
 }
@@ -53,8 +55,8 @@ class _JobQueue {
         return this.list.count;
     }
 
-    add(sourceId: '_local_' | string, entryId: string, queryName: string, params: any, outputFilename?: string) {
-        const job = createJob(sourceId, entryId, queryName, params, outputFilename);
+    add(sourceId: '_local_' | string, entryId: string, queryName: string, params: any, modelNums?: number[], outputFilename?: string) {
+        const job = createJob(sourceId, entryId, queryName, params, modelNums, outputFilename);
         this.list.addLast(job);
         return job.id;
     }

+ 1 - 1
src/servers/model/server/query.ts

@@ -40,7 +40,7 @@ export async function resolveJob(job: Job): Promise<CifWriter.Encoder<any>> {
 
     try {
         perf.start('query');
-        const sourceStructures = await resolveStructures(wrappedStructure);
+        const sourceStructures = await resolveStructures(wrappedStructure, job.modelNums);
         if (!sourceStructures.length) throw new Error('Model not available');
 
         let structures: Structure[] = sourceStructures;