Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import logging 

2import os 

3from urllib.request import urlopen 

4from django.core.files import File 

5from django.core.files.base import ContentFile 

6from django.core.serializers.json import DjangoJSONEncoder 

7from django.core.validators import FileExtensionValidator 

8from django.db import models 

9from django.utils.timezone import now 

10from django.utils.translation import gettext_lazy as _ 

11from jutil.format import is_media_full_path, strip_media_root, get_media_full_path 

12from jutil.modelfields import SafeCharField, SafeTextField 

13 

14from jsanctions.helpers import get_country_iso2_code 

15 

16logger = logging.getLogger(__name__) 

17 

18 

19REMARK_BRIEF_LENGTH = 128 

20DEFAULT_DESCRIPTION_TYPE = {"blank": True, "max_length": 512, "default": ""} 

21DEFAULT_REMARK_TYPE = { 

22 "verbose_name": _("remark"), 

23 "null": True, 

24 "default": None, 

25 "blank": True, 

26 "on_delete": models.SET_NULL, 

27} 

28DEFAULT_CODE_TYPE = {"blank": True, "max_length": 32, "default": ""} 

29DEFAULT_DATE_TYPE = {"blank": True, "null": True, "default": None} 

30LANGUAGE_CODE_TYPE = {"blank": True, "default": "", "max_length": 5} 

31COUNTRY_CODE_TYPE = {"blank": True, "default": "", "max_length": 3} 

32DEFAULT_BOOLEAN_TYPE = {"blank": True, "default": None, "null": True} 

33DEFAULT_INT_TYPE = {"blank": True, "default": None, "null": True} 

34REGULATION_SUMMARY_TYPE = { 

35 "verbose_name": _("regulation summary"), 

36 "blank": True, 

37 "default": None, 

38 "null": True, 

39 "on_delete": models.PROTECT, 

40} 

41 

42 

43class SanctionsListFileManager(models.Manager): 

44 def create_from_filename(self, filename: str, **kwargs): 

45 full_path = os.path.realpath(filename) 

46 file = self.create(**kwargs) 

47 assert isinstance(file, SanctionsListFile) 

48 if is_media_full_path(full_path): 

49 file.file.name = strip_media_root(full_path) 

50 file.save() 

51 logger.info('%s used as is', file.file) 

52 else: 

53 with open(full_path, "rb") as fp: 

54 plain_filename = os.path.basename(filename) 

55 file.file.save(plain_filename, File(fp)) 

56 logger.info('%s written', file.file) 

57 return file 

58 

59 def create_from_url(self, url: str, filename: str, **kwargs): 

60 response = urlopen(url) 

61 body = response.read() 

62 plain_filename = os.path.basename(filename) 

63 file = self.create(**kwargs) 

64 file.file.save(plain_filename, ContentFile(body)) 

65 logger.info('%s written', file.file) 

66 return file 

67 

68 

69class SanctionListObject(models.Model): 

70 pass 

71 

72 

73class SanctionsListFile(SanctionListObject): 

74 objects = SanctionsListFileManager() # type: ignore 

75 created = models.DateTimeField(verbose_name=_("created"), default=now, blank=True, editable=False, db_index=True) 

76 imported = models.DateTimeField( 

77 verbose_name=_("imported"), default=None, null=True, blank=True, editable=False, db_index=True 

78 ) 

79 generation_date = models.DateField( 

80 verbose_name=_("generation date"), default=None, blank=True, null=True, editable=False, db_index=True 

81 ) 

82 file = models.FileField(verbose_name=_("file"), upload_to="uploads", validators=[FileExtensionValidator(["xml"])]) 

83 list_type = SafeCharField(verbose_name=_("list type"), max_length=128, db_index=True) 

84 global_file_id = SafeCharField(verbose_name=_("global file id"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

85 

86 class Meta: 

87 verbose_name = _("sanction list") 

88 verbose_name_plural = _("sanction lists") 

89 

90 def __str__(self): 

91 return "{}".format(os.path.basename(self.file.name)) 

92 

93 @property 

94 def full_path(self) -> str: 

95 return get_media_full_path(self.file.name) 

96 

97 

98class Remark(models.Model): 

99 container = models.ForeignKey(SanctionListObject, on_delete=models.CASCADE) 

100 text = SafeTextField(verbose_name=_("text"), blank=True) 

101 

102 class Meta: 

103 verbose_name = _("remark") 

104 verbose_name_plural = _("remarks") 

105 

106 def __str__(self) -> str: 

107 return str(self.text) 

108 

109 @property 

110 def text_brief(self) -> str: 

111 return self.text if len(self.text) < REMARK_BRIEF_LENGTH else self.text[:REMARK_BRIEF_LENGTH] + "..." 

112 

113 

114class SubjectType(SanctionListObject): 

115 PERSON = 'P' 

116 ENTERPRISE = 'E' 

117 VESSEL = 'V' 

118 AIRCRAFT = 'A' 

119 CLASSIFICATION_CODES = [ 

120 (PERSON, _('person')), 

121 (ENTERPRISE, _('enterprise')), 

122 (VESSEL, _('vessel')), 

123 (AIRCRAFT, _('aircraft')), 

124 ] 

125 

126 classification_code = SafeCharField(verbose_name=_("classification code"), **DEFAULT_CODE_TYPE) # type: ignore 

127 code = SafeCharField(verbose_name=_("code"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

128 

129 class Meta: 

130 verbose_name = _("subject type") 

131 verbose_name_plural = _("subject types") 

132 

133 def __str__(self) -> str: 

134 return str(self.code) 

135 

136 

137class Regulation(SanctionListObject): 

138 sanction = models.ForeignKey("SanctionEntity", verbose_name=_("sanction entity"), on_delete=models.CASCADE) 

139 regulation_type = SafeCharField(verbose_name=_("regulation type"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

140 organisation_type = SafeCharField(verbose_name=_("organization type"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

141 publication_date = models.DateField(verbose_name=_("publication date"), **DEFAULT_DATE_TYPE) # type: ignore 

142 publication_url = models.URLField(verbose_name=_("url"), blank=True, default="") 

143 entry_into_force_date = models.DateField(verbose_name=_("entry into force date"), **DEFAULT_DATE_TYPE) # type: ignore 

144 number_title = SafeCharField(verbose_name=_("number title"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

145 programme = SafeCharField(verbose_name=_("programmer"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

146 logical_id = models.BigIntegerField(verbose_name=_("logical id"), blank=True, null=True, default=None) 

147 

148 class Meta: 

149 verbose_name = _("regulation") 

150 verbose_name_plural = _("regulations") 

151 

152 

153class RegulationSummary(SanctionListObject): 

154 regulation_type = SafeCharField(verbose_name=_("regulation type"), **DEFAULT_CODE_TYPE) # type: ignore 

155 number_title = SafeCharField(verbose_name=_("number title"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

156 publication_date = models.DateField(verbose_name=_("publication date"), **DEFAULT_DATE_TYPE) # type: ignore 

157 publication_url = models.URLField(verbose_name=_("url"), blank=True, default="") 

158 

159 class Meta: 

160 verbose_name = _("regulation summary") 

161 verbose_name_plural = _("regulation summaries") 

162 

163 def __str__(self) -> str: 

164 return "{} {}".format(self.regulation_type, self.number_title) 

165 

166 

167class NameAlias(SanctionListObject): 

168 sanction = models.ForeignKey("SanctionEntity", verbose_name=_("sanction entity"), on_delete=models.CASCADE) 

169 first_name = SafeCharField(verbose_name=_("first name"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

170 middle_name = SafeCharField(verbose_name=_("middle name"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

171 last_name = SafeCharField(verbose_name=_("last name"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

172 whole_name = SafeCharField(verbose_name=_("whole name"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

173 name_language = SafeCharField(verbose_name=_("name language"), **LANGUAGE_CODE_TYPE) # type: ignore 

174 function = SafeCharField(verbose_name=_("function"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

175 title = SafeCharField(verbose_name=_("title"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

176 regulation_language = SafeCharField(verbose_name=_("regulation language"), **LANGUAGE_CODE_TYPE) # type: ignore 

177 logical_id = models.BigIntegerField(verbose_name=_("logical id"), blank=True, null=True, default=None) 

178 regulation_summary = models.ForeignKey(RegulationSummary, **REGULATION_SUMMARY_TYPE) # type: ignore 

179 

180 class Meta: 

181 verbose_name = _("name alias") 

182 verbose_name_plural = _("name aliases") 

183 

184 def __str__(self) -> str: 

185 return str(self.whole_name) 

186 

187 def clean(self): 

188 if len(self.function) > 256: 

189 self.function = self.function[:256] 

190 if not self.whole_name: 

191 self.whole_name = (self.first_name + ' ' + self.last_name).strip() 

192 

193 

194class Identification(SanctionListObject): 

195 sanction = models.ForeignKey("SanctionEntity", verbose_name=_("sanction entity"), on_delete=models.CASCADE) 

196 diplomatic = models.BooleanField(verbose_name=_("diplomatic"), **DEFAULT_BOOLEAN_TYPE) # type: ignore 

197 known_expired = models.BooleanField(verbose_name=_("known expired"), **DEFAULT_BOOLEAN_TYPE) # type: ignore 

198 known_false = models.BooleanField(verbose_name=_("known false"), **DEFAULT_BOOLEAN_TYPE) # type: ignore 

199 reported_lost = models.BooleanField(verbose_name=_("reported lost"), **DEFAULT_BOOLEAN_TYPE) # type: ignore 

200 revoked_by_issuer = models.BooleanField(verbose_name=_("revoked by issuer"), **DEFAULT_BOOLEAN_TYPE) # type: ignore 

201 issue_date = models.DateField(verbose_name=_("issue date"), **DEFAULT_DATE_TYPE) # type: ignore 

202 issued_by = SafeCharField(verbose_name=_("issued by"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

203 latin_number = SafeCharField(verbose_name=_("latin number"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

204 name_on_document = SafeCharField(verbose_name=_("name on document"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

205 number = SafeCharField(verbose_name=_("number"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

206 region = SafeCharField(verbose_name=_("region"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

207 country_iso2_code = SafeCharField(verbose_name=_("issued by"), **COUNTRY_CODE_TYPE) # type: ignore 

208 country_description = SafeCharField(verbose_name=_("country description"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

209 identification_type_code = SafeCharField(verbose_name=_("identification type code"), **DEFAULT_CODE_TYPE) # type: ignore 

210 identification_type_description = SafeCharField(verbose_name=_("identification type code"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

211 regulation_language = SafeCharField(verbose_name=_("regional language"), **LANGUAGE_CODE_TYPE) # type: ignore 

212 logical_id = models.BigIntegerField(verbose_name=_("logical id"), blank=True, null=True, default=None) 

213 regulation_summary = models.ForeignKey(RegulationSummary, **REGULATION_SUMMARY_TYPE) # type: ignore 

214 

215 class Meta: 

216 verbose_name = _("identification") 

217 verbose_name_plural = _("identifications") 

218 

219 def clean(self): 

220 if self.country_description and not self.country_iso2_code: 

221 self.country_iso2_code = get_country_iso2_code(self.country_description) 

222 

223 

224class BirthDate(SanctionListObject): 

225 sanction = models.ForeignKey("SanctionEntity", verbose_name=_("sanction entity"), on_delete=models.CASCADE) 

226 circa = models.BooleanField(verbose_name=_("circa"), **DEFAULT_BOOLEAN_TYPE) # type: ignore 

227 calendar_type = SafeCharField(verbose_name=_("calendar type"), **DEFAULT_CODE_TYPE) # type: ignore 

228 city = SafeCharField(verbose_name=_("city"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

229 zip_code = SafeCharField(verbose_name=_("zip code"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

230 birth_date = models.DateField(verbose_name=_("birth date"), **DEFAULT_DATE_TYPE) # type: ignore 

231 birth_date_description = SafeCharField(verbose_name=_("zip code"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

232 day_of_month = models.IntegerField(verbose_name=_("day of month"), **DEFAULT_INT_TYPE) # type: ignore 

233 month_of_year = models.IntegerField(verbose_name=_("month of year"), **DEFAULT_INT_TYPE) # type: ignore 

234 year = models.IntegerField(verbose_name=_("year"), **DEFAULT_INT_TYPE) # type: ignore 

235 region = SafeCharField(verbose_name=_("region"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

236 place = SafeCharField(verbose_name=_("place"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

237 country_iso2_code = SafeCharField(verbose_name=_("country"), **COUNTRY_CODE_TYPE) # type: ignore 

238 country_description = SafeCharField(verbose_name=_("country description"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

239 regulation_language = SafeCharField(verbose_name=_("regional language"), **LANGUAGE_CODE_TYPE) # type: ignore 

240 logical_id = models.BigIntegerField(verbose_name=_("logical id"), blank=True, null=True, default=None) 

241 

242 class Meta: 

243 verbose_name = _("birth date") 

244 verbose_name_plural = _("birth dates") 

245 

246 

247class Citizenship(SanctionListObject): 

248 sanction = models.ForeignKey("SanctionEntity", verbose_name=_("sanction entity"), on_delete=models.CASCADE) 

249 region = SafeCharField(verbose_name=_("region"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

250 country_iso2_code = SafeCharField(verbose_name=_("country"), **COUNTRY_CODE_TYPE) # type: ignore 

251 country_description = SafeCharField(verbose_name=_("country description"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

252 regulation_language = SafeCharField(verbose_name=_("regional language"), **LANGUAGE_CODE_TYPE) # type: ignore 

253 logical_id = models.BigIntegerField(verbose_name=_("logical id"), blank=True, null=True, default=None) 

254 regulation_summary = models.ForeignKey(RegulationSummary, **REGULATION_SUMMARY_TYPE) # type: ignore 

255 

256 class Meta: 

257 verbose_name = _("citizenship") 

258 verbose_name_plural = _("citizenships") 

259 

260 

261class Address(SanctionListObject): 

262 sanction = models.ForeignKey("SanctionEntity", verbose_name=_("sanction entity"), on_delete=models.CASCADE) 

263 city = SafeCharField(verbose_name=_("city"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

264 street = SafeCharField(verbose_name=_("street"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

265 po_box = SafeCharField(verbose_name=_("p.o. box"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

266 zip_code = SafeCharField(verbose_name=_("zip code"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

267 as_at_listing_time = models.BooleanField(_("as at listing time"), **DEFAULT_BOOLEAN_TYPE) # type: ignore 

268 place = SafeCharField(verbose_name=_("place"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

269 region = SafeCharField(verbose_name=_("region"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

270 country_iso2_code = SafeCharField(verbose_name=_("country"), **COUNTRY_CODE_TYPE) # type: ignore 

271 country_description = SafeCharField(verbose_name=_("country description"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

272 regulation_language = SafeCharField(verbose_name=_("regional language"), **LANGUAGE_CODE_TYPE) # type: ignore 

273 logical_id = models.BigIntegerField(verbose_name=_("logical id"), blank=True, null=True, default=None) 

274 regulation_summary = models.ForeignKey(RegulationSummary, **REGULATION_SUMMARY_TYPE) # type: ignore 

275 

276 class Meta: 

277 verbose_name = _("address") 

278 verbose_name_plural = _("addresses") 

279 

280 def clean(self): 

281 if self.country_description and not self.country_iso2_code: 

282 self.country_iso2_code = get_country_iso2_code(self.country_description) 

283 

284 

285class SanctionEntity(SanctionListObject): 

286 source = models.ForeignKey(SanctionsListFile, verbose_name=_("source"), on_delete=models.CASCADE) 

287 designation_details = SafeCharField(verbose_name=_("designation details"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

288 united_nation_id = SafeCharField(verbose_name=_("United Nation identifier"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

289 eu_reference_number = SafeCharField(verbose_name=_("EU reference number"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore 

290 logical_id = models.BigIntegerField(verbose_name=_("logical id"), blank=True, null=True, default=None) 

291 subject_type = models.ForeignKey(SubjectType, verbose_name=_("subject type"), on_delete=models.PROTECT, null=True, default=None, blank=True) 

292 data = models.JSONField(_('data'), default=dict, blank=True, encoder=DjangoJSONEncoder) # type: ignore 

293 

294 class Meta: 

295 verbose_name = _("sanction entity") 

296 verbose_name_plural = _("sanction entities")