Source code for apis_core.apis_vocabularies.models

import inspect
import re
import sys
import unicodedata
import yaml

from django.contrib.auth.models import User
from django.db import models
from django.utils.functional import cached_property
from reversion import revisions as reversion
from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation


[docs]@reversion.register() class VocabNames(models.Model): """List of Vocabulary names to allow the easy retrieval\ of Vovcabulary names and classes from the VocabsBaseClass""" name = models.CharField(max_length=255) def get_vocab_label(self): return re.sub(r"([A-Z])", r" \1", self.name).strip()
[docs]@reversion.register() class VocabsBaseClass(models.Model): """ An abstract base class for other classes which contain so called 'controlled vocabulary' to describe subtypes of main temporalized entites""" choices_status = ( ('rej', 'rejected'), ('ac', 'accepted'), ('can', 'candidate'), ('del', 'deleted') ) name = models.CharField(max_length=255, verbose_name='Name') description = models.TextField( blank=True, help_text="Brief description of the used term.") parent_class = models.ForeignKey( 'self', blank=True, null=True, on_delete=models.CASCADE ) status = models.CharField(max_length=4, choices=choices_status, default='can') userAdded = models.ForeignKey( User, blank=True, null=True, on_delete=models.SET_NULL ) vocab_name = models.ForeignKey( VocabNames, blank=True, null=True, on_delete=models.SET_NULL ) if 'apis_highlighter' in settings.INSTALLED_APPS: from apis_highlighter.models import Annotation annotation_set = GenericRelation(Annotation) def __str__(self): return self.label
[docs] def save(self, *args, **kwargs): d, created = VocabNames.objects.get_or_create(name=type(self).__name__) self.vocab_name = d if self.name != unicodedata.normalize('NFC', self.name): # secure correct unicode encoding self.name = unicodedata.normalize('NFC', self.name) super(VocabsBaseClass, self).save(*args, **kwargs) return self
@cached_property def label(self): d = self res = self.name while d.parent_class: res = d.parent_class.name + ' >> ' + res d = d.parent_class return res
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class RelationBaseClass(VocabsBaseClass): """ An abstract base class for other classes which contain so called 'controlled vocabulary' to describe the relations between main temporalized entities ('db_')""" name_reverse = models.CharField( max_length=255, verbose_name='Name reverse', help_text='Inverse relation like: "is sub-class of" vs. "is super-class of".', blank=True) def __str__(self): return self.name @cached_property def label_reverse(self): d = self if len(self.name_reverse) < 1: res = '(' + self.name + ')' else: res = self.name_reverse while d.parent_class: try: t = RelationBaseClass.objects.get(pk=d.parent_class.pk).name_reverse if len(t) < 1: t = '(' + d.parent_class.name + ')' except Exception as e: t = '(' + d.parent_class.name + ')' res = t + ' >> ' + res d = d.parent_class return res
[docs] def save(self, *args, **kwargs): if self.name_reverse != unicodedata.normalize('NFC', self.name_reverse): self.name_reverse = unicodedata.normalize('NFC', self.name_reverse) if self.name_reverse == "" or self.name_reverse == None: self.name_reverse = self.name + " [REVERSE]" super(RelationBaseClass, self).save(*args, **kwargs) return self
[docs]@reversion.register() class VocabsUri(models.Model): """Class to store URIs for imported types. URI class from metainfo is not used in order to keep the vocabularies module/app seperated from the rest of the application. """ uri = models.URLField() domain = models.CharField(max_length=255, blank=True) rdf_link = models.URLField(blank=True) vocab = models.ForeignKey(VocabsBaseClass, blank=True, null=True, on_delete=models.CASCADE) # loaded: set to True when RDF was loaded and parsed into the data model loaded = models.BooleanField(default=False) # loaded_time: Timestamp when file was loaded and parsed loaded_time = models.DateTimeField(blank=True, null=True) def __str__(self): return self.uri
####################################################################### # # entity types # #######################################################################
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class WorkType(VocabsBaseClass): """Holds controlled vocabularies about work-types""" pass
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class Title(VocabsBaseClass): """A person´s (academic) title""" abbreviation = models.CharField(max_length=10, blank=True)
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class ProfessionType(VocabsBaseClass): """Holds controlled vocabularies about profession-types""" pass
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class PlaceType(VocabsBaseClass): """Holds controlled vocabularies about place-types""" pass
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class InstitutionType(VocabsBaseClass): """Holds controlled vocabularies about institution-types""" pass
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class EventType(VocabsBaseClass): """Holds controlled vocabularies about event-types""" pass
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class LabelType(VocabsBaseClass): """Holds controlled vocabularies about label-types""" pass
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class CollectionType(VocabsBaseClass): """e.g. reseachCollection, importCollection """ pass
[docs]@reversion.register(follow=['vocabsbaseclass_ptr']) class TextType(VocabsBaseClass): """used to store the Text types for the forms""" entity = models.CharField(max_length=255) collections = models.ManyToManyField('apis_metainfo.Collection', blank=True) lang = models.CharField( max_length=3, blank=True, null=True, help_text="The ISO 639-3 (or 2) code for the label's language.", verbose_name='ISO Code', default='deu')
####################################################################### # # relation types # #######################################################################
[docs]class AbstractRelationType(RelationBaseClass): """ Abstract super class which encapsulates common logic between the different relationtypes and provides various methods relating to either all or a specific relationtypes. """ class Meta: abstract = True _all_relationtype_classes = None _all_relationtype_names = None _related_entity_field_names = None # Methods dealing with all relationtypes ####################################################################################################################
[docs] @classmethod def get_all_relationtype_classes(cls): """ :return: list of all python classes of the relationtypes defined within this models' module """ if cls._all_relationtype_classes == None: relationtype_classes = [] relationtype_names = [] for relationtype_name, relationtype_class in inspect.getmembers( sys.modules[__name__], inspect.isclass): if relationtype_class.__module__ == "apis_core.apis_vocabularies.models" and \ relationtype_name != "ent_class" and \ relationtype_name.endswith("Relation"): relationtype_classes.append(relationtype_class) relationtype_names.append(relationtype_name.lower()) cls._all_relationtype_classes = relationtype_classes cls._all_relationtype_names = relationtype_names return cls._all_relationtype_classes
[docs] @classmethod def get_relationtype_class_of_name(cls, relationtype_name): """ :param entity_name: str : The name of an relationtype :return: The model class of the relationtype respective to the given name """ for relationtype_class in cls.get_all_relationtype_classes(): if relationtype_class.__name__.lower() == relationtype_name.lower(): return relationtype_class raise Exception("Could not find relationtype class of name:", relationtype_name)
[docs] @classmethod def get_all_relationtype_names(cls): """ :return: list of all class names in lower case of the relationtypes defined within this models' module """ if cls._all_relationtype_names == None: cls.get_all_relationtype_classes() return cls._all_relationtype_names
# Methods dealing with related entities ####################################################################################################################
####################################################################### # Person-Relation-Types #######################################################################
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class PersonPersonRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Persons and Persons""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class PersonPlaceRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Persons and Places""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class PersonInstitutionRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Persons and Persons""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class PersonEventRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Persons and Events""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class PersonWorkRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Persons and Works""" pass
####################################################################### # Institution-Relation-Types #######################################################################
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class InstitutionEventRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Institutions and Events.""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class InstitutionPlaceRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Institutions and Places.""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class InstitutionInstitutionRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Institutions and Institutions.""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class InstitutionWorkRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Institutions and Works.""" pass
####################################################################### # Place-Relation-Types #######################################################################
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class PlacePlaceRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Places and Places""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class PlaceEventRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Places and Events""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class PlaceWorkRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Places and Works""" pass
####################################################################### # Event-Relation-Types #######################################################################
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class EventEventRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Events and Events""" pass
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class EventWorkRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Events and Works""" pass
####################################################################### # Work-Relation-Types #######################################################################
[docs]@reversion.register(follow=['relationbaseclass_ptr']) class WorkWorkRelation(AbstractRelationType): """Holds controlled vocabularies relation types of Works and Works""" pass
a_ents = getattr(settings, 'APIS_ADDITIONAL_ENTITIES', False) if a_ents: with open(a_ents, 'r') as ents_file: ents = yaml.load(ents_file, Loader=yaml.CLoader) print(ents) for ent in ents['entities']: for voc in ent.get('vocabs', []): attributes = {"__module__": __name__} ent_class = type(voc, (VocabsBaseClass,), attributes) globals()[ent['name']] = ent_class rels = ent.get("relations", []) base_ents = ['Person', 'Institution', 'Place', 'Work', 'Event'] if isinstance(rels, str): if rels == 'all': rels = base_ents + [x['name'].title() for x in ents['entities']] else: rels = base_ents + rels for r2 in rels: attributes = {"__module__":__name__} if r2 in base_ents: rel_class_name = f"{r2.title()}{ent['name'].title()}" else: rel_class_name = f"{ent['name'].title()}{r2.title()}" attributes = {"__module__": __name__} if f"{rel_class_name}Relation" not in globals().keys(): ent_class = type(f"{rel_class_name}Relation", (AbstractRelationType,), attributes) globals()[f"{rel_class_name}Relation"] = ent_class