Commit 244e2d7a authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Resolve metainfo dependency on the server side.

parent 8a81ae37
Subproject commit 567a96be45394556d94c61cb29e345ebb31a2f9f Subproject commit 22024d2bea48daaa850497bf0452f20a1850a4c0
class BadNomadMIError extends Error {
}
class Schema {
category = 'category'
section = 'section'
property = 'property'
value = 'value'
reference = 'reference'
pkg = 'package'
isCategory = (element) => element.mType === this.category
isFeature = (element) => element.mType === this.section || this.isProperty(element)
isSection = (element) => element.mType === this.section
isReference = (element) => element.mType === this.reference
isValue = (element) => element.mType === this.value
isProperty = (element) => this.isValue(element) || this.isReference(element)
isDefinition = (element) => this.isCategory(element) || this.isFeature(element)
isPakage = (element) => element.mType === this.pkg
isElement = (element) => this.isPakage(element) || this.isDefinition(element)
type = (type) => {
if (type === 'C') {
return 'string'
} else if (type === 'f') {
return 'float'
} else if (type === 'i') {
return 'integer'
} else {
return type
}
}
isParent = (child, parent) => {
let current = child.parent
while (current) {
if (parent === current) {
return true
}
current = current.parent
}
return false
}
allContents = (element, func) => {
if (this.isPakage(element)) {
(element.definitions || []).forEach(element => this.allContents(element, func))
}
func(element)
}
}
export const schema = new Schema()
export default class MetaInfoRepository {
constructor(metaInfoPackages) {
this.contents = []
this.names = {}
this.warnings = []
this.errors = []
Object.keys(metaInfoPackages).forEach(packageName => this.addNomadMiJson(packageName, metaInfoPackages[packageName]))
this.resolveAll()
this.resolveSuperNames()
this.addReverseRefs()
this.addCategoryFeaturesToSections()
this.warnings.forEach(warning => console.warning(warning))
if (this.errors.length) {
console.log(this.errors[0])
console.log('there might be more errors')
}
// this.errors.forEach(error => console.error(error))
}
createProxy(reference) {
return {
mIsProxy: true,
mReference: reference
}
}
get(metaInfoName) {
return this.names[metaInfoName]
}
resolve(proxy) {
if (proxy.mIsProxy) {
const resolved = this.names[proxy.mReference]
return resolved || proxy
} else {
return proxy
}
}
resolveSuperNames() {
this.allContents(element => {
if (element._superNames) {
element._superNames.forEach(parentOrSuper => {
if (schema.isSection(parentOrSuper)) {
if (schema.isFeature(element)) {
if (element.parent) {
// this.errors.push(
// new BadNomadMIError(`More than one parent in feature ${element.name}`))
} else {
element.parent = parentOrSuper
}
} else {
if (element.parent) {
this.errors.push(
new BadNomadMIError(`More than one section for category ${element.name}`))
} else {
element.parent = parentOrSuper
}
}
} else if (schema.isCategory(parentOrSuper)) {
if (schema.isCategory(element)) {
element.super = element.super || []
element.super.push(parentOrSuper)
} else if (schema.isFeature(element)) {
element.categories = element.categories || []
element.categories.push(parentOrSuper)
} else {
this.errors.push(new BadNomadMIError(`Non feature ${element.name} references category ${parentOrSuper.name}.`))
}
} else if (schema.isProperty(parentOrSuper)) {
this.warnings.push(`SuperName in ${element.name} references property ${parentOrSuper.name}. That is not allowed ?!`)
} else {
this.errors.push(new BadNomadMIError(`Referenced parent or super ${parentOrSuper.name} is not a section or category (in ${element.name})`))
}
})
}
})
}
resolveAll() {
this.allContents(element => {
if (element._superNames) {
element._superNames = element._superNames.map((ref) => {
const resolved = this.resolve(ref)
if (resolved.mIsProxy) {
element.problems.push(`Could not resolve parent section ${ref.mReference} in ${element.name}.`)
}
return resolved
})
}
if (element.referencedSection) {
const ref = element.referencedSection
const resolved = this.resolve(ref)
if (resolved.mIsProxy) {
element.problems.push(`Could not resolve referenced section ${ref.mReference} in ${element.name}.`)
}
element.referencedSection = resolved
}
})
}
addReverseRefs() {
this.allContents(definition => {
if (definition.parent) {
definition.parent.features = definition.parent.features || []
definition.parent.features.push(definition)
}
if (schema.isPakage(definition)) {
definition.definitions.forEach(feature => {
feature.package = definition
})
}
})
}
addCategoryFeaturesToSections() {
this.allContents(definition => {
if (schema.isProperty(definition) && definition.categories) {
definition.categories.forEach(category => {
const section = category.parent
if (section) {
section.features = section.features || []
if (section.features.indexOf(definition) === -1) {
section.features.push(definition)
definition.parent = section
}
}
})
}
})
}
allContents(func) {
this.contents.forEach(element => schema.allContents(element, func))
}
addName(namedElement) {
const {name} = namedElement
if (this.names[name]) {
this.errors.push(new BadNomadMIError(`Element with name ${namedElement.name} does already exist.`))
} else {
this.names[name] = namedElement
}
}
addNomadMiJson(name, json) {
const transformMetaInfo = (metaInfo) => {
const isMeta = metaInfo.kindStr === 'type_meta'
const isSection = metaInfo.kindStr === 'type_section'
const isCategory = metaInfo.kindStr === 'type_abstract_document_content'
const isProperty = metaInfo.dtypeStr && metaInfo.dtypeStr !== 'r'
const isReference = metaInfo.dtypeStr === 'r' || metaInfo.referencedSections
const isValue = isProperty && !isReference
const superNames = metaInfo.superNames || []
const definition = {
name: metaInfo.name,
description: metaInfo.description,
miJson: metaInfo,
type: schema.type(metaInfo.dtypeStr),
problems: [],
_superNames: superNames.map(ref => this.createProxy(ref))
}
if (isSection) {
definition.mType = schema.section
} else if (isCategory) {
definition.mType = schema.category
} else if (isValue) {
definition.mType = schema.value
} else if (isReference) {
definition.mType = schema.reference
if (!metaInfo.referencedSections || metaInfo.referencedSections.length < 1) {
definition.problems.push(`Reference ${definition.name} does not reference anything.`)
} else {
definition.referencedSection = this.createProxy(metaInfo.referencedSections[0])
}
} else if (isMeta) {
// ignore meta-meta definitions
} else {
this.errors.push(new BadNomadMIError(`Cannot determine mType ${metaInfo.kindStr} of feature ${name}:${metaInfo.name}`))
}
this.addName(definition)
return definition
}
const metaInfos = json || []
const pkg = {
mType: schema.pkg,
name: name,
definitions: metaInfos
.filter(metaInfo => !metaInfo.name.startsWith('x_'))
.map(transformMetaInfo)
}
this.addName(pkg)
this.contents.push(pkg)
}
}
...@@ -8,6 +8,7 @@ import { Typography, withStyles } from '@material-ui/core' ...@@ -8,6 +8,7 @@ import { Typography, withStyles } from '@material-ui/core'
import LoginLogout from './LoginLogout' import LoginLogout from './LoginLogout'
import { Cookies, withCookies } from 'react-cookie' import { Cookies, withCookies } from 'react-cookie'
import { compose } from 'recompose' import { compose } from 'recompose'
import MetaInfoRepository from './MetaInfoRepository'
const ApiContext = React.createContext() const ApiContext = React.createContext()
...@@ -261,11 +262,11 @@ class Api { ...@@ -261,11 +262,11 @@ class Api {
.finally(this.onFinishLoading) .finally(this.onFinishLoading)
} }
_cachedMetaInfo = null _metaInfoRepository = null
async getMetaInfo() { async getMetaInfo() {
if (this._cachedMetaInfo) { if (this._metaInfoRepository) {
return this._cachedMetaInfo return this._metaInfoRepository
} else { } else {
this.onStartLoading() this.onStartLoading()
const loadMetaInfo = async(path) => { const loadMetaInfo = async(path) => {
...@@ -273,31 +274,11 @@ class Api { ...@@ -273,31 +274,11 @@ class Api {
return client.apis.archive.get_metainfo({metainfo_path: path}) return client.apis.archive.get_metainfo({metainfo_path: path})
.catch(this.handleApiError) .catch(this.handleApiError)
.then(response => response.body) .then(response => response.body)
.then(data => {
if (!this._cachedMetaInfo) {
this._cachedMetaInfo = {
loadedDependencies: {}
}
}
this._cachedMetaInfo.loadedDependencies[path] = true
if (data.metaInfos) {
data.metaInfos.forEach(info => {
this._cachedMetaInfo[info.name] = info
info.relativePath = path
})
}
if (data.dependencies) {
data.dependencies
.filter(dep => this._cachedMetaInfo.loadedDependencies[dep.relativePath] !== true)
.forEach(dep => {
loadMetaInfo(dep.relativePath)
})
}
})
} }
await loadMetaInfo('all.nomadmetainfo.json') const metaInfos = await loadMetaInfo('all.nomadmetainfo.json')
this._metaInfoRepository = new MetaInfoRepository({'all.nomadmetainfo.json': metaInfos})
this.onFinishLoading() this.onFinishLoading()
return this._cachedMetaInfo return this._metaInfoRepository
} }
} }
...@@ -495,6 +476,8 @@ class WithApiComponent extends React.Component { ...@@ -495,6 +476,8 @@ class WithApiComponent extends React.Component {
raiseError(error) { raiseError(error) {
const { raiseError, showErrorPage } = this.props const { raiseError, showErrorPage } = this.props
console.error(error)
if (!showErrorPage) { if (!showErrorPage) {
raiseError(error) raiseError(error)
} else { } else {
......
...@@ -90,7 +90,7 @@ class ArchiveEntryView extends React.Component { ...@@ -90,7 +90,7 @@ class ArchiveEntryView extends React.Component {
render() { render() {
const { classes, uploadId, calcId } = this.props const { classes, uploadId, calcId } = this.props
const { data, showMetaInfo, metaInfo } = this.state const { data, showMetaInfo, metaInfo } = this.state
const metaInfoData = metaInfo ? metaInfo[showMetaInfo] : null const metaInfoData = metaInfo ? metaInfo.get(showMetaInfo) : null
return ( return (
<div className={classes.root}> <div className={classes.root}>
......
...@@ -23,6 +23,7 @@ from flask import send_file ...@@ -23,6 +23,7 @@ from flask import send_file
from flask_restplus import abort, Resource from flask_restplus import abort, Resource
import nomad_meta_info import nomad_meta_info
from nomadcore.local_meta_info import load_metainfo
from nomad.files import UploadFiles, Restricted from nomad.files import UploadFiles, Restricted
...@@ -124,12 +125,7 @@ class MetainfoResource(Resource): ...@@ -124,12 +125,7 @@ class MetainfoResource(Resource):
file_dir = os.path.dirname(os.path.abspath(nomad_meta_info.__file__)) file_dir = os.path.dirname(os.path.abspath(nomad_meta_info.__file__))
metainfo_file = os.path.normpath(os.path.join(file_dir, metainfo_path.strip())) metainfo_file = os.path.normpath(os.path.join(file_dir, metainfo_path.strip()))
rv = send_file( meta_info, _ = load_metainfo(metainfo_file)
metainfo_file, return meta_info.toJsonList(False), 200
mimetype='application/json',
as_attachment=True,
attachment_filename=os.path.basename(metainfo_file))
return rv
except FileNotFoundError: except FileNotFoundError:
abort(404, message='The metainfo %s does not exist.' % metainfo_path) abort(404, message='The metainfo %s does not exist.' % metainfo_path)
...@@ -545,6 +545,13 @@ class TestArchive(UploadFilesBasedTests): ...@@ -545,6 +545,13 @@ class TestArchive(UploadFilesBasedTests):
def test_get_metainfo(self, client): def test_get_metainfo(self, client):
rv = client.get('/archive/metainfo/all.nomadmetainfo.json') rv = client.get('/archive/metainfo/all.nomadmetainfo.json')
assert rv.status_code == 200 assert rv.status_code == 200
metainfo = json.loads((rv.data))
names = {}
for item in metainfo:
name = item['name']
assert name not in names
names[name] = item
assert len(metainfo) > 0
class TestRepo(): class TestRepo():
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment