Coverage for jsanctions/models.py : 93%

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
14from jsanctions.helpers import get_country_iso2_code
16logger = logging.getLogger(__name__)
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}
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
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
69class SanctionListObject(models.Model):
70 pass
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
86 class Meta:
87 verbose_name = _("sanction list")
88 verbose_name_plural = _("sanction lists")
90 def __str__(self):
91 return "{}".format(os.path.basename(self.file.name))
93 @property
94 def full_path(self) -> str:
95 return get_media_full_path(self.file.name)
98class Remark(models.Model):
99 container = models.ForeignKey(SanctionListObject, on_delete=models.CASCADE)
100 text = SafeTextField(verbose_name=_("text"), blank=True)
102 class Meta:
103 verbose_name = _("remark")
104 verbose_name_plural = _("remarks")
106 def __str__(self) -> str:
107 return str(self.text)
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] + "..."
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 ]
126 classification_code = SafeCharField(verbose_name=_("classification code"), **DEFAULT_CODE_TYPE) # type: ignore
127 code = SafeCharField(verbose_name=_("code"), **DEFAULT_DESCRIPTION_TYPE) # type: ignore
129 class Meta:
130 verbose_name = _("subject type")
131 verbose_name_plural = _("subject types")
133 def __str__(self) -> str:
134 return str(self.code)
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)
148 class Meta:
149 verbose_name = _("regulation")
150 verbose_name_plural = _("regulations")
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="")
159 class Meta:
160 verbose_name = _("regulation summary")
161 verbose_name_plural = _("regulation summaries")
163 def __str__(self) -> str:
164 return "{} {}".format(self.regulation_type, self.number_title)
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
180 class Meta:
181 verbose_name = _("name alias")
182 verbose_name_plural = _("name aliases")
184 def __str__(self) -> str:
185 return str(self.whole_name)
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()
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
215 class Meta:
216 verbose_name = _("identification")
217 verbose_name_plural = _("identifications")
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)
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)
242 class Meta:
243 verbose_name = _("birth date")
244 verbose_name_plural = _("birth dates")
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
256 class Meta:
257 verbose_name = _("citizenship")
258 verbose_name_plural = _("citizenships")
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
276 class Meta:
277 verbose_name = _("address")
278 verbose_name_plural = _("addresses")
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)
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
294 class Meta:
295 verbose_name = _("sanction entity")
296 verbose_name_plural = _("sanction entities")