diff --git a/package.json b/package.json index da84eb4c19e0d296aeb11f79c12371889898cfe8..c32a46c131903f5c651d7e401b58fceccac469f5 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test": "npm run lint && jest", "build": "npm run build-tsc && npm run build-extra && npm run build-webpack", "build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer", - "build-tsc": "tsc --incremental && tsc --build src/servers --incremental", + "build-tsc": "tsc --incremental && tsc --build tsconfig.servers.json --incremental", "build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/", "build-webpack": "webpack --mode production --config ./webpack.config.production.js", "build-webpack-viewer": "webpack --mode production --config ./webpack.config.viewer.js", @@ -24,7 +24,7 @@ "watch-viewer": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer\"", "watch-viewer-debug": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer-debug\"", "watch-tsc": "tsc --watch --incremental", - "watch-servers": "tsc --build src/servers --watch --incremental", + "watch-servers": "tsc --build tsconfig.servers.json --watch --incremental", "watch-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/ --watch", "watch-webpack": "webpack -w --mode development --display minimal", "watch-webpack-viewer": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.js", diff --git a/src/mol-io/writer/cif/encoder.ts b/src/mol-io/writer/cif/encoder.ts index 02005fd2b90ba61bad0ebbc61bd560dcd1f1b1a9..95ffece477692481de4936337d36dd16ba391071 100644 --- a/src/mol-io/writer/cif/encoder.ts +++ b/src/mol-io/writer/cif/encoder.ts @@ -108,6 +108,11 @@ export namespace Field { return this; } + add(field: Field<K, D>) { + this.fields.push(field); + return this; + } + getFields() { return this.fields; } } @@ -222,6 +227,8 @@ export namespace Category { } export interface Encoder<T = string | Uint8Array> extends EncoderBase { + readonly isBinary: boolean, + setFilter(filter?: Category.Filter): void, isCategoryIncluded(name: string): boolean, setFormatter(formatter?: Category.Formatter): void, diff --git a/src/mol-io/writer/cif/encoder/binary.ts b/src/mol-io/writer/cif/encoder/binary.ts index 7bafaca3d0ad81fb69b25f6abbd9eed8ac032e74..df5e19b9c0463758b08c9a6b0c2a97c9d8f7cbaa 100644 --- a/src/mol-io/writer/cif/encoder/binary.ts +++ b/src/mol-io/writer/cif/encoder/binary.ts @@ -28,6 +28,8 @@ export default class BinaryEncoder implements Encoder<Uint8Array> { private filter: Category.Filter = Category.DefaultFilter; private formatter: Category.Formatter = Category.DefaultFormatter; + readonly isBinary = true; + binaryEncodingProvider: BinaryEncodingProvider | undefined = void 0; setFilter(filter?: Category.Filter) { diff --git a/src/mol-io/writer/cif/encoder/text.ts b/src/mol-io/writer/cif/encoder/text.ts index bfb15d7e21b6314ea9104ae6030468004eb5435f..94049da039901960a2e6e04a37ef0b1a964b3a9f 100644 --- a/src/mol-io/writer/cif/encoder/text.ts +++ b/src/mol-io/writer/cif/encoder/text.ts @@ -19,6 +19,8 @@ export default class TextEncoder implements Encoder<string> { private filter: Category.Filter = Category.DefaultFilter; private formatter: Category.Formatter = Category.DefaultFormatter; + readonly isBinary = false; + binaryEncodingProvider = void 0; setFilter(filter?: Category.Filter) { diff --git a/src/mol-model/structure/export/categories/atom_site_operator_mapping.ts b/src/mol-model/structure/export/categories/atom_site_operator_mapping.ts index c76615de78285c00c2db84ff0ee1ea847f5452b3..ae09e71c0a493b45c70a0913dadc1438c5be8277 100644 --- a/src/mol-model/structure/export/categories/atom_site_operator_mapping.ts +++ b/src/mol-model/structure/export/categories/atom_site_operator_mapping.ts @@ -6,18 +6,21 @@ import { SymmetryOperator } from '../../../../mol-math/geometry'; import { CifExportContext } from '../mmcif'; -import { StructureElement, StructureProperties as P } from '../../structure'; +import { StructureElement, StructureProperties as P, CifExportCategoryInfo } from '../../structure'; import Unit from '../../structure/unit'; import { Segmentation } from '../../../../mol-data/int'; import { CifWriter } from '../../../../mol-io/writer/cif'; import { Column } from '../../../../mol-data/db'; -export function atom_site_operator_mapping(encoder: CifWriter.Encoder, ctx: CifExportContext) { + +export function atom_site_operator_mapping(ctx: CifExportContext): CifExportCategoryInfo | undefined { const entries = getEntries(ctx); if (entries.length === 0) return; - encoder.writeCategory(Category, entries, { ignoreFilter: true }); + return [Category, entries, { ignoreFilter: true }]; } +export const AtomSiteOperatorMappingCategoryName = 'molstar_atom_site_operator_mapping'; + export const AtomSiteOperatorMappingSchema = { molstar_atom_site_operator_mapping: { label_asym_id: Column.Schema.Str(), diff --git a/src/mol-model/structure/export/categories/utils.ts b/src/mol-model/structure/export/categories/utils.ts index 4f500efbf56d9dfe93322614ca74b4b1d1958e47..4914c55046862c591099a844cde401b1893600af 100644 --- a/src/mol-model/structure/export/categories/utils.ts +++ b/src/mol-model/structure/export/categories/utils.ts @@ -14,6 +14,7 @@ import { sortArray } from '../../../../mol-data/util'; import { CifWriter } from '../../../../mol-io/writer/cif'; import { CifExportContext } from '../mmcif'; import { MmcifFormat } from '../../../../mol-model-formats/structure/mmcif'; +import { CifCategory, CifField, getCifFieldType } from '../../../../mol-io/reader/cif'; export function getModelMmCifCategory<K extends keyof mmCIF_Schema>(model: Model, name: K): mmCIF_Database[K] | undefined { if (!MmcifFormat.is(model.sourceData)) return; @@ -58,4 +59,41 @@ export function copy_mmCif_category(name: keyof mmCIF_Schema, condition?: (struc return CifWriter.Category.ofTable(table); } }; +} + +export function copy_source_mmCifCategory(encoder: CifWriter.Encoder, ctx: CifExportContext, category: CifCategory): CifWriter.Category<CifExportContext> | undefined { + if (!MmcifFormat.is(ctx.firstModel.sourceData)) return; + + const fs = CifWriter.fields<number, undefined>(); + if (encoder.isBinary) { + for (const f of category.fieldNames) { + // TODO: this could be optimized + const field = classifyField(f, category.getField(f)!); + fs.add(field); + } + } else { + for (const f of category.fieldNames) { + const field = category.getField(f)!; + fs.str(f, row => field.str(row)); + } + } + + const fields = fs.getFields(); + return { + name: category.name, + instance() { + return { fields, source: [{ data: void 0, rowCount: category.rowCount }] }; + } + }; +} + +function classifyField(name: string, field: CifField): CifWriter.Field { + const type = getCifFieldType(field); + if (type['@type'] === 'str') { + return { name, type: CifWriter.Field.Type.Str, value: field.str, valueKind: field.valueKind }; + } else if (type['@type'] === 'float') { + return CifWriter.Field.float(name, field.float, { valueKind: field.valueKind, typedArray: Float64Array }); + } else { + return CifWriter.Field.int(name, field.int, { valueKind: field.valueKind, typedArray: Int32Array }); + } } \ No newline at end of file diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index c6368925dfc083010e5e77665404c76db48e3668..725a1923979f2d40f2303d3adc298fbd4fd9243e 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -13,10 +13,11 @@ import CifCategory = CifWriter.Category import { _struct_conf, _struct_sheet_range } from './categories/secondary-structure'; import { _chem_comp, _pdbx_chem_comp_identifier, _pdbx_nonpoly_scheme } from './categories/misc'; import { Model } from '../model'; -import { getUniqueEntityIndicesFromStructures, copy_mmCif_category } from './categories/utils'; +import { getUniqueEntityIndicesFromStructures, copy_mmCif_category, copy_source_mmCifCategory } from './categories/utils'; import { _struct_asym, _entity_poly, _entity_poly_seq } from './categories/sequence'; import { CustomPropertyDescriptor } from '../common/custom-property'; import { atom_site_operator_mapping } from './categories/atom_site_operator_mapping'; +import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif'; export interface CifExportContext { structures: Structure[], @@ -24,6 +25,10 @@ export interface CifExportContext { cache: any } +export type CifExportCategoryInfo = + | [CifWriter.Category, any /** context */, CifWriter.Encoder.WriteCategoryOptions] + | [CifWriter.Category, any /** context */] + export namespace CifExportContext { export function create(structures: Structure | Structure[]): CifExportContext { const structureArray = Array.isArray(structures) ? structures : [structures]; @@ -99,8 +104,8 @@ export const mmCIF_Export_Filters = { } }; -function encodeCustomProp(customProp: CustomPropertyDescriptor, ctx: CifExportContext, encoder: CifWriter.Encoder, params: encode_mmCIF_categories_Params) { - if (!customProp.cifExport || customProp.cifExport.categories.length === 0) return; +function getCustomPropCategories(customProp: CustomPropertyDescriptor, ctx: CifExportContext, params?: encode_mmCIF_categories_Params): CifExportCategoryInfo[] { + if (!customProp.cifExport || customProp.cifExport.categories.length === 0) return []; const prefix = customProp.cifExport.prefix; const cats = customProp.cifExport.categories; @@ -114,14 +119,21 @@ function encodeCustomProp(customProp: CustomPropertyDescriptor, ctx: CifExportCo ctx.cache[propId + '__ctx'] = propCtx; } } + + const ret: CifExportCategoryInfo[] = []; for (const cat of cats) { - if (params.skipCategoryNames && params.skipCategoryNames.has(cat.name)) continue; + if (params?.skipCategoryNames?.has(cat.name)) continue; if (cat.name.indexOf(prefix) !== 0) throw new Error(`Custom category '${cat.name}' name must start with prefix '${prefix}.'`); - encoder.writeCategory(cat, propCtx); + ret.push([cat, propCtx]); } + return ret; } -type encode_mmCIF_categories_Params = { skipCategoryNames?: Set<string>, exportCtx?: CifExportContext } +type encode_mmCIF_categories_Params = { + skipCategoryNames?: Set<string>, + exportCtx?: CifExportContext, + copyAllCategories?: boolean +} /** Doesn't start a data block */ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures: Structure | Structure[], params?: encode_mmCIF_categories_Params) { @@ -129,30 +141,95 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures: const models = first.models; if (models.length !== 1) throw 'Can\'t export stucture composed from multiple models.'; - const _params = params || { }; - const ctx: CifExportContext = params && params.exportCtx ? params.exportCtx : CifExportContext.create(structures); + const ctx: CifExportContext = params?.exportCtx || CifExportContext.create(structures); + if (params?.copyAllCategories && MmcifFormat.is(models[0].sourceData)) { + encode_mmCIF_categories_copyAll(encoder, ctx); + } else { + console.log('default'); + encode_mmCIF_categories_default(encoder, ctx, params); + } +} + +function encode_mmCIF_categories_default(encoder: CifWriter.Encoder, ctx: CifExportContext, params?: encode_mmCIF_categories_Params) { for (const cat of Categories) { - if (_params.skipCategoryNames && _params.skipCategoryNames.has(cat.name)) continue; + if (params?.skipCategoryNames && params?.skipCategoryNames.has(cat.name)) continue; encoder.writeCategory(cat, ctx); } - if ((!_params.skipCategoryNames || !_params.skipCategoryNames.has('atom_site')) && encoder.isCategoryIncluded('atom_site')) { - atom_site_operator_mapping(encoder, ctx); + if (!params?.skipCategoryNames?.has('atom_site') && encoder.isCategoryIncluded('atom_site')) { + const info = atom_site_operator_mapping(ctx); + if (info) encoder.writeCategory(info[0], info[1], info[2]); } - for (const customProp of models[0].customProperties.all) { - encodeCustomProp(customProp, ctx, encoder, _params); + const _params = params || { }; + for (const customProp of ctx.firstModel.customProperties.all) { + for (const [cat, propCtx] of getCustomPropCategories(customProp, ctx, _params)) { + encoder.writeCategory(cat, propCtx); + } } - const structureCustomProps = new Set<CustomPropertyDescriptor>(); for (const s of ctx.structures) { if (!s.hasCustomProperties) continue; - for (const p of s.customPropertyDescriptors.all) structureCustomProps.add(p); + for (const customProp of s.customPropertyDescriptors.all) { + for (const [cat, propCtx] of getCustomPropCategories(customProp, ctx, _params)) { + encoder.writeCategory(cat, propCtx); + } + } } - structureCustomProps.forEach(customProp => encodeCustomProp(customProp, ctx, encoder, _params)); } +function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExportContext) { + const providedCategories = new Map<string, CifExportCategoryInfo>(); + + for (const cat of Categories) { + providedCategories.set(cat.name, [cat, ctx]); + } + + const mapping = atom_site_operator_mapping(ctx); + if (mapping) providedCategories.set(mapping[0].name, mapping); + + for (const customProp of ctx.firstModel.customProperties.all) { + for (const info of getCustomPropCategories(customProp, ctx)) { + providedCategories.set(info[0].name, info); + } + } + + for (const s of ctx.structures) { + if (!s.hasCustomProperties) continue; + for (const customProp of s.customPropertyDescriptors.all) { + for (const info of getCustomPropCategories(customProp, ctx)) { + providedCategories.set(info[0].name, info); + } + } + } + + const handled = new Set<string>(); + + const data = (ctx.firstModel.sourceData as MmcifFormat).data; + for (const catName of data.frame.categoryNames) { + handled.add(catName); + + if (providedCategories.has(catName)) { + const info = providedCategories.get(catName)!; + encoder.writeCategory(info[0], info[1], info[2]); + } else { + if ((data.db as any)[catName]) { + const cat = copy_mmCif_category(catName as any); + encoder.writeCategory(cat, ctx); + } else { + const cat = copy_source_mmCifCategory(encoder, ctx, data.frame.categories[catName]); + if (cat) encoder.writeCategory(cat); + } + } + } + + providedCategories.forEach((info, name) => { + if (!handled.has(name)) encoder.writeCategory(info[0], info[1], info[2]); + }); +} + + function to_mmCIF(name: string, structure: Structure, asBinary = false) { const enc = CifWriter.createEncoder({ binary: asBinary }); enc.startDataBlock(name); diff --git a/src/servers/common/swagger-ui/index.ts b/src/servers/common/swagger-ui/index.ts index 1c0348a7c61a47bf8d4db60546f0af4b16ff4cbb..20366f824f6c92c6017c06200317656d2fe65d76 100644 --- a/src/servers/common/swagger-ui/index.ts +++ b/src/servers/common/swagger-ui/index.ts @@ -5,11 +5,11 @@ */ import * as express from 'express'; -import * as fs from 'fs'; import { getAbsoluteFSPath } from 'swagger-ui-dist'; import { ServeStaticOptions } from 'serve-static'; import { interpolate } from '../../../mol-util/string'; import { Handler } from 'express-serve-static-core'; +import IndexTemplate from './indexTemplate'; export function swaggerUiAssetsHandler(options?: ServeStaticOptions): Handler { const opts = options || {}; @@ -25,8 +25,7 @@ export interface SwaggerUIOptions { } function createHTML(options: SwaggerUIOptions) { - const htmlTemplate = fs.readFileSync(`${__dirname}/indexTemplate.html`).toString(); - return interpolate(htmlTemplate, options); + return interpolate(IndexTemplate, options); } export function swaggerUiIndexHandler(options: SwaggerUIOptions): express.Handler { diff --git a/src/servers/common/swagger-ui/indexTemplate.html b/src/servers/common/swagger-ui/indexTemplate.ts similarity index 80% rename from src/servers/common/swagger-ui/indexTemplate.html rename to src/servers/common/swagger-ui/indexTemplate.ts index 92869e8c5713a1f663fccefd9484562096cae5b1..5c6a1d3b0cb7d5f2e79d730a1a9aac77ca01f505 100644 --- a/src/servers/common/swagger-ui/indexTemplate.html +++ b/src/servers/common/swagger-ui/indexTemplate.ts @@ -1,10 +1,10 @@ -<!DOCTYPE html> +export default `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> - <title>${title}</title> - <link rel="stylesheet" type="text/css" href="${apiPrefix}/swagger-ui.css" > - ${shortcutIconLink} + <title>\${title}</title> + <link rel="stylesheet" type="text/css" href="\${apiPrefix}/swagger-ui.css" > + \${shortcutIconLink} <style> html @@ -30,8 +30,8 @@ <body> <div id="swagger-ui"></div> - <script src="${apiPrefix}/swagger-ui-bundle.js"> </script> - <script src="${apiPrefix}/swagger-ui-standalone-preset.js"> </script> + <script src="\${apiPrefix}/swagger-ui-bundle.js"> </script> + <script src="\${apiPrefix}/swagger-ui-standalone-preset.js"> </script> <script> function HidePlugin() { // this plugin overrides some components to return nothing @@ -44,7 +44,7 @@ } window.onload = function () { var ui = SwaggerUIBundle({ - url: '${openapiJsonUrl}', + url: '\${openapiJsonUrl}', validatorUrl: null, docExpansion: 'list', dom_id: '#swagger-ui', @@ -63,4 +63,4 @@ } </script> </body> -</html> \ No newline at end of file +</html>`; \ No newline at end of file diff --git a/src/servers/model/server/api-local.ts b/src/servers/model/server/api-local.ts index c94230dd4a41c22c266515cb24d87f1c6f032610..9235932a2440e54257e7a3a30997eab1d7616e62 100644 --- a/src/servers/model/server/api-local.ts +++ b/src/servers/model/server/api-local.ts @@ -13,11 +13,11 @@ import { Job, JobEntry, JobManager } from './jobs'; import { resolveJob } from './query'; import { StructureCache } from './structure-wrapper'; - export type Entry<Q extends QueryName = QueryName> = { input: string, query: Q, modelNums?: number[], + copyAllCategories?: boolean, params?: QueryParams<Q>, } @@ -43,6 +43,7 @@ export async function runLocal(input: LocalInput) { queryName: q.query, queryParams: q.params || { }, modelNums: q.modelNums, + copyAllCategories: !!q.copyAllCategories })), writer: job.asTarGz ? new TarballFileResultWriter(job.output, job.gzipLevel) diff --git a/src/servers/model/server/api-schema.ts b/src/servers/model/server/api-schema.ts index e2071c4d7c5262afcafcb1d9ca340d58f272ddc0..34b532020f9b1e5b6a312d8e14102454f740698c 100644 --- a/src/servers/model/server/api-schema.ts +++ b/src/servers/model/server/api-schema.ts @@ -52,7 +52,7 @@ function getPaths() { const queryManyExample: MultipleQuerySpec = { queries: [ { entryId: '1cbs', query: 'residueInteraction', params: { atom_site: [{ label_comp_id: 'REA' }], radius: 5 } }, - { entryId: '1tqn', query: 'full' } + { entryId: '1tqn', query: 'full', copy_all_categories: true } ], encoding: 'cif', asTarGz: false @@ -174,7 +174,12 @@ function getParamInfo(info: QueryParamInfo) { description: info.description, required: !!info.required, schema: { - type: info.type === QueryParamType.String ? 'string' : info.type === QueryParamType.Integer ? 'integer' : 'number', + type: info.type === QueryParamType.String + ? 'string' : info.type === QueryParamType.Integer + ? 'integer' + : info.type === QueryParamType.Boolean + ? 'boolean' + : 'number', enum: info.supportedValues ? info.supportedValues : void 0, default: info.defaultValue }, diff --git a/src/servers/model/server/api-web-multiple.ts b/src/servers/model/server/api-web-multiple.ts index f073af8a1477ca3da41a17bbbe213ac763efd40e..c6ad015f3589dcb0d88e502cd47eaadec3cd08db 100644 --- a/src/servers/model/server/api-web-multiple.ts +++ b/src/servers/model/server/api-web-multiple.ts @@ -11,7 +11,8 @@ export interface MultipleQueryEntry<Name extends QueryName = QueryName> { entryId: string, query: Name, params?: QueryParams<Name>, - model_nums?: number[] + model_nums?: number[], + copy_all_categories?: boolean } export interface MultipleQuerySpec { diff --git a/src/servers/model/server/api-web.ts b/src/servers/model/server/api-web.ts index d92ce1d89c081427b1e60c22bb48a5b4a62c408b..3ce061a5fadadcf8bf69b93fd13be88f38026ba4 100644 --- a/src/servers/model/server/api-web.ts +++ b/src/servers/model/server/api-web.ts @@ -63,7 +63,8 @@ function mapQuery(app: express.Express, queryName: string, queryDefinition: Quer entryId, queryName: queryName as any, queryParams, - modelNums: commonParams.model_nums + modelNums: commonParams.model_nums, + copyAllCategories: !!commonParams.copy_all_categories })], writer: createResultWriter(res, commonParams.encoding === 'bcif', entryId, queryName), options: { binary: commonParams.encoding === 'bcif' } @@ -134,7 +135,8 @@ function createMultiJob(spec: MultipleQuerySpec, res: express.Response) { entryId: q.entryId, queryName: q.query, queryParams: q.params || { }, - modelNums: q.model_nums + modelNums: q.model_nums, + copyAllCategories: !!q.copy_all_categories })), writer, options: { binary: spec.encoding?.toLowerCase() === 'bcif', tarball: spec.asTarGz } diff --git a/src/servers/model/server/api.ts b/src/servers/model/server/api.ts index e2a797be53d2fcf670349c7543ce4f5ec63e6468..fd2861dd7b06259266a3c8ca7447207f22965d6d 100644 --- a/src/servers/model/server/api.ts +++ b/src/servers/model/server/api.ts @@ -13,6 +13,7 @@ export enum QueryParamType { JSON, String, Integer, + Boolean, Float } @@ -44,12 +45,14 @@ export interface QueryDefinition<Params = any> { export const CommonQueryParamsInfo: QueryParamInfo[] = [ { name: 'model_nums', type: QueryParamType.String, description: `A comma-separated list of model ids (i.e. 1,2). If set, only include atoms with the corresponding '_atom_site.pdbx_PDB_model_num' field.` }, { name: 'encoding', type: QueryParamType.String, defaultValue: 'cif', description: `Determines the output encoding (text based 'CIF' or binary 'BCIF').`, supportedValues: ['cif', 'bcif'] }, + { name: 'copy_all_categories', type: QueryParamType.Boolean, defaultValue: false, description: 'If true, copy all categories from the input file.' }, { name: 'data_source', type: QueryParamType.String, defaultValue: '', description: 'Allows to control how the provided data source ID maps to input file (as specified by the server instance config).' } ]; export interface CommonQueryParamsInfo { model_nums?: number[], encoding?: 'cif' | 'bcif', + copy_all_categories?: boolean data_source?: string } @@ -234,6 +237,7 @@ function _normalizeQueryParams(params: { [p: string]: string }, paramList: Query case QueryParamType.String: el = value; break; case QueryParamType.Integer: el = parseInt(value); break; case QueryParamType.Float: el = parseFloat(value); break; + case QueryParamType.Boolean: el = Boolean(+value); break; } if (p.validation) p.validation(el); @@ -260,6 +264,7 @@ export function normalizeRestCommonParams(params: any): CommonQueryParamsInfo { return { model_nums: params.model_nums ? ('' + params.model_nums).split(',').map(n => n.trim()).filter(n => !!n).map(n => +n) : void 0, data_source: params.data_source, + copy_all_categories: Boolean(params.copy_all_categories), encoding: ('' + params.encoding).toLocaleLowerCase() === 'bcif' ? 'bcif' : 'cif' }; } \ No newline at end of file diff --git a/src/servers/model/server/jobs.ts b/src/servers/model/server/jobs.ts index 62919e74704116b32b2c4576291c355031cc7645..49c512e5bbfacc9bd8476e20d63460efe092f594 100644 --- a/src/servers/model/server/jobs.ts +++ b/src/servers/model/server/jobs.ts @@ -40,7 +40,8 @@ export interface JobEntry { queryDefinition: QueryDefinition, normalizedParams: any, - modelNums?: number[] + modelNums?: number[], + copyAllCategories: boolean } interface JobEntryDefinition<Name extends QueryName> { @@ -48,7 +49,8 @@ interface JobEntryDefinition<Name extends QueryName> { entryId: string, queryName: Name, queryParams: QueryParams<Name>, - modelNums?: number[] + modelNums?: number[], + copyAllCategories: boolean } export function JobEntry<Name extends QueryName>(definition: JobEntryDefinition<Name>): JobEntry { @@ -65,7 +67,8 @@ export function JobEntry<Name extends QueryName>(definition: JobEntryDefinition< entryId: definition.entryId, queryDefinition, normalizedParams, - modelNums: definition.modelNums + modelNums: definition.modelNums, + copyAllCategories: !!definition.copyAllCategories }; } diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts index 4b08c065bec5c1d48945f3fd44166bdac3eb590e..ce6c67924673a7ca35d2400826ed2b7471b60391 100644 --- a/src/servers/model/server/query.ts +++ b/src/servers/model/server/query.ts @@ -163,7 +163,7 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc const queries = structures.map(s => entry.queryDefinition.query(entry.normalizedParams, s)); const result: Structure[] = []; for (let i = 0; i < structures.length; i++) { - const s = await StructureSelection.unionStructure(StructureQuery.run(queries[i], structures[i], { timeoutMs: Config.queryTimeoutMs })); + const s = StructureSelection.unionStructure(StructureQuery.run(queries[i], structures[i], { timeoutMs: Config.queryTimeoutMs })); if (s.elementCount > 0) result.push(s); } perf.end('query'); @@ -178,9 +178,9 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc encoder.writeCategory(_model_server_result, entry); encoder.writeCategory(_model_server_params, entry); - if (entry.queryDefinition.filter) encoder.setFilter(entry.queryDefinition.filter); - if (result.length > 0) encode_mmCIF_categories(encoder, result); - if (entry.queryDefinition.filter) encoder.setFilter(); + if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter(entry.queryDefinition.filter); + if (result.length > 0) encode_mmCIF_categories(encoder, result, { copyAllCategories: entry.copyAllCategories }); + if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter(); perf.end('encode'); const stats: Stats = { diff --git a/src/servers/tsconfig.json b/tsconfig.servers.json similarity index 85% rename from src/servers/tsconfig.json rename to tsconfig.servers.json index a1d79bbde5f0134635cfaee4ed2ce53063d373c8..343cb7b047808a8ba0aa24d9b872bcc3a40f9770 100644 --- a/src/servers/tsconfig.json +++ b/tsconfig.servers.json @@ -16,8 +16,8 @@ "noEmitHelpers": true, "jsx": "react", "lib": [ "es6", "dom", "esnext.asynciterable", "es2016" ], - "rootDir": "../../src", - "outDir": "../../lib/servers" + "rootDir": "src", + "outDir": "lib/servers" }, - "include": [ "**/*" ] + "include": [ "src/servers/**/*" ] } \ No newline at end of file