Пример #1
0
    def test_formats(self):
        self.assertEqual("ThisIsIt", camelcase("this is it"))
        self.assertEqual("ThisIsIT", camelcase("  this   is iT   "))
        self.assertEqual("IBeY", camelcase("i be y "))
        self.assertEqual("ThisIsIt", camelcase("This__is_it"))

        self.assertEqual("this_is_it", underscore(" this is it "))
        self.assertEqual("this_is_it", underscore("this   is   it"))

        self.assertEqual("thisIsIt", lcamelcase("   this   is\t  it\n"))

        self.assertEqual('abc', be('  abc\n'))
        self.assertEqual('', be(None))
        self.assertEqual('', be('   '))
Пример #2
0
    def _link(self, obj: Optional[Element], *, after_link: str = None, use_desc: bool=False,
             add_subset: bool=True) -> str:
        """ Create a link to ref if appropriate.

        @param ref: the name or value of a class, slot, type or the name of a built in type.
        @param after_link: Text to put between link and description
        @param use_desc: True means append a description after the link if available
        @param add_subset: True means add any subset information that is available
        @return:
        """
        nl = '\n'
        if obj is None or not self.is_secondary_ref(obj.name):
            return self.bbin(obj)
        if isinstance(obj, SlotDefinition):
            link_name = ((be(obj.domain) + '➞') if obj.alias else '') + self.aliased_slot_name(obj)
            link_ref = underscore(obj.name)
        elif isinstance(obj, TypeDefinition):
            link_name = camelcase(obj.name)
            link_ref = f"types/{link_name}" if not self.no_types_dir else f"{link_name}"
        elif isinstance(obj, ClassDefinition):
            link_name = camelcase(obj.name)
            link_ref = camelcase(link_name)
        else:
            link_name = obj.name
            link_ref = link_name
        desc = self.desc_for(obj, use_desc)
        return f'[{link_name}]' \
               f'({link_ref}.{self.format})' + \
                 (f' {after_link} ' if after_link else '') + (f' - {desc.split(nl)[0]}' if desc else '')
Пример #3
0
    def visit_schema(self, directory: str = None, classes: Set[ClassDefinitionName] = None, image_dir: bool = False,
                     noimages: bool = False, **_) -> None:
        self.gen_classes = classes if classes else []
        for cls in self.gen_classes:
            if cls not in self.schema.classes:
                raise ValueError("Unknown class name: {cls}")
        if self.gen_classes:
            self.gen_classes_neighborhood = self.neighborhood(list(self.gen_classes))

        self.directory = directory
        if directory:
            os.makedirs(directory, exist_ok=True)
        elif image_dir:
            raise ValueError(f"Image directory can only be used with '-d' option")
        if image_dir:
            self.image_directory = os.path.join(directory, 'images')
            if not noimages:
                os.makedirs(self.image_directory, exist_ok=True)
        self.noimages = noimages
        if not self.no_types_dir:
            os.makedirs(os.path.join(directory, 'types'), exist_ok=True)

        with open(self.exist_warning(directory, 'index.md'), 'w') as ixfile:
            with redirect_stdout(ixfile):
                self.frontmatter(f"{self.schema.name.title()} schema")
                self.para(be(self.schema.description))

                self.header(3, 'Classes')
                for cls in sorted(self.schema.classes.values(), key=lambda c: c.name):
                    if not cls.is_a and not cls.mixin and self.is_secondary_ref(cls.name):
                        self.class_hier(cls)

                self.header(3, 'Mixins')
                for cls in sorted(self.schema.classes.values(), key=lambda c: c.name):
                    if cls.mixin and self.is_secondary_ref(cls.name):
                        self.class_hier(cls)

                self.header(3, 'Slots')
                for slot in sorted(self.schema.slots.values(), key=lambda s: s.name):
                    if not slot.is_a and self.is_secondary_ref(slot.name):
                        self.pred_hier(slot)

                self.header(3, 'Enums')
                for enu in sorted(self.schema.enums.values(), key=lambda e: e.name):
                    self.enum_hier(enu)

                self.header(3, 'Types')
                self.header(4, 'Built in')
                for builtin_name in sorted(self.synopsis.typebases.keys()):
                    self.bullet(f'**{builtin_name}**')
                self.header(4, 'Defined')
                for typ in sorted(self.schema.types.values(), key=lambda t: t.name):
                    if self.is_secondary_ref(typ.name):
                        if typ.typeof:
                            typ_typ = self.type_link(typ.typeof)
                        else:
                            typ_typ = f'**{typ.base}**'

                        self.bullet(self.type_link(typ, after_link=f' ({typ_typ})', use_desc=True))
Пример #4
0
    def gen_schema(self) -> str:
        split_descripton = '\n#              '.join(
            split_line(be(self.schema.description), split_len=100))
        head = f'''# Auto generated from {self.schema.source_file} by {self.generatorname} version: {self.generatorversion}
# Generation date: {self.schema.generation_date}
# Schema: {self.schema.name}
#''' if self.schema.generation_date else ''

        return f'''{head}
Пример #5
0
 def visit_class(self, cls: ClassDefinition) -> bool:
     if cls.mixin or cls.abstract:
         return False
     self.clsobj = JsonObj(title=camelcase(cls.name),
                           type='object',
                           properties=JsonObj(),
                           required=[],
                           additionalProperties=False,
                           description=be(cls.description))
     return True
Пример #6
0
 def visit_class(self, cls: ClassDefinition) -> bool:
     # TODO: find out what to do with mappings
     if not self.closure or cls.name in self.closure:
         self.writer.writerow({
             'id': underscore(cls.name),
             # 'mappings': "|".join(cls.mappings),
             'mappings': '',
             'description': be(cls.description)
         })
         return True
     return False
Пример #7
0
    def gen_schema(self) -> str:
        # The metamodel uses Enumerations to define itself, so don't import if we are generating the metamodel
        enumimports = '' if self.genmeta else \
            'from linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions\n'
        handlerimport = 'from linkml.utils.enumerations import EnumDefinitionImpl'
        split_descripton = '\n#              '.join(
            split_line(be(self.schema.description), split_len=100))
        head = f'''# Auto generated from {self.schema.source_file} by {self.generatorname} version: {self.generatorversion}
# Generation date: {self.schema.generation_date}
# Schema: {self.schema.name}
#''' if self.schema.generation_date else ''

        return f'''{head}
Пример #8
0
 def visit_class(self, cls: ClassDefinition) -> bool:
     self.clswq = (WQ().add_class(camelcase(cls.name)).label(
         camelcase(cls.name)).description(be(cls.description)))
     if cls.is_a:
         self.clswq.parent(camelcase(cls.is_a))
     if cls.abstract:
         self.clswq.abstract()
     if cls.broad_mappings:
         if any(
                 str(self.namespaces.uri_for(m)) ==
                 "http://terminusdb.com/schema/system#Document"
                 for m in cls.broad_mappings):
             self.clswq.parent("Document")
     return True
Пример #9
0
 def element_header(self, obj: Element, name: str, curie: str, uri: str) -> None:
     simple_name = curie.split(':', 1)[1]
     if isinstance(obj, TypeDefinition):
         obj_type = 'Type'
     elif isinstance(obj, ClassDefinition):
         obj_type = 'Class'
     elif isinstance(obj, SlotDefinition):
         obj_type = 'Slot'
     elif isinstance(obj, EnumDefinition):
         obj_type = 'Enum'
     else:
         obj_type = 'Class'
     self.header(1, f"{obj_type}: {simple_name}" + (f" _(deprecated)_" if obj.deprecated else ""))
     self.para(be(obj.description))
     print(f'URI: [{curie}]({uri})')
     print()
Пример #10
0
    def element_header(self, obj: Element, name: str, curie: str,
                       uri: str) -> None:
        """
        Write the header for an element.

        Parameters
        ----------
        obj: linkml_model.meta.Element
            An element
        name: str
            The name of the element
        curie: str
            The CURIE of the element
        uri: str
            The URI of the element

        """
        if curie.startswith('http'):
            if curie.startswith(
                    'https://w3id.org/biolink/vocab/linkml:types/'):
                simple_name = curie.split('/')[-1]
                uri = f"https://linkml.github.io/linkml-model/docs/types/{simple_name}"
                simple_name = f"metatype:{simple_name}"
            else:
                simple_name = curie
        else:
            simple_name = curie.split(':', 1)[1]
        if isinstance(obj, TypeDefinition):
            obj_type = 'Type'
        elif isinstance(obj, ClassDefinition):
            obj_type = 'Class'
        elif isinstance(obj, SlotDefinition):
            obj_type = 'Slot'
        else:
            obj_type = 'Class'
        self.header(
            1, f"{obj_type}: {simple_name}" +
            (f" _(deprecated)_" if obj.deprecated else ""))
        self.para(be(obj.description))
        print(f'URI: [{curie}]({uri})')
        print()
Пример #11
0
    def visit_slot(self, aliased_slot_name: str, slot: SlotDefinition) -> None:
        """
        Visit a given slot definition and write the following properties in Markdown,
        - Frontmatter
        - Mappings
        - Description
        - Domain and Range constraints
        - Parents
        - Children
        - Used by

        Parameters
        ----------
        cls: linkml_model.meta.SlotDefinition
            A SlotDefinition

        """
        if not slot.alias:
            with open(self.dir_path(slot), 'w') as slotfile:
                with redirect_stdout(slotfile):
                    slot_curie = self.namespaces.uri_or_curie_for(
                        self.namespaces._base, underscore(slot.name))
                    slot_uri = self.namespaces.uri_for(slot_curie)
                    ancs = self.ancestors(slot)
                    if 'related to' in ancs:
                        if slot.mixin:
                            parent = 'Slot Mixins'
                        else:
                            parent = 'Predicates'
                        grand_parent = 'Slots'
                        slot_type = 'Relation'
                    elif 'node property' in ancs:
                        if slot.mixin:
                            parent = 'Slot Mixins'
                        else:
                            parent = 'Node Properties'
                        grand_parent = 'Slots'
                        slot_type = 'Slot'
                    elif 'association slot' in ancs:
                        if slot.mixin:
                            parent = 'Slot Mixins'
                        else:
                            parent = 'Edge Properties'
                        grand_parent = 'Slots'
                        slot_type = 'Slot'
                    else:
                        if slot.mixin:
                            parent = 'Slot Mixins'
                        else:
                            parent = 'Other Slots'
                        grand_parent = 'Slots'
                        slot_type = 'Slot'
                    self.frontmatter(
                        **{
                            'parent': parent,
                            'title': slot_curie,
                            'grand_parent': grand_parent,
                            'layout': 'default'
                        })
                    simple_name = slot_curie.split(':', 1)[1]
                    self.header(
                        1, f"{slot_type}: {simple_name}" +
                        (f" _(deprecated)_" if slot.deprecated else ""))
                    for s in slot.in_subset:
                        self.badges(s, f'{s}-subset-label')

                    self.para(be(slot.description))
                    print(f'URI: [{slot_curie}]({slot_uri})')

                    self.header(2, 'Domain and Range')
                    print(
                        f'{self.class_link(slot.domain)} ->{self.predicate_cardinality(slot)} '
                        f'{self.class_type_link(slot.range)}')

                    self.header(2, 'Parents')
                    if slot.is_a:
                        self.bullet(f' is_a: {self.slot_link(slot.is_a)}')

                    self.header(2, 'Children')
                    if slot.name in sorted(self.synopsis.isarefs):
                        for child in sorted(
                                self.synopsis.isarefs[slot.name].slotrefs):
                            child_slot = self.schema.slots[child]
                            if not child_slot.alias:
                                self.bullet(f' {self.slot_link(child)}')

                    self.header(2, 'Used by')
                    if slot.name in sorted(self.synopsis.slotrefs):
                        for rc in sorted(
                                self.synopsis.slotrefs[slot.name].classrefs):
                            self.bullet(f'{self.class_link(rc)}')
                    if aliased_slot_name == 'relation':
                        if slot.subproperty_of:
                            self.bullet(
                                f' reifies: {self.slot_link(slot.subproperty_of) if slot.subproperty_of in self.schema.slots else slot.subproperty_of}'
                            )
                    self.element_properties(slot)
Пример #12
0
    def visit_schema(self,
                     directory: str = None,
                     classes: Set[ClassDefinitionName] = None,
                     image_dir: bool = False,
                     noimages: bool = False,
                     **_) -> None:
        """
        Visit the schema and generate Markdown for each ClassDefinition, SlotDefinition,
        and TypeDefinition.

        Parameters
        ----------
        directory: str
            The directory to write to
        classes: Set[ClassDefinitionName]
            A set of classes to subset
        image_dir: str
            The directory to write static images
        noimages: bool
            Whether or not to generate static images

        """
        self.gen_classes = classes if classes else []
        for cls in self.gen_classes:
            if cls not in self.schema.classes:
                raise ValueError("Unknown class name: {cls}")
        if self.gen_classes:
            self.gen_classes_neighborhood = self.neighborhood(
                list(self.gen_classes))

        self.directory = directory
        if directory:
            os.makedirs(directory, exist_ok=True)
        elif image_dir:
            raise ValueError(
                f"Image directory can only be used with '-d' option")
        if image_dir:
            self.image_directory = os.path.join(directory, 'images')
            if not noimages:
                os.makedirs(self.image_directory, exist_ok=True)
        self.noimages = noimages
        self.types_directory = os.path.join(directory, 'types')
        os.makedirs(self.types_directory, exist_ok=True)
        self.doc_root_title = f'Browse {self.schema.name.title().replace("-", " ")}'
        self.seen_elements = set()
        with open(os.path.join(directory, 'index.md'), 'w') as ixfile:
            # Create the data model index
            with redirect_stdout(ixfile):
                self.frontmatter(
                    **{
                        'title': self.doc_root_title,
                        'has_children': 'true',
                        'nav_order': 2,
                        'layout': 'default',
                        'has_toc': 'false'
                    })
                self.para(be(self.schema.description))

                self.header(2, 'Classes')

                self.header(3, 'Entities')

                for cls in sorted(self.schema.classes.values(),
                                  key=lambda c: c.name):
                    ancs = self.ancestors(cls)
                    if 'named thing' in ancs:
                        if not cls.is_a and not cls.mixin and self.is_secondary_ref(
                                cls.name):
                            self.seen_elements.add(cls.name)
                            self.class_hier(cls)

                self.header(3, 'Associations')
                for cls in sorted(self.schema.classes.values(),
                                  key=lambda c: c.name):
                    ancs = self.ancestors(cls)
                    if 'association' in ancs:
                        if not cls.is_a and not cls.mixin and self.is_secondary_ref(
                                cls.name):
                            self.seen_elements.add(cls.name)
                            self.class_hier(cls)

                self.header(3, 'Class Mixins')
                for cls in sorted(self.schema.classes.values(),
                                  key=lambda c: c.name):
                    if cls.mixin and self.is_secondary_ref(cls.name):
                        if cls.name not in self.seen_elements:
                            self.seen_elements.add(cls.name)
                            self.class_hier(cls)

                self.header(3, 'Other Classes')
                for cls in sorted(self.schema.classes.values(),
                                  key=lambda c: c.name):
                    if cls.name not in self.seen_elements:
                        self.seen_elements.add(cls.name)
                        self.class_hier(cls)

                self.header(2, 'Slots')
                self.header(3, 'Predicates')
                for slot in sorted(self.schema.slots.values(),
                                   key=lambda c: c.name):
                    if not slot.alias:
                        if 'related to' in self.ancestors(
                                slot) and not slot.mixin:
                            self.seen_elements.add(slot.name)
                            self.pred_hier(slot)

                self.header(3, 'Node Properties')
                for slot in sorted(self.schema.slots.values(),
                                   key=lambda s: s.name):
                    ancs = self.ancestors(slot)
                    if not slot.alias:
                        if 'node property' in ancs and not slot.mixin:
                            self.seen_elements.add(slot.name)
                            self.pred_hier(slot)

                self.header(3, 'Edge Properties')
                for slot in sorted(self.schema.slots.values(),
                                   key=lambda s: s.name):
                    ancs = self.ancestors(slot)
                    if not slot.alias:
                        if 'association slot' in ancs and not slot.mixin:
                            self.seen_elements.add(slot.name)
                            self.pred_hier(slot)

                self.header(3, 'Slot Mixins')
                for slot in sorted(self.schema.slots.values(),
                                   key=lambda s: s.name):
                    if not slot.alias:
                        if slot.mixin:
                            self.seen_elements.add(slot.name)
                            self.pred_hier(slot)

                self.header(3, 'Other Slots')
                for slot in sorted(self.schema.slots.values(),
                                   key=lambda s: s.name):
                    if not slot.alias:
                        if slot.name not in self.seen_elements:
                            self.seen_elements.add(slot.name)
                            self.pred_hier(slot)

                self.header(2, 'Types')
                self.header(3, 'Built in')
                for builtin_name in sorted(self.synopsis.typebases.keys()):
                    self.bullet(f'**{builtin_name}**')

                self.header(3, 'Defined')
                for typ in sorted(self.schema.types.values(),
                                  key=lambda t: t.name):
                    if self.is_secondary_ref(typ.name):
                        if typ.typeof:
                            typ_typ = self.type_link(typ.typeof)
                        else:
                            typ_typ = f'**{typ.base}**'

                        self.bullet(
                            self.type_link(typ,
                                           after_link=f' ({typ_typ})',
                                           use_desc=True))

        # create parent for organizing markdown based on Class types
        with open(os.path.join(directory, 'classes.md'), 'w') as file:
            file.write(
                f'---\nparent: {self.doc_root_title}\ntitle: Classes\nhas_children: true\nnav_order: 1\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'entities.md'), 'w') as file:
            file.write(
                f'---\nparent: Classes\ngrand_parent: {self.doc_root_title}\ntitle: Entities\nhas_children: true\nnav_order: 1\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'associations.md'), 'w') as file:
            file.write(
                f'---\nparent: Classes\ngrand_parent: {self.doc_root_title}\ntitle: Associations\nhas_children: true\nnav_order: 2\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'class_mixins.md'), 'w') as file:
            file.write(
                f'---\nparent: Classes\ngrand_parent: {self.doc_root_title}\ntitle: Class Mixins\nhas_children: true\nnav_order: 3\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'other_classes.md'), 'w') as file:
            file.write(
                f'---\nparent: Classes\ngrand_parent: {self.doc_root_title}\ntitle: Other Classes\nhas_children: true\nnav_order: 4\nlayout: default\n---'
            )

        # create parent for organizing markdown based on Slot types
        with open(os.path.join(directory, 'slots.md'), 'w') as file:
            file.write(
                f'---\nparent: {self.doc_root_title}\ntitle: Slots\nhas_children: true\nnav_order: 2\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'predicates.md'), 'w') as file:
            file.write(
                f'---\nparent: Slots\n\ngrand_parent: {self.doc_root_title}\ntitle: Predicates\nhas_children: true\nnav_order: 1\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'node_properties.md'), 'w') as file:
            file.write(
                f'---\nparent: Slots\ngrand_parent: {self.doc_root_title}\ntitle: Node Properties\nhas_children: true\nnav_order: 2\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'edge_properties.md'), 'w') as file:
            file.write(
                f'---\nparent: Slots\ngrand_parent: {self.doc_root_title}\ntitle: Edge Properties\nhas_children: true\nnav_order: 3\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'slot_mixins.md'), 'w') as file:
            file.write(
                f'---\nparent: Slots\ngrand_parent: {self.doc_root_title}\ntitle: Slot Mixins\nhas_children: true\nnav_order: 4\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'other_slots.md'), 'w') as file:
            file.write(
                f'---\nparent: Slots\ngrand_parent: {self.doc_root_title}\ntitle: Other Slots\nhas_children: true\nnav_order: 5\nlayout: default\n---'
            )

        # create parent for organizing markdown based on Type types
        os.makedirs(os.path.join(directory, 'types'), exist_ok=True)
        with open(os.path.join(directory, 'types', 'index.md'), 'w') as file:
            file.write(
                f'---\nparent: {self.doc_root_title}\ntitle: Types\nhas_children: true\nnav_order: 3\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'types', 'built_in_types.md'),
                  'w') as file:
            file.write(
                f'---\nparent: Types\ngrand_parent: {self.doc_root_title}\ntitle: Built-in Types\nhas_children: true\nnav_order: 1\nlayout: default\n---'
            )
        with open(os.path.join(directory, 'types', 'defined_types.md'),
                  'w') as file:
            file.write(
                f'---\nparent: Types\ngrand_parent: {self.doc_root_title}\ntitle: Defined Types\nhas_children: true\nnav_order: 2\nlayout: default\n---'
            )
Пример #13
0
 def gen_enum_comment(self, enum: EnumDefinition) -> str:
     return f'"""\n\t{wrapped_annotation(be(enum.description))}\n\t"""' if be(
         enum.description) else ''
Пример #14
0
    def gen_classdef(self, cls: ClassDefinition) -> str:
        """ Generate python definition for class cls """

        parentref = f'({self.formatted_element_name(cls.is_a, True) if cls.is_a else "YAMLRoot"})'
        slotdefs = self.gen_class_variables(cls)
        postinits = self.gen_postinits(cls)

        wrapped_description = f'\n\t"""\n\t{wrapped_annotation(be(cls.description))}\n\t"""' if be(
            cls.description) else ''

        return ('\n@dataclass' if slotdefs else '') + \
               f'\nclass {self.class_or_type_name(cls.name)}{parentref}:{wrapped_description}' + \
               f'{self.gen_inherited_slots(cls)}' + \
               f'{self.gen_class_meta(cls)}' + \
               (f'\n\t{slotdefs}' if slotdefs else '') + \
               (f'\n{postinits}' if postinits else '')