diff --git a/src/apps/schema-generator/schema-from-mmcif-dic.ts b/src/apps/schema-generator/schema-from-cif-dic.ts similarity index 100% rename from src/apps/schema-generator/schema-from-mmcif-dic.ts rename to src/apps/schema-generator/schema-from-cif-dic.ts diff --git a/src/apps/schema-generator/util/cif-dic.ts b/src/apps/schema-generator/util/cif-dic.ts index 6036c7c82f1e12dfd1817fdd5f897315ebd60306..9b16bc602beec6be83d60887658d8cd9b8f8fcf8 100644 --- a/src/apps/schema-generator/util/cif-dic.ts +++ b/src/apps/schema-generator/util/cif-dic.ts @@ -77,7 +77,7 @@ interface FrameData { } // get field from given or linked category -function getField ( category: string, field: string, d: Data.CifFrame, ctx: FrameData): Data.CifField|undefined { +function getField (category: string, field: string, d: Data.CifFrame, ctx: FrameData): Data.CifField|undefined { const { categories, links } = ctx const cat = d.categories[category] @@ -130,6 +130,7 @@ function getSubCategory (d: Data.CifFrame, ctx: FrameData): string|undefined { function getDescription (d: Data.CifFrame, ctx: FrameData): string|undefined { const value = getField('item_description', 'description', d, ctx) if (value) { + // trim (after newlines) and remove references to square brackets return value.str(0).trim() .replace(/(\r\n|\r|\n)([ \t]+)/g, '\n') .replace(/(\[[1-3]\])+ element/, 'elements') @@ -195,6 +196,40 @@ export function generateSchema (frames: CifFrame[]) { const links: FrameLinks = {} const ctx = { categories, links } + // get category metadata + frames.forEach(d => { + if (d.header[0] === '_') return + const categoryKeyNames = new Set<string>() + const categoryKey = d.categories['category_key'] + if (categoryKey) { + const categoryKey_names = categoryKey.getField('name') + if (categoryKey_names) { + for (let i = 0, il = categoryKey_names.rowCount; i < il; ++i) { + categoryKeyNames.add(categoryKey_names.str(i)) + } + } + } + let description = '' + const category = d.categories['category'] + if (category) { + const category_description = category.getField('description') + if (category_description) { + description = category_description.str(0).trim() + .replace(/(\r\n|\r|\n)([ \t]+)/g, '\n') // remove padding after newlines + } else { + console.log(`no description given for category '${category}'`) + } + } + if (categoryKeyNames.size === 0) { + console.log(`no key given for category '${category}'`) + } + schema[d.header] = { description, key: categoryKeyNames, columns: {} } + // console.log('++++++++++++++++++++++++++++++++++++++++++') + // console.log('name', d.header) + // console.log('desc', description) + // console.log('key', categoryKeyNames) + }) + // build list of links between categories frames.forEach(d => { if (d.header[0] !== '_') return @@ -216,6 +251,7 @@ export function generateSchema (frames: CifFrame[]) { } }) + // get field data Object.keys(categories).forEach(fullName => { const d = categories[fullName] if (!d) { @@ -226,10 +262,15 @@ export function generateSchema (frames: CifFrame[]) { const itemName = d.header.substring(d.header.indexOf('.') + 1) let fields: { [k: string]: Column } if (categoryName in schema) { - fields = schema[categoryName] + fields = schema[categoryName].columns } else { + console.log(`category '${categoryName}' has no metadata`) fields = {} - schema[categoryName] = fields + schema[categoryName] = { + description: '', + key: new Set(), + columns: fields + } } const description = getDescription(d, ctx) || '' diff --git a/src/apps/schema-generator/util/generate.ts b/src/apps/schema-generator/util/generate.ts index 8b57095a893353998ca7f8c92b54df584ea6a94d..ecf33b4955a02f66877089644c1557ca07c7448d 100644 --- a/src/apps/schema-generator/util/generate.ts +++ b/src/apps/schema-generator/util/generate.ts @@ -18,17 +18,7 @@ function header (name: string, info: string, importDatabasePath = 'mol-data/db') import { Database, Column } from '${importDatabasePath}' -import Schema = Column.Schema - -const str = Schema.str; -const int = Schema.int; -const float = Schema.float; -const coord = Schema.coord; - -const Aliased = Schema.Aliased; -const Matrix = Schema.Matrix; -const Vector = Schema.Vector; -const List = Schema.List;` +import Schema = Column.Schema` } function footer (name: string) { @@ -37,6 +27,32 @@ export type ${name}_Schema = typeof ${name}_Schema; export interface ${name}_Database extends Database<${name}_Schema> {}` } +function getTypeShorthands(schema: Database, fields?: Filter) { + const types = new Set<string>() + Object.keys(schema).forEach(table => { + if (fields && !fields[table]) return + const { columns} = schema[table] + Object.keys(columns).forEach(columnName => { + if (fields && !fields[table][columnName]) return + types.add(schema[table].columns[columnName].type) + }) + }) + const shorthands: string[] = [] + types.forEach(type => { + switch (type) { + case 'str': shorthands.push('const str = Schema.str;'); break + case 'int': shorthands.push('const int = Schema.int;'); break + case 'float': shorthands.push('const float = Schema.float;'); break + case 'coord': shorthands.push('const coord = Schema.coord;'); break + case 'enum': shorthands.push('const Aliased = Schema.Aliased;'); break + case 'matrix': shorthands.push('const Matrix = Schema.Matrix;'); break + case 'vector': shorthands.push('const Vector = Schema.Vector;'); break + case 'list': shorthands.push('const List = Schema.List;'); break + } + }) + return shorthands.join('\n') +} + function getTypeDef(c: Column): string { switch (c.type) { case 'str': return 'str' @@ -63,27 +79,34 @@ function getTypeDef(c: Column): string { const reSafePropertyName = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/ function safePropertyString(name: string) { return name.match(reSafePropertyName) ? name : `'${name}'` } +function doc(description: string, spacesCount: number) { + const spaces = ' '.repeat(spacesCount) + return [ + `${spaces}/**`, + `${indentString(description, 1, `${spaces} * `)}`.replace(/ +\n/g, '\n'), + `${spaces} */` + ].join('\n') +} + export function generate (name: string, info: string, schema: Database, fields?: Filter, importDatabasePath?: string) { const codeLines: string[] = [] codeLines.push(`export const ${name}_Schema = {`) Object.keys(schema).forEach(table => { if (fields && !fields[table]) return + const { description, columns} = schema[table] + if (description) codeLines.push(doc(description, 4)) codeLines.push(` ${safePropertyString(table)}: {`) - const columns = schema[table] Object.keys(columns).forEach(columnName => { if (fields && !fields[table][columnName]) return - const typeDef = getTypeDef(columns[columnName]) - if (columns[columnName].description) { - codeLines.push(` /**`) - codeLines.push(`${indentString(columns[columnName].description, 1, ' * ')}`) - codeLines.push(` */`) - } + const c = columns[columnName] + const typeDef = getTypeDef(c) + if (c.description) codeLines.push(doc(c.description, 8)) codeLines.push(` ${safePropertyString(columnName)}: ${typeDef},`) }) codeLines.push(' },') }) codeLines.push('}') - return `${header(name, info, importDatabasePath)}\n\n${codeLines.join('\n')}\n${footer(name)}` + return `${header(name, info, importDatabasePath)}\n\n${getTypeShorthands(schema, fields)}\n\n${codeLines.join('\n')}\n${footer(name)}` } diff --git a/src/apps/schema-generator/util/schema.ts b/src/apps/schema-generator/util/schema.ts index 3cc2026e741448c7a3575096642adcc3b389e891..c18668e94d9c92cc6c51f4f34f3bed5e2218063b 100644 --- a/src/apps/schema-generator/util/schema.ts +++ b/src/apps/schema-generator/util/schema.ts @@ -5,7 +5,11 @@ */ export interface Database { [ tableName: string ]: Table } -export interface Table { [ columnName: string ]: Column } +export interface Table { + description: string + key: Set<string> + columns: { [ columnName: string ]: Column } +} export type Column = IntCol | StrCol | FloatCol | CoordCol | EnumCol | VectorCol | MatrixCol | ListCol type BaseCol = { description: string }