def test_domain_slots(self): """ has_phenotype shouldn't appear in the UML graph """ yuml = YumlGenerator(env.input_path('issue_12.yaml')).serialize() self.assertEqual('http://yuml.me/diagram/nofunky;dir:TB/class/[BiologicalEntity]++- ' 'required thing 0..1>[PhenotypicFeature],[BiologicalEntity]', yuml) resp = requests.get(yuml) self.assertTrue(resp.ok)
def test_prefix(self): env.generate_single_file( 'issue112.yuml', lambda: YumlGenerator(env.input_path('issue_112.yaml')).serialize( ), value_is_returned=True) with open(env.expected_path('issue112.yuml')) as f: url = f.read() resp = requests.get(url) self.assertTrue(resp.ok)
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: 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 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})') 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: 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 sorted(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