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} |")
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'
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 '')
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('|', '%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
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('|', '|') 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