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

92 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-07-06 07:19 +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 @property 

19 def get_canonical_url(self): 

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

21 

22 @functools.cached_property 

23 def get_prev_id(self): 

24 prev_instance = ( 

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

26 ) 

27 if prev_instance is not None: 

28 return prev_instance.id 

29 return False 

30 

31 @functools.cached_property 

32 def get_next_id(self): 

33 next_instance = ( 

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

35 ) 

36 if next_instance is not None: 

37 return next_instance.id 

38 return False 

39 

40 def import_data(self, data): 

41 super().import_data(data) 

42 if "same_as" in data: 

43 self._uris = data["same_as"] 

44 self.save() 

45 if "relations" in data: 

46 self.create_relations_to_uris = data["relations"] 

47 self.save() 

48 

49 

50######################### 

51# Abstract base classes # 

52######################### 

53 

54 

55# These abstract base classes are named after 

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

57# trying to implement CIDOC CRM in Django. 

58 

59 

60class E21_Person(Entity): 

61 forename = models.CharField( 

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

63 ) 

64 surname = models.CharField( 

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

66 ) 

67 gender = models.CharField( 

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

69 ) 

70 date_of_birth = models.DateField( 

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

72 ) 

73 date_of_death = models.DateField( 

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

75 ) 

76 

77 class Meta: 

78 abstract = True 

79 verbose_name = _("person") 

80 verbose_name_plural = _("persons") 

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

82 

83 def __str__(self): 

84 if self.forename and self.surname: 

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

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

87 

88 import_definitions = { 

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

90 x, person.E21_PersonFromDNB 

91 ), 

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

93 x, person.E21_PersonFromWikidata 

94 ), 

95 } 

96 

97 @classmethod 

98 def get_rdf_types(cls): 

99 return [CRM.E21_Person] 

100 

101 

102class E53_Place(Entity): 

103 """ 

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

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

106 """ 

107 

108 label = models.CharField( 

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

110 ) 

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

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

113 feature_code = models.CharField( 

114 blank=True, 

115 default="", 

116 max_length=16, 

117 verbose_name=_("feature code"), 

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

119 ) 

120 

121 class Meta: 

122 abstract = True 

123 verbose_name = _("place") 

124 verbose_name_plural = _("places") 

125 ordering = ["label"] 

126 

127 def __str__(self): 

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

129 

130 import_definitions = { 

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

132 x, place.E53_PlaceFromDNB 

133 ), 

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

135 x, place.E53_PlaceFromGeonames 

136 ), 

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

138 x, place.E53_PlaceFromWikidata 

139 ), 

140 } 

141 

142 @classmethod 

143 def get_rdf_types(cls): 

144 return [CRM.E53_Place] 

145 

146 @classmethod 

147 def create_from_string(cls, string): 

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

149 

150 

151class E74_Group(Entity): 

152 label = models.CharField( 

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

154 ) 

155 

156 class Meta: 

157 abstract = True 

158 verbose_name = _("group") 

159 verbose_name_plural = _("groups") 

160 ordering = ["label"] 

161 

162 def __str__(self): 

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

164 

165 import_definitions = { 

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

167 x, group.E74_GroupFromDNB 

168 ), 

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

170 x, group.E74_GroupFromWikidata 

171 ), 

172 } 

173 

174 @classmethod 

175 def get_rdf_types(cls): 

176 return [CRM.E74_Group] 

177 

178 @classmethod 

179 def create_from_string(cls, string): 

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

181 

182 

183class SimpleLabelEntity(SimpleLabelModel, Entity): 

184 class Meta(SimpleLabelModel.Meta): 

185 abstract = True