def _visit(self, node: Any) -> Optional[Any]: if isinstance(node, (YAMLRoot, dict)): if isinstance(node, YAMLRoot): node = node.__dict__ for k, v in list(node.items()): if v: new_v = self._visit(v) if new_v is not None: node[k] = new_v elif isinstance(node, list): for i in range(0, len(node)): new_v = self._visit(node[i]) if new_v is not None: node[i] = new_v elif isinstance(node, set): for v in list(node): new_v = self._visit(v) if new_v is not None: node.remove(v) node.add(new_v) elif isinstance(node, ClassDefinitionName): return ClassDefinitionName(camelcase(node)) elif isinstance(node, SlotDefinitionName): return SlotDefinitionName(underscore(node)) elif isinstance(node, TypeDefinitionName): return TypeDefinitionName(underscore(node)) elif isinstance(node, ElementName): return ClassDefinitionName(camelcase(node)) if node in self.schema.classes else \ SlotDefinitionName(underscore(node)) if node in self.schema.slots else \ TypeDefinitionName(underscore(node)) if node in self.schema.types else \ builtin_uri(str(node)) if str(node) in builtin_names else None elif str(node) in builtin_names: return builtin_uri(str(node)) return None
def adjust_slot(self, slot: SlotDefinition) -> None: if slot.range in self.schema.classes: slot.range = ClassDefinitionName(camelcase(slot.range)) elif slot.range in self.schema.slots: slot.range = SlotDefinitionName(underscore(slot.range)) elif slot.range in self.schema.types: slot.range = TypeDefinitionName(underscore(slot.range)) elif slot.range in (Builtin.uri, Builtin.anytype): slot.range = '@id' elif slot.range in builtin_names and builtin_names[slot.range] not in (Builtin.anytype, Builtin.uri): slot.range = builtin_uri(slot.range)
def visit_class_slot(self, cls: ClassDefinition, aliased_slot_name: str, slot: SlotDefinition) -> None: if self.inline: # If inline we have to include redefined slots prop = JsonObj(type=JsonObj()) prop.type['$ref'] = self.jref(underscore(slot.name)) elif slot.multivalued: prop = JsonObj(type="array", items=self.type_or_ref(slot.range)) else: prop = JsonObj(type="string") if slot.description: prop.description = slot.description self.clsobj.properties[underscore(aliased_slot_name)] = prop
def link(self, ref: Optional[Union[str, 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: """ obj = self.obj_for(ref) if isinstance(ref, str) else ref nl = '\n' if isinstance( obj, str) or obj is None or not self.is_secondary_ref(obj.name): return self.bbin(ref) if isinstance(obj, SlotDefinition): link_name = ((be(obj.domain) + '.') if obj.alias else '') + self.aliased_slot_name(obj) link_ref = underscore(obj.name) else: link_name = self.obj_name(obj) link_ref = link_name desc = self.desc_for(obj, use_desc) return f'[{link_name}]' \ f'({link_ref}.{self.format})' + \ (f' *subsets*: ({"| ".join(obj.in_subset)})' if add_subset and obj.in_subset else '') + \ (f' {after_link} ' if after_link else '') + (f' - {desc.split(nl)[0]}' if desc else '')
def _default(self, obj): """ JSON serializer callback. 1) Filter out empty values (None, {}, [] and False) and mangle the names 2) Add ID entries for dictionary entries :param obj: YAMLRoot object to serialize :return: Serialized version of obj """ from metamodel.metamodel import ClassDefinition, SlotDefinition, TypeDefinition if isinstance(obj, JsonObj): rval = dict() for k, v in obj.__dict__.items(): if not k.startswith('_') and v is not None and ( not isinstance(v, (dict, list, bool)) or v): if isinstance(v, dict): itemslist = [] for vk, vv in v.items(): if isinstance(vv, ClassDefinition): vv['@id'] = camelcase(vk) elif isinstance(vv, (SlotDefinition, TypeDefinition)): if k != 'slot_usage': vv['@id'] = underscore(vk) itemslist.append(vv) rval[k] = itemslist else: rval[k] = v return rval else: return super()._default(obj)
def visit_slot(self, aliased_slot_name: str, slot: SlotDefinition) -> None: with open(self.dir_path(slot), 'w') as slotfile: with redirect_stdout(slotfile): self.frontmatter(f"Slot: {aliased_slot_name}") self.para(be(slot.description)) slot_uri = BIOENTITY[underscore(slot.name)] print(f'URI: [{slot_uri}](slot_uri)') self.mappings(slot) self.header(2, 'Domain and Range') print(f'{self.link(slot.domain)} -> {self.link(slot.range)}') self.header(2, 'Inheritance') if slot.is_a: self.bullet(f' is_a: {self.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): self.bullet(f' child: {self.link(child)}') self.header(2, 'Used in') if slot.name in sorted(self.synopsis.slotrefs): for rc in sorted( self.synopsis.slotrefs[slot.name].classrefs): self.bullet(f' usage: {self.link(rc)}') if aliased_slot_name == 'relation': if slot.subproperty_of: self.bullet( f' reifies: {self.link(slot.subproperty_of)}')
def visit_class(self, cls: ClassDefinition) -> bool: if not self.closure or cls.name in self.closure: self.writer.writerow({'id': underscore(cls.name), 'mappings': "|".join(cls.mappings), 'description': be(cls.description)}) return True return False
def _predicate(self, name: SlotDefinitionName) -> IRIREF: slot = self.schema.slots[name] if slot.mappings: return IRIREF(cu.expand_uri(slot.mappings[0])) else: # TODO: look at the RDF to figure out what URI's go here return IRIREF(BIOENTITY[underscore(name)])
def obj_name(self, obj: Union[str, Element]) -> str: """ Return the formatted name used for the supplied definition """ if isinstance(obj, str): obj = self.obj_for(obj) if isinstance(obj, SlotDefinition): return underscore(self.aliased_slot_name(obj)) else: return camelcase(obj if isinstance(obj, str) else obj.name)
def visit_slot(self, slot_name: str, slot: SlotDefinition) -> None: # Don't emit redefined slots unless we are inlining if slot_name == slot.name or self.inline: defn = JsonObj(type="array", items=self.type_or_ref(slot.range)) if slot.multivalued \ else self.type_or_ref(slot.range) if slot.description: defn.description = slot.description self.schemaobj.definitions[underscore(slot.name)] = defn
def visit_class_slot(self, cls: ClassDefinition, aliased_slot_name: str, slot: SlotDefinition) -> None: field = GOLRField(id=underscore(aliased_slot_name), description=slot.description, display_name=slot.name) if slot.multivalued: field.cardinality = 'multi' self.class_obj.fields.append(field)
def end_class(self, cls: ClassDefinition) -> None: if self.cls_subj and self.cls_obj: rnode = 'relation' self.edge(self.aliased_slot_name(self.cls_subj), self.aliased_slot_name(self.cls_obj), label=rnode) self.edge(self.aliased_slot_name(self.cls_subj), rnode, style='dotted') self.edge(self.aliased_slot_name(self.cls_obj), rnode, style='dotted') if self.classdot: self.classdot.format = self.format self.classdot.render(underscore(cls.name), self.dirname, view=False, cleanup=True)
def visit_class(self, cls: ClassDefinition) -> bool: if not cls.abstract: self.class_obj = GOLRClass(id=underscore(cls.name), schema_generating=True, description=cls.description, display_name=cls.name, document_category=cls.name, weight=20) return True else: return False
def class_box(self, cn: ClassDefinitionName) -> str: """ Generate a box for the class. Populate its interior only if (a) it hasn't previously been generated and (b) it appears in the gen_classes list @param cn: @param inherited: @return: """ slot_defs: List[str] = [] if cn not in self.box_generated and (not self.focus_classes or cn in self.focus_classes): cls = self.schema.classes[cn] for slotname in self.filtered_cls_slots(cn, all_slots=True): slot = self.schema.slots[slotname] if not slot.range or slot.range in builtin_names or slot.range in self.schema.types: mod = self.prop_modifier(cls, slot) slot_defs.append( underscore(self.aliased_slot_name(slot)) + mod + ':' + underscore(slot.range) + self.cardinality(slot)) self.box_generated.add(cn) self.referenced.add(cn) return '[' + camelcase(cn) + ('|' + ';'.join(slot_defs) if slot_defs else '') + ']'
def visit_slot(self, aliased_slot_name: str, slot: SlotDefinition) -> None: slot_def = {} sn = underscore(slot.name) if not slot.alias: rng = self.grounded_slot_range(slot) if rng != DEFAULT_BUILTIN_TYPE_NAME: builtin_rng_uri = builtin_uri(rng) slot_def['@type'] = builtin_rng_uri \ if builtin_rng_uri and builtin_names.get(rng, None) not in (Builtin.uri, Builtin.anytype) else "@id" if slot.multivalued: slot_def['@container'] = '@list' self.add_mappings(slot, slot_def) if slot_def: self.prefixmap[sn] = slot_def
def dir_path(self, obj: Union[ClassDefinition, SlotDefinition]) -> str: filename = self.obj_name(obj) if isinstance( obj, ClassDefinition) else underscore(obj.name) return f'{self.directory}/{filename}.md'
def type_uri(tn: TypeDefinitionName) -> URIRef: return BIOENTITY[underscore(tn)]
def prop_uri(pn: SlotDefinitionName) -> URIRef: return BIOENTITY[underscore(pn)]
def python_slot_name() -> str: return underscore(name)
def end_class(self, cls: ClassDefinition) -> None: fn = os.path.join(self.dirname, underscore(cls.name + '-config.yaml')) with open(fn, 'w') as f: f.write(as_yaml(self.class_obj))