Source code for fastr.helpers.schematotable

# Copyright 2011-2014 Biomedical Imaging Group Rotterdam, Departments of
# Medical Informatics and Radiology, Erasmus MC, Rotterdam, The Netherlands
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.

A module to generate reStructuredText tables from json schema files

from jsonschema import RefResolver

[docs]class SchemaPrinter(object): """ Object that create a table in reStructuedText from a json schema """
[docs] def __init__(self, schema, skipfirst=False): """ Create the printer object :param dict schema: the json schema to print :param bool skipfirst: flag to indicate that the first line should not be printed """ self.schema = schema self.location = [] self.depth = 0 self.maxdepth = 0 self.length = {} self.description_length = 12 self.lines = [] self.resolver = RefResolver('', self.schema) self.skipfirst = skipfirst self.parse()
[docs] def __str__(self): """ String representation of json schema (that is the printed table) """ return '\n'.join(self.printlines())
[docs] def descend(self, properties): """ Descend into a subschema :param dict properties: the properties in the subschema """ for key, subschema in sorted(properties.items()): key = '``{}``'.format(key) self.location.append(key) self.depth += 1 self.maxdepth = max(self.maxdepth, self.depth) if self.depth not in self.length: self.length[self.depth] = len(key) + 2 else: self.length[self.depth] = max(self.length[self.depth], len(key) + 2) self.parse(subschema) self.location.pop() self.depth -= 1
[docs] def parse(self, schema=None): """ Parse a schema :param dict schema: the schema to parse """ if schema is None: schema = self.schema # If it is a reference, follow and write with that instead if '$ref' in schema: with self.resolver.resolving(schema['$ref']) as newschema: return self.parse(newschema) if 'type' not in schema: return datatype = schema['type'] if 'description' in schema: self.description_length = max(self.description_length, len(schema['description'])) description = schema['description'] else: description = '' if datatype == 'array': self.location[-1] = '``{}[]``'.format(self.location[-1][2:-2]) self.lines.append((self.location[:], datatype, description)) if datatype == 'object': self.descend(schema['properties']) elif datatype == 'array' and isinstance(schema['items'], dict): items = schema['items'] if '$ref' in items: with self.resolver.resolving(items['$ref']) as newschema: if 'properties' in newschema: return self.descend(newschema['properties']) elif 'properties' in items: self.descend(items['properties'])
[docs] def printlines(self): """ Given a parsed schema (parsing happens when the object is constructed), print all the lines :return: the printed table :rtype: str """ output_lines = [] sepertor = ''.join('+{{keys[{n}]:{{fill[{n}]}}<{length}}}'.format(length=l, n=nr-1) for nr, l in self.length.items()) sepertor = '{start}+{{desc:{{dfill}}<{length}}}+'.format(start=sepertor, length=self.description_length) contentline = ''.join('{{sep[{n}]}}{{keys[{n}]: <{length}}}'.format(length=l, n=nr-1) for nr, l in self.length.items()) contentline = '{start}|{{desc: <{length}}}|'.format(start=contentline, length=self.description_length) lastkey = [] dummykey = [''] * self.maxdepth first = True if self.skipfirst: lines = self.lines[1:] else: lines = self.lines for line in lines: key = line[0] limit = min(len(lastkey), len(key)) same = [key[k] == lastkey[k] if k < limit else False for k in range(self.maxdepth)] fill = [' ' if x else '-' for x in same] keys = [key[k] if k < len(key) and not same[k] else '' for k in range(self.maxdepth)] sep = ['|' if k <= len(key) else ' ' for k in range(self.maxdepth)] output_lines.append(sepertor.format(keys=dummykey, fill=fill, desc='', dfill='-')) if first: headerkey = dummykey[:] headerkey[0] = 'Attributes' headersep = '|' + ' ' * (self.maxdepth - 1) output_lines.append(contentline.format(keys=headerkey, desc='Description', sep=headersep)) output_lines.append(sepertor.format(keys=dummykey, fill='===', desc='', dfill='=')) output_lines.append(contentline.format(keys=keys, desc=line[2], sep=sep)) first = False lastkey = key fill = '-' * self.maxdepth output_lines.append(sepertor.format(keys=dummykey, fill=fill, desc='', dfill='-')) return output_lines