Example #1
0
    def visit_type(self, typ: TypeDefinition) -> None:
        """
        Visit a given type definition and write the following properties in Markdown,
        - Frontmatter
        - Description
        - Domain and Range constraints
        - Parents
        - Children
        - Used by

        Parameters
        ----------
        typ: linkml_runtime.linkml_model.meta.TypeDefinition
            A TypeDefinition

        """
        with open(self.dir_path(typ), 'w') as typefile:
            with redirect_stdout(typefile):
                full_path = sfx(self.namespaces._base) + (sfx(
                    typ.imported_from) if typ.imported_from else '')
                type_curie = self.namespaces.uri_or_curie_for(
                    full_path, camelcase(typ.name))
                type_uri = self.namespaces.uri_for(type_curie)

                if type_curie.startswith(
                        'https://w3id.org/biolink/vocab/linkml:types/'):
                    ref = type_curie.split('/')[-1]
                    type_uri = f"https://linkml.github.io/linkml-model/docs/types/{ref}"
                    type_curie = f"metatype:{ref}"
                elif type_uri.startswith('https://w3id.org/biolink/vocab/'):
                    ref = type_curie.split('/')[-1]
                    type_uri = f"https://w3id.org/biolink/vocab/types/{ref}"
                if typ.imported_from and 'linkml:types' in typ.imported_from:
                    parent = 'Built-in Types'
                else:
                    parent = 'Defined Types'
                self.frontmatter(
                    **{
                        'parent': parent,
                        'grand_parent': 'Types',
                        'title': type_curie,
                        'layout': 'default'
                    })
                self.element_header(typ, typ.name, type_curie, type_uri)

                print("|  |  |  |")
                print("| --- | --- | --- |")
                if typ.typeof:
                    print(
                        f"| Parent type | | {self.class_type_link(typ.typeof)} |"
                    )
                print(f"| Root (builtin) type | | **{typ.base}** |")
                if typ.repr:
                    print(f"| Representation | | {typ.repr} |")
Example #2
0
 def dir_path(
     self, obj: Union[ClassDefinition, SlotDefinition, TypeDefinition,
                      EnumDefinition]
 ) -> str:
     filename = self.formatted_element_name(obj) if isinstance(obj, ClassDefinition) \
         else underscore(obj.name) if isinstance(obj, SlotDefinition) \
         else underscore(obj.name) if isinstance(obj, EnumDefinition) \
         else camelcase(obj.name)
     subdir = '/types' if isinstance(
         obj, TypeDefinition) and not self.no_types_dir else ''
     return f'{self.directory}{subdir}/{filename}.md'
Example #3
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_name = 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)
        elif isinstance(obj, SubsetDefinition):
            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 '')
Example #4
0
    def visit_class(self, cls: ClassDefinition) -> bool:
        """
        Visit a given class definition and write the following properties in Markdown,
        - Frontmatter
        - Mappings
        - Description
        - UML
        - Identifier prefixes
        - Parents
        - Uses Mixins
        - Children
        - Mixin for
        - Referenced by class
        - Attributes
        - Domain constraints for slots

        Parameters
        ----------
        cls: linkml_runtime.linkml_model.meta.ClassDefinition
            A ClassDefinition

        Returns
        -------
        bool

        """
        if self.gen_classes and cls.name not in self.gen_classes:
            return False
        with open(self.dir_path(cls), 'w') as clsfile:
            with redirect_stdout(clsfile):
                class_curi = self.namespaces.uri_or_curie_for(
                    self.namespaces._base, camelcase(cls.name))
                class_uri = self.namespaces.uri_for(class_curi)
                ancs = self.ancestors(cls)
                if 'named thing' in ancs:
                    parent = 'Entities'
                    grand_parent = 'Classes'
                elif 'association' in ancs:
                    parent = 'Associations'
                    grand_parent = 'Classes'
                elif cls.mixin:
                    parent = 'Class Mixins'
                    grand_parent = 'Classes'
                else:
                    parent = 'Other Classes'
                    grand_parent = 'Classes'
                self.frontmatter(
                    **{
                        'parent': parent,
                        'title': class_curi,
                        'grand_parent': grand_parent,
                        'layout': 'default'
                    })
                self.element_header(cls, cls.name, class_curi, class_uri)
                for m in cls.mappings:
                    self.badges(m, 'mapping-label')

                if self.image_directory:
                    yg = YumlGenerator(self)
                    yg.serialize(classes=[cls.name],
                                 directory=self.image_directory,
                                 load_image=not self.noimages)
                    img_url = os.path.join(
                        'images', os.path.basename(yg.output_file_name))
                else:
                    yg = YumlGenerator(self)
                    img_url = yg.serialize(classes=[cls.name])

                img_url = img_url.replace(' ', '%20')
                img_url = img_url.replace('<', '%3C')
                img_url = img_url.replace('^', '%5E')
                img_url = img_url.replace('>', '%3E')
                img_url = img_url.replace('|', '%7C')
                img_url = img_url.replace('*', '%2A')
                img_url = img_url.replace('&#124;', '%7C')

                self.horizontal_line()
                print(f'![img]({img_url})')
                self.horizontal_line()
                self.mappings(cls)

                if cls.id_prefixes:
                    self.header(2, 'Identifier prefixes')
                    for p in cls.id_prefixes:
                        self.bullet(f'{p}')

                if cls.is_a is not None:
                    self.header(2, 'Parents')
                    self.bullet(
                        f' is_a: {self.class_link(cls.is_a, use_desc=True)}')
                if cls.mixins:
                    self.header(2, 'Uses Mixins')
                    for mixin in cls.mixins:
                        self.bullet(
                            f' mixin: {self.class_link(mixin, use_desc=True)}')

                if cls.name in self.synopsis.isarefs:
                    self.header(2, 'Children')
                    for child in sorted(
                            self.synopsis.isarefs[cls.name].classrefs):
                        self.bullet(f'{self.class_link(child, use_desc=True)}')

                if cls.name in self.synopsis.mixinrefs:
                    self.header(2, 'Mixin for')
                    for mixin in sorted(
                            self.synopsis.mixinrefs[cls.name].classrefs):
                        self.bullet(
                            f'{self.class_link(mixin, use_desc=True, after_link="(mixin)")}'
                        )

                if cls.name in self.synopsis.classrefs:
                    self.header(2, 'Referenced by class')
                    for sn in sorted(
                            self.synopsis.classrefs[cls.name].slotrefs):
                        slot = self.schema.slots[sn]
                        if slot.range == cls.name:
                            if slot.alias and slot.usage_slot_name:
                                original_slot = self.schema.slots[
                                    slot.usage_slot_name]
                            else:
                                original_slot = slot
                            self.bullet(
                                f' **{self.class_link(slot.domain)}** '
                                f'*{self.slot_link(original_slot, add_subset=False)}*{self.predicate_cardinality(slot)}  '
                                f'**{self.class_type_link(slot.range)}**')

                self.header(2, 'Attributes')
                own_slots = [
                    slot for slot in
                    [self.schema.slots[sn] for sn in sorted(cls.slots)]
                    if slot.owner == cls.name
                ]
                if own_slots:
                    self.header(3, 'Own')
                    for slot in own_slots:
                        if slot.alias and slot.usage_slot_name:
                            slot = self.schema.slots[slot.usage_slot_name]
                        self.slot_field(cls, slot)

                for slot_owner in sorted({
                        slot.owner
                        for slot in
                    [self.schema.slots[sn] for sn in cls.slots]
                        if slot.owner != slot.name and slot.owner != cls.name
                }):
                    self.header(3, "Inherited from " + slot_owner + ':')
                    for owner_slot_name in self.schema.classes[
                            slot_owner].slots:
                        owner_slot = self.schema.slots[owner_slot_name]
                        if owner_slot.owner == slot_owner:
                            if owner_slot.alias and owner_slot.usage_slot_name:
                                owner_slot = self.schema.slots[
                                    owner_slot.usage_slot_name]
                            self.slot_field(cls, owner_slot)

                domain_for_slots = [
                    slot for slot in
                    [self.schema.slots[sn] for sn in sorted(cls.slots)]
                    if slot.domain == cls.name
                ]
                if domain_for_slots:
                    self.header(3, 'Domain for slot:')
                    for slot in domain_for_slots:
                        if slot.alias and slot.usage_slot_name:
                            slot = self.schema.slots[slot.usage_slot_name]
                        self.slot_field(cls, slot)

                self.element_properties(cls)

        return True
Example #5
0
    def visit_class(self, cls: ClassDefinition) -> bool:

        # allow client to relabel metamodel
        mixin_local_name = self.get_metamodel_slot_name('Mixin')
        class_local_name = self.get_metamodel_slot_name('Class')

        if self.gen_classes and cls.name not in self.gen_classes:
            return False

        with open(self.exist_warning(self.dir_path(cls)), 'w') as clsfile:
            with redirect_stdout(clsfile):
                class_curi = self.namespaces.uri_or_curie_for(
                    self.namespaces._base, camelcase(cls.name))
                class_uri = self.namespaces.uri_for(class_curi)
                self.element_header(cls, cls.name, class_curi, class_uri)
                print()
                if not self.noyuml:
                    if self.image_directory:
                        yg = YumlGenerator(self)
                        yg.serialize(classes=[cls.name],
                                     directory=self.image_directory,
                                     load_image=not self.noimages)
                        img_url = os.path.join(
                            'images', os.path.basename(yg.output_file_name))
                    else:
                        yg = YumlGenerator(self)
                        img_url = yg.serialize(classes=[cls.name])\
                            .replace('?', '%3F').replace(' ', '%20').replace('|', '&#124;')

                    print(f'[![img]({img_url})]({img_url})')

                self.mappings(cls)

                if cls.id_prefixes:
                    self.header(2, 'Identifier prefixes')
                    for p in cls.id_prefixes:
                        self.bullet(f'{p}')

                if cls.is_a is not None:
                    self.header(2, 'Parents')
                    self.bullet(
                        f' is_a: {self.class_link(cls.is_a, use_desc=True)}')
                if cls.mixins:
                    self.header(2, f'Uses {mixin_local_name}')
                    for mixin in cls.mixins:
                        self.bullet(
                            f' mixin: {self.class_link(mixin, use_desc=True)}')

                if cls.name in self.synopsis.isarefs:
                    self.header(2, 'Children')
                    for child in sorted(
                            self.synopsis.isarefs[cls.name].classrefs):
                        self.bullet(f'{self.class_link(child, use_desc=True)}')

                if cls.name in self.synopsis.mixinrefs:
                    self.header(2, f'{mixin_local_name} for')
                    for mixin in sorted(
                            self.synopsis.mixinrefs[cls.name].classrefs):
                        self.bullet(
                            f'{self.class_link(mixin, use_desc=True, after_link="(mixin)")}'
                        )

                if cls.name in self.synopsis.classrefs:
                    self.header(2, f'Referenced by {class_local_name}')
                    for sn in sorted(
                            self.synopsis.classrefs[cls.name].slotrefs):
                        slot = self.schema.slots[sn]
                        if slot.range == cls.name:
                            self.bullet(
                                f' **{self.class_link(slot.domain)}** ➞'
                                f'*{self.slot_link(slot, add_subset=False)}*{self.predicate_cardinality(slot)}  '
                                f'**{self.class_type_link(slot.range)}**')

                self.header(2, 'Attributes')

                # List all of the slots that directly belong to the class
                slot_list = [
                    slot
                    for slot in [self.schema.slots[sn] for sn in cls.slots]
                ]
                own_slots = [
                    slot for slot in slot_list if cls.name in slot.domain_of
                ]
                if own_slots:
                    self.header(3, 'Own')
                    for slot in own_slots:
                        self.slot_field(cls, slot)
                        slot_list.remove(slot)

                # List all of the inherited slots
                ancestors = set(self.ancestors(cls))
                inherited_slots = [
                    slot for slot in slot_list
                    if set(slot.domain_of).intersection(ancestors)
                ]
                if inherited_slots:
                    self.header(3, "Inherited from " + cls.is_a + ':')
                    for inherited_slot in inherited_slots:
                        self.slot_field(cls, inherited_slot)
                        slot_list.remove(inherited_slot)

                # List all of the slots acquired through mixing
                mixed_in_classes = set()
                for mixin in cls.mixins:
                    mixed_in_classes.add(mixin)
                    mixed_in_classes.update(
                        set(self.ancestors(self.schema.classes[mixin])))
                for slot in slot_list:
                    mixers = set(slot.domain_of).intersection(mixed_in_classes)
                    for mixer in mixers:
                        self.header(3, "Mixed in from " + mixer + ':')
                        self.slot_field(cls, slot)

                self.element_properties(cls)

        return False