Coverage for apis_core/entities/abc.py: 72%

94 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-28 06:34 +0000

1import functools 

2 

3from django.db import models 

4from django.urls import reverse 

5from django.utils.encoding import force_str 

6from django.utils.translation import gettext_lazy as _ 

7 

8from apis_core.entities.rdfconfigs import group, person, place 

9from apis_core.generic.abc import GenericModel, SimpleLabelModel 

10from apis_core.generic.utils.rdf_namespace import CRM 

11from apis_core.utils.rdf import load_uri_using_path 

12 

13 

14class Entity(GenericModel): 

15 class Meta: 

16 abstract = True 

17 

18 class Config(GenericModel.Config): 

19 overview_section = _("Entities") 

20 

21 @property 

22 def get_canonical_url(self): 

23 return reverse("apis_core:canonical-entity", args=[self.pk]) 

24 

25 @functools.cached_property 

26 def get_prev_id(self): 

27 prev_instance = ( 

28 type(self).objects.filter(id__lt=self.id).order_by("-id").only("id").first() 

29 ) 

30 if prev_instance is not None: 

31 return prev_instance.id 

32 return False 

33 

34 @functools.cached_property 

35 def get_next_id(self): 

36 next_instance = ( 

37 type(self).objects.filter(id__gt=self.id).order_by("id").only("id").first() 

38 ) 

39 if next_instance is not None: 

40 return next_instance.id 

41 return False 

42 

43 def import_data(self, data): 

44 super().import_data(data) 

45 if "same_as" in data: 

46 self._uris = data["same_as"] 

47 self.save() 

48 if "relations" in data: 

49 self.create_relations_to_uris = data["relations"] 

50 self.save() 

51 

52 

53######################### 

54# Abstract base classes # 

55######################### 

56 

57 

58# These abstract base classes are named after 

59# CIDOC CRM entities, but we are *NOT*(!) 

60# trying to implement CIDOC CRM in Django. 

61 

62 

63class E21_Person(Entity): 

64 forename = models.CharField( 

65 blank=True, default="", max_length=4096, verbose_name=_("forename") 

66 ) 

67 surname = models.CharField( 

68 blank=True, default="", max_length=4096, verbose_name=_("surname") 

69 ) 

70 gender = models.CharField( 

71 blank=True, default="", max_length=4096, verbose_name=_("gender") 

72 ) 

73 date_of_birth = models.DateField( 

74 blank=True, null=True, verbose_name=_("date of birth") 

75 ) 

76 date_of_death = models.DateField( 

77 blank=True, null=True, verbose_name=_("date of death") 

78 ) 

79 

80 class Meta: 

81 abstract = True 

82 verbose_name = _("person") 

83 verbose_name_plural = _("persons") 

84 ordering = ["surname", "forename"] 

85 

86 def __str__(self): 

87 if self.forename and self.surname: 

88 return f"{self.forename} {self.surname}" 

89 return self.forename or self.surname or force_str(_("No name")) 

90 

91 import_definitions = { 

92 "https://d-nb.info/*|/.*.rdf": lambda x: load_uri_using_path( 

93 x, person.E21_PersonFromDNB 

94 ), 

95 "http://www.wikidata.org/*|/.*.rdf": lambda x: load_uri_using_path( 

96 x, person.E21_PersonFromWikidata 

97 ), 

98 } 

99 

100 @classmethod 

101 def get_rdf_types(cls): 

102 return [CRM.E21_Person] 

103 

104 

105class E53_Place(Entity): 

106 """ 

107 The feature_code field refers to the geonames feature codes, as 

108 listed on https://www.geonames.org/export/codes.html 

109 """ 

110 

111 label = models.CharField( 

112 blank=True, default="", max_length=4096, verbose_name=_("label") 

113 ) 

114 latitude = models.FloatField(blank=True, null=True, verbose_name=_("latitude")) 

115 longitude = models.FloatField(blank=True, null=True, verbose_name=_("longitude")) 

116 feature_code = models.CharField( 

117 blank=True, 

118 default="", 

119 max_length=16, 

120 verbose_name=_("feature code"), 

121 help_text='<a href="https://www.geonames.org/export/codes.html" target="_blank">Geonames Feature Code List</a>', 

122 ) 

123 

124 class Meta: 

125 abstract = True 

126 verbose_name = _("place") 

127 verbose_name_plural = _("places") 

128 ordering = ["label"] 

129 

130 def __str__(self): 

131 return self.label or force_str(_("No label")) 

132 

133 import_definitions = { 

134 "https://d-nb.info/*|/.*.rdf": lambda x: load_uri_using_path( 

135 x, place.E53_PlaceFromDNB 

136 ), 

137 "https://sws.geonames.org/*|/.*.rdf*": lambda x: load_uri_using_path( 

138 x, place.E53_PlaceFromGeonames 

139 ), 

140 "http://www.wikidata.org/*|/.*.rdf": lambda x: load_uri_using_path( 

141 x, place.E53_PlaceFromWikidata 

142 ), 

143 } 

144 

145 @classmethod 

146 def get_rdf_types(cls): 

147 return [CRM.E53_Place] 

148 

149 @classmethod 

150 def create_from_string(cls, string): 

151 return cls.objects.create(label=string) 

152 

153 

154class E74_Group(Entity): 

155 label = models.CharField( 

156 blank=True, default="", max_length=4096, verbose_name=_("label") 

157 ) 

158 

159 class Meta: 

160 abstract = True 

161 verbose_name = _("group") 

162 verbose_name_plural = _("groups") 

163 ordering = ["label"] 

164 

165 def __str__(self): 

166 return self.label or force_str(_("No label")) 

167 

168 import_definitions = { 

169 "https://d-nb.info/*|/.*.rdf": lambda x: load_uri_using_path( 

170 x, group.E74_GroupFromDNB 

171 ), 

172 "http://www.wikidata.org/*|/.*.rdf": lambda x: load_uri_using_path( 

173 x, group.E74_GroupFromWikidata 

174 ), 

175 } 

176 

177 @classmethod 

178 def get_rdf_types(cls): 

179 return [CRM.E74_Group] 

180 

181 @classmethod 

182 def create_from_string(cls, string): 

183 return cls.objects.create(label=string) 

184 

185 

186class SimpleLabelEntity(SimpleLabelModel, Entity): 

187 class Meta(SimpleLabelModel.Meta): 

188 abstract = True