From 3039fae5124c00a67283c9b707e4a411149d93b1 Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Fri, 19 Mar 2021 11:05:22 +0100 Subject: Format - black: ishtar_common --- ishtar_common/models.py | 3310 +++++++++++++++++++++++++++++------------------ 1 file changed, 2036 insertions(+), 1274 deletions(-) (limited to 'ishtar_common/models.py') diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 56c65c1f3..88aa993c6 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -54,8 +54,11 @@ from django.contrib.postgres.fields import JSONField from django.contrib.postgres.indexes import GinIndex from django.contrib.sites.models import Site from django.core.cache import cache -from django.core.exceptions import ObjectDoesNotExist, ValidationError, \ - MultipleObjectsReturned +from django.core.exceptions import ( + ObjectDoesNotExist, + ValidationError, + MultipleObjectsReturned, +) from django.core.files.base import ContentFile from django.core.files.uploadedfile import SimpleUploadedFile from django.core.urlresolvers import reverse @@ -65,55 +68,136 @@ from django.db.utils import DatabaseError from django.template import Context, Template from django.template.defaultfilters import slugify from django.utils.functional import lazy -from ishtar_common.utils import ugettext_lazy as _, ugettext, \ - pgettext_lazy, get_generated_id, get_current_profile, duplicate_item, \ - get_image_path +from ishtar_common.utils import ( + ugettext_lazy as _, + ugettext, + pgettext_lazy, + get_generated_id, + get_current_profile, + duplicate_item, + get_image_path, +) from ishtar_common.utils_secretary import IshtarSecretaryRenderer -from ishtar_common.alternative_configs import ALTERNATE_CONFIGS, \ - ALTERNATE_CONFIGS_CHOICES +from ishtar_common.alternative_configs import ( + ALTERNATE_CONFIGS, + ALTERNATE_CONFIGS_CHOICES, +) from ishtar_common.data_importer import pre_importer_action -from ishtar_common.model_managers import SlugModelManager, ExternalIdManager, \ - UUIDModelManager +from ishtar_common.model_managers import ( + SlugModelManager, + ExternalIdManager, + UUIDModelManager, +) from ishtar_common.model_merging import merge_model_objects -from ishtar_common.models_imports import ImporterModel, ImporterType, \ - ImporterDefault, ImporterDefaultValues, ImporterColumn, \ - ImporterDuplicateField, Regexp, ImportTarget, TargetKey, FormaterType, \ - Import, TargetKeyGroup, ValueFormater -from ishtar_common.utils import get_cache, create_slug, \ - get_all_field_names, cached_label_changed, \ - generate_relation_graph, max_size_help - -from ishtar_common.models_common import GeneralType, HierarchicalType, \ - BaseHistorizedItem, LightHistorizedItem, FullSearch, \ - SearchAltName, OwnPerms, Cached, \ - Address, post_save_cache, TemplateItem, SpatialReferenceSystem, \ - DashboardFormItem, document_attached_changed, SearchAltName, \ - DynamicRequest, GeoItem, CompleteIdentifierItem, SearchVectorConfig, \ - DocumentItem, QuickAction, MainItem, Merge, ShortMenuItem, Town, \ - ImageContainerModel, StatisticItem, CachedGen, CascasdeUpdate, \ - Department, State +from ishtar_common.models_imports import ( + ImporterModel, + ImporterType, + ImporterDefault, + ImporterDefaultValues, + ImporterColumn, + ImporterDuplicateField, + Regexp, + ImportTarget, + TargetKey, + FormaterType, + Import, + TargetKeyGroup, + ValueFormater, +) +from ishtar_common.utils import ( + get_cache, + create_slug, + get_all_field_names, + cached_label_changed, + generate_relation_graph, + max_size_help, +) + +from ishtar_common.models_common import ( + GeneralType, + HierarchicalType, + BaseHistorizedItem, + LightHistorizedItem, + FullSearch, + SearchAltName, + OwnPerms, + Cached, + Address, + post_save_cache, + TemplateItem, + SpatialReferenceSystem, + DashboardFormItem, + document_attached_changed, + SearchAltName, + DynamicRequest, + GeoItem, + CompleteIdentifierItem, + SearchVectorConfig, + DocumentItem, + QuickAction, + MainItem, + Merge, + ShortMenuItem, + Town, + ImageContainerModel, + StatisticItem, + CachedGen, + CascasdeUpdate, + Department, + State, +) __all__ = [ - 'ImporterModel', 'ImporterType', 'ImporterDefault', 'ImporterDefaultValues', - 'ImporterColumn', 'ImporterDuplicateField', 'Regexp', 'ImportTarget', - 'TargetKey', 'FormaterType', 'Import', 'TargetKeyGroup', 'ValueFormater', - 'Organization', 'Person', 'valid_id', 'Town', 'SpatialReferenceSystem', - 'OrganizationType', 'Document', 'GeneralType', 'get_generated_id', - 'LightHistorizedItem', 'OwnPerms', 'Address', 'post_save_cache', - 'DashboardFormItem', 'ShortMenuItem', 'document_attached_changed', - 'SearchAltName', 'DynamicRequest', 'GeoItem', - 'SearchVectorConfig', 'DocumentItem', 'CachedGen', 'StatisticItem', - 'CascasdeUpdate', 'Department', 'State', 'CompleteIdentifierItem' + "ImporterModel", + "ImporterType", + "ImporterDefault", + "ImporterDefaultValues", + "ImporterColumn", + "ImporterDuplicateField", + "Regexp", + "ImportTarget", + "TargetKey", + "FormaterType", + "Import", + "TargetKeyGroup", + "ValueFormater", + "Organization", + "Person", + "valid_id", + "Town", + "SpatialReferenceSystem", + "OrganizationType", + "Document", + "GeneralType", + "get_generated_id", + "LightHistorizedItem", + "OwnPerms", + "Address", + "post_save_cache", + "DashboardFormItem", + "ShortMenuItem", + "document_attached_changed", + "SearchAltName", + "DynamicRequest", + "GeoItem", + "SearchVectorConfig", + "DocumentItem", + "CachedGen", + "StatisticItem", + "CascasdeUpdate", + "Department", + "State", + "CompleteIdentifierItem", ] logger = logging.getLogger(__name__) def post_save_user(sender, **kwargs): - user = kwargs['instance'] + user = kwargs["instance"] if kwargs["created"]: try: @@ -131,85 +215,106 @@ class ValueGetter(object): COL_LABELS = {} GET_VALUES_EXTRA = [] GET_VALUES_EXCLUDE_FIELDS = [ - 'search_vector', 'id', 'multi_polygon', 'point_2d', 'point', - 'history_m2m'] + "search_vector", + "id", + "multi_polygon", + "point_2d", + "point", + "history_m2m", + ] GET_VALUES_ = [ - 'preservation_to_considers', 'alterations', 'alteration_causes'] + "preservation_to_considers", + "alterations", + "alteration_causes", + ] GET_VALUES_EXTRA_TYPES = [ - 'preservation_to_considers', 'alterations', 'alteration_causes'] + "preservation_to_considers", + "alterations", + "alteration_causes", + ] def _get_values_documents(self, prefix="", filtr=None): values = {} - if not hasattr(self, 'documents'): + if not hasattr(self, "documents"): return values if not filtr or prefix + "documents" in filtr: values[prefix + "documents"] = [ - doc.get_values(no_values=True) - for doc in self.documents.all() + doc.get_values(no_values=True) for doc in self.documents.all() ] if filtr and prefix + "main_image" not in filtr: return values - if hasattr(self, "main_image") and self.main_image and hasattr( - self.main_image, "get_values"): + if ( + hasattr(self, "main_image") + and self.main_image + and hasattr(self.main_image, "get_values") + ): values[prefix + "main_image"] = self.main_image.get_values( - no_values=True) + no_values=True + ) return values def _get_values_update_sub_filter(self, filtr, prefix): if not filtr: return - return [k[len(prefix):] for k in filtr if k.startswith(prefix)] + return [k[len(prefix) :] for k in filtr if k.startswith(prefix)] - def get_values(self, prefix='', no_values=False, filtr=None, **kwargs): + def get_values(self, prefix="", no_values=False, filtr=None, **kwargs): if not prefix: prefix = self._prefix exclude = kwargs.get("exclude", []) values = {} - if hasattr(self, "qrcode") and ( - not filtr or prefix + 'qrcode_path' in filtr) and \ - prefix + 'qrcode_path' not in exclude: - values[prefix + 'qrcode_path'] = self.qrcode_path + if ( + hasattr(self, "qrcode") + and (not filtr or prefix + "qrcode_path" in filtr) + and prefix + "qrcode_path" not in exclude + ): + values[prefix + "qrcode_path"] = self.qrcode_path for field_name in get_all_field_names(self): try: value = getattr(self, field_name) except (AttributeError, MultipleObjectsReturned): continue - if field_name in self.GET_VALUES_EXCLUDE_FIELDS or \ - prefix + field_name in exclude: + if ( + field_name in self.GET_VALUES_EXCLUDE_FIELDS + or prefix + field_name in exclude + ): continue if filtr and not any( - field_name for f in filtr - if f.startswith(prefix + field_name)): + field_name for f in filtr if f.startswith(prefix + field_name) + ): continue - if hasattr(value, 'get_values'): - new_prefix = prefix + field_name + '_' + if hasattr(value, "get_values"): + new_prefix = prefix + field_name + "_" values.update( - value.get_values(new_prefix, filtr=filtr, **kwargs)) + value.get_values(new_prefix, filtr=filtr, **kwargs) + ) if hasattr(self, "get_values_for_" + field_name): values[prefix + field_name] = getattr( - self, "get_values_for_" + field_name)() + self, "get_values_for_" + field_name + )() else: values[prefix + field_name] = value values.update(self._get_values_documents(prefix=prefix, filtr=filtr)) for extra_field in self.GET_VALUES_EXTRA: - values[prefix + extra_field] = getattr(self, extra_field) or '' + values[prefix + extra_field] = getattr(self, extra_field) or "" for key, val in values.items(): if val is None: - val = '' + val = "" elif (key in self.GET_VALUES_EXTRA_TYPES or "type" in key) and ( - val.__class__.__name__.split('.')[0] == 'ManyRelatedManager'): + val.__class__.__name__.split(".")[0] == "ManyRelatedManager" + ): val = " ; ".join(str(v) for v in val.all()) elif not isinstance(val, (tuple, list, dict)): val = str(val) - if val.endswith('.None'): - val = '' + if val.endswith(".None"): + val = "" values[key] = val if (prefix and prefix != self._prefix) or no_values: # do not provide KEYS and VALUES for sub-items return values value_list = [] for key, value_ in values.items(): - if key in ('KEYS', 'VALUES'): + if key in ("KEYS", "VALUES"): continue value_list.append((key, str(value_))) for global_var in GlobalVar.objects.all(): @@ -217,10 +322,12 @@ class ValueGetter(object): return values @classmethod - def get_empty_values(cls, prefix=''): + def get_empty_values(cls, prefix=""): if not prefix: prefix = cls._prefix - return {prefix + field_name: '' for field_name in get_all_field_names(cls)} + return { + prefix + field_name: "" for field_name in get_all_field_names(cls) + } class HistoryModel(models.Model): @@ -231,14 +338,14 @@ class HistoryModel(models.Model): if not self.history_m2m or key not in self.history_m2m: return models = self.__class__.__module__ - if not models.endswith('.models'): + if not models.endswith(".models"): models += ".models" models = import_module(models) - model = getattr( - models, self.__class__.__name__[len('Historical'):]) + model = getattr(models, self.__class__.__name__[len("Historical") :]) related_model = getattr(model, key).rel.model - return related_model.history_decompress(self.history_m2m[key], - create=create) + return related_model.history_decompress( + self.history_m2m[key], create=create + ) def valid_id(cls): @@ -262,8 +369,7 @@ def valid_ids(cls): try: cls.objects.get(pk=v) except ObjectDoesNotExist: - raise ValidationError( - _("A selected item is not a valid item.")) + raise ValidationError(_("A selected item is not a valid item.")) return func @@ -317,11 +423,11 @@ class ItemKey(models.Model): key = models.TextField(_("Key")) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() - content_object = GenericForeignKey('content_type', 'object_id') + content_object = GenericForeignKey("content_type", "object_id") importer = models.ForeignKey( - Import, null=True, blank=True, - help_text=_("Specific key to an import")) - user = models.ForeignKey('IshtarUser', blank=True, null=True) + Import, null=True, blank=True, help_text=_("Specific key to an import") + ) + user = models.ForeignKey("IshtarUser", blank=True, null=True) group = models.ForeignKey(TargetKeyGroup, blank=True, null=True) def __str__(self): @@ -329,14 +435,23 @@ class ItemKey(models.Model): class ImageModel(models.Model, ImageContainerModel): - image = models.ImageField(upload_to=get_image_path, blank=True, null=True, - max_length=255, help_text=max_size_help()) + image = models.ImageField( + upload_to=get_image_path, + blank=True, + null=True, + max_length=255, + help_text=max_size_help(), + ) thumbnail = models.ImageField( - upload_to=get_image_path, blank=True, null=True, max_length=255, - help_text=max_size_help()) + upload_to=get_image_path, + blank=True, + null=True, + max_length=255, + help_text=max_size_help(), + ) IMAGE_MAX_SIZE = settings.IMAGE_MAX_SIZE THUMB_MAX_SIZE = settings.THUMB_MAX_SIZE - IMAGE_PREFIX = '' + IMAGE_PREFIX = "" class Meta: abstract = True @@ -344,7 +459,7 @@ class ImageModel(models.Model, ImageContainerModel): def has_changed(self, field): if not self.pk: return True - manager = getattr(self.__class__, 'objects') + manager = getattr(self.__class__, "objects") old = getattr(manager.get(pk=self.pk), field) return getattr(self, field) != old @@ -352,17 +467,17 @@ class ImageModel(models.Model, ImageContainerModel): """Returns the image resized to fit inside a box of the given size""" image.thumbnail(size, Image.ANTIALIAS) temp = BytesIO() - image.save(temp, 'jpeg') + image.save(temp, "jpeg") temp.seek(0) - return SimpleUploadedFile('temp', temp.read()) + return SimpleUploadedFile("temp", temp.read()) def save(self, *args, **kwargs): - if 'force_copy' in kwargs: - kwargs.pop('force_copy') + if "force_copy" in kwargs: + kwargs.pop("force_copy") super(ImageModel, self).save(*args, **kwargs) return # manage images - if not self.has_changed('image'): + if not self.has_changed("image"): return super(ImageModel, self).save(*args, **kwargs) if not self.image: self.thumbnail = None @@ -377,14 +492,16 @@ class ImageModel(models.Model, ImageContainerModel): try: image = Image.open(self.image.file) # convert to RGB - if image.mode not in ('L', 'RGB'): - image = image.convert('RGB') + if image.mode not in ("L", "RGB"): + image = image.convert("RGB") # resize if necessary if self.IMAGE_MAX_SIZE: - self.image.save(filename, - self.create_thumb(image, self.IMAGE_MAX_SIZE), - save=False) + self.image.save( + filename, + self.create_thumb(image, self.IMAGE_MAX_SIZE), + save=False, + ) if old_path != self.image.path: try: @@ -398,7 +515,8 @@ class ImageModel(models.Model, ImageContainerModel): self.thumbnail.save( thumb_filename, self.create_thumb(image, self.THUMB_MAX_SIZE), - save=False) + save=False, + ) except (IOError, ValueError): self.thumbnail = None self.image = None @@ -408,10 +526,8 @@ class ImageModel(models.Model, ImageContainerModel): return super(ImageModel, self).save(*args, **kwargs) def _get_thumb_name(self, filename): - splited = filename.split('.') - return "{}-thumb.{}".format( - ".".join(splited[:-1]), splited[-1] - ) + splited = filename.split(".") + return "{}-thumb.{}".format(".".join(splited[:-1]), splited[-1]) class BulkUpdatedItem(object): @@ -428,7 +544,7 @@ class BulkUpdatedItem(object): """ if not transaction_id: transaction_id = str(time.time()) - args = ['cached_label_bulk_update', transaction_id] + extra_args + args = ["cached_label_bulk_update", transaction_id] + extra_args key, val = get_cache(cls, args) if val: return transaction_id, True @@ -440,60 +556,98 @@ class RelationItem(models.Model): """ Items with relation between them """ + MAIN_UP_MODEL_QUERY = "" relation_image = models.FileField( - _("Generated relation image (SVG)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated relation image (SVG)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) relation_bitmap_image = models.FileField( - _("Generated relation image (PNG)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated relation image (PNG)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) relation_dot = models.FileField( - _("Generated relation image (DOT)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated relation image (DOT)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) relation_image_above = models.FileField( - _("Generated above relation image (SVG)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated above relation image (SVG)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) relation_dot_above = models.FileField( - _("Generated above relation image (DOT)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated above relation image (DOT)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) relation_bitmap_image_above = models.FileField( - _("Generated above relation image (PNG)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated above relation image (PNG)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) relation_image_below = models.FileField( - _("Generated below relation image (SVG)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated below relation image (SVG)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) relation_dot_below = models.FileField( - _("Generated below relation image (DOT)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated below relation image (DOT)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) relation_bitmap_image_below = models.FileField( - _("Generated below relation image (PNG)"), null=True, blank=True, - upload_to=get_image_path, help_text=max_size_help() + _("Generated below relation image (PNG)"), + null=True, + blank=True, + upload_to=get_image_path, + help_text=max_size_help(), ) class Meta: abstract = True def generate_relation_image( - self, highlight_current=True, render_above=True, - render_below=True, full=False): - generate_relation_graph(self, highlight_current=highlight_current, - render_above=render_above, - render_below=render_below, full=full) + self, + highlight_current=True, + render_above=True, + render_below=True, + full=False, + ): + generate_relation_graph( + self, + highlight_current=highlight_current, + render_above=render_above, + render_below=render_below, + full=full, + ) class JsonDataSectionManager(models.Manager): def get_by_natural_key(self, name, app_label, model): - return self.get(name=name, - content_type__app_label=app_label, - content_type__model=model) + return self.get( + name=name, + content_type__app_label=app_label, + content_type__model=model, + ) class JsonDataSection(models.Model): @@ -505,7 +659,7 @@ class JsonDataSection(models.Model): class Meta: verbose_name = _("Json data - Menu") verbose_name_plural = _("Json data - Menus") - ordering = ['order', 'name'] + ordering = ["order", "name"] unique_together = ("name", "content_type") def natural_key(self): @@ -516,48 +670,59 @@ class JsonDataSection(models.Model): JSON_VALUE_TYPES = ( - ('T', _("Text")), - ('LT', _("Long text")), - ('I', _("Integer")), - ('B', _("Boolean")), - ('F', _("Float")), - ('D', _("Date")), - ('C', _("Choices")), + ("T", _("Text")), + ("LT", _("Long text")), + ("I", _("Integer")), + ("B", _("Boolean")), + ("F", _("Float")), + ("D", _("Date")), + ("C", _("Choices")), ) class JsonDataFieldManager(models.Manager): def get_by_natural_key(self, key, app_label, model): - return self.get(key=key, content_type__app_label=app_label, - content_type__model=model) + return self.get( + key=key, + content_type__app_label=app_label, + content_type__model=model, + ) class JsonDataField(models.Model): name = models.CharField(_("Name"), max_length=200) content_type = models.ForeignKey(ContentType) key = models.CharField( - _("Key"), max_length=200, - help_text=_("Value of the key in the JSON schema. For hierarchical " - "key use \"__\" to explain it. For instance for the key " - "'my_subkey' with data such as {'my_key': {'my_subkey': " - "'value'}}, its value will be reached with " - "my_key__my_subkey.")) + _("Key"), + max_length=200, + help_text=_( + "Value of the key in the JSON schema. For hierarchical " + 'key use "__" to explain it. For instance for the key ' + "'my_subkey' with data such as {'my_key': {'my_subkey': " + "'value'}}, its value will be reached with " + "my_key__my_subkey." + ), + ) display = models.BooleanField(_("Display"), default=True) - value_type = models.CharField(_("Type"), default="T", max_length=10, - choices=JSON_VALUE_TYPES) + value_type = models.CharField( + _("Type"), default="T", max_length=10, choices=JSON_VALUE_TYPES + ) order = models.IntegerField(_("Order"), default=10) - search_index = models.BooleanField(_("Use in search indexes"), - default=False) - section = models.ForeignKey(JsonDataSection, blank=True, null=True, - on_delete=models.SET_NULL) + search_index = models.BooleanField( + _("Use in search indexes"), default=False + ) + section = models.ForeignKey( + JsonDataSection, blank=True, null=True, on_delete=models.SET_NULL + ) custom_forms = models.ManyToManyField( - "CustomForm", blank=True, through="CustomFormJsonField") + "CustomForm", blank=True, through="CustomFormJsonField" + ) objects = JsonDataFieldManager() class Meta: verbose_name = _("Json data - Field") verbose_name_plural = _("Json data - Fields") - ordering = ['order', 'name'] + ordering = ["order", "name"] unique_together = ("content_type", "key") def natural_key(self): @@ -571,29 +736,35 @@ class JsonDataField(models.Model): return if self.section.content_type != self.content_type: raise ValidationError( - _("Content types of the field and of the menu do not match")) + _("Content types of the field and of the menu do not match") + ) LOGICAL_TYPES = ( - ('above', _("Above")), - ('below', _("Below")), - ('equal', _("Equal")), - ('include', _("Include")), - ('included', _("Is included")), + ("above", _("Above")), + ("below", _("Below")), + ("equal", _("Equal")), + ("include", _("Include")), + ("included", _("Is included")), ) class GeneralRelationType(GeneralType): order = models.IntegerField(_("Order"), default=1) symmetrical = models.BooleanField(_("Symmetrical")) - tiny_label = models.CharField(_("Tiny label"), max_length=50, - blank=True, null=True) + tiny_label = models.CharField( + _("Tiny label"), max_length=50, blank=True, null=True + ) inverse_relation = models.ForeignKey( - 'self', verbose_name=_("Inverse relation"), blank=True, - null=True) + "self", verbose_name=_("Inverse relation"), blank=True, null=True + ) logical_relation = models.CharField( - verbose_name=_("Logical relation"), max_length=10, - choices=LOGICAL_TYPES, blank=True, null=True) + verbose_name=_("Logical relation"), + max_length=10, + choices=LOGICAL_TYPES, + blank=True, + null=True, + ) class Meta: abstract = True @@ -602,7 +773,8 @@ class GeneralRelationType(GeneralType): # cannot have symmetrical and an inverse_relation if self.symmetrical and self.inverse_relation: raise ValidationError( - _("Cannot have symmetrical and an inverse_relation")) + _("Cannot have symmetrical and an inverse_relation") + ) def get_tiny_label(self): return self.tiny_label or self.label or "" @@ -611,9 +783,10 @@ class GeneralRelationType(GeneralType): obj = super(GeneralRelationType, self).save(*args, **kwargs) # after saving check that the inverse_relation of the inverse_relation # point to the saved object - if self.inverse_relation \ - and (not self.inverse_relation.inverse_relation - or self.inverse_relation.inverse_relation != self): + if self.inverse_relation and ( + not self.inverse_relation.inverse_relation + or self.inverse_relation.inverse_relation != self + ): self.inverse_relation.inverse_relation = self self.inverse_relation.symmetrical = False self.inverse_relation.save() @@ -623,7 +796,7 @@ class GeneralRelationType(GeneralType): class GeneralRecordRelations(object): @classmethod def general_types(cls): - return ['relation_type'] + return ["relation_type"] def save(self, *args, **kwargs): super(GeneralRecordRelations, self).save(*args, **kwargs) @@ -638,8 +811,11 @@ class GeneralRecordRelations(object): if not sym_rel_type: return - dct = {'right_record': self.left_record, - 'left_record': self.right_record, 'relation_type': sym_rel_type} + dct = { + "right_record": self.left_record, + "left_record": self.right_record, + "relation_type": sym_rel_type, + } self.__class__.objects.get_or_create(**dct) return self @@ -653,9 +829,11 @@ def post_delete_record_relation(sender, instance, **kwargs): # no symetric/inverse is defined if not sym_rel_type: return - dct = {'right_record_id': instance.left_record_id, - 'left_record_id': instance.right_record_id, - 'relation_type': sym_rel_type} + dct = { + "right_record_id": instance.left_record_id, + "left_record_id": instance.right_record_id, + "relation_type": sym_rel_type, + } q = instance.__class__.objects.filter(**dct) if q.count(): q.delete() @@ -664,342 +842,474 @@ def post_delete_record_relation(sender, instance, **kwargs): class SearchQuery(models.Model): label = models.TextField(_("Label"), blank=True, default="") query = models.TextField(_("Query"), blank=True, default="") - content_type = models.ForeignKey(ContentType, - verbose_name=_("Content type")) + content_type = models.ForeignKey( + ContentType, verbose_name=_("Content type") + ) profile = models.ForeignKey("UserProfile", verbose_name=_("Profile")) is_alert = models.BooleanField(_("Is an alert"), default=False) class Meta: verbose_name = _("Search query") verbose_name_plural = _("Search queries") - ordering = ['label'] + ordering = ["label"] def __str__(self): return str(self.label) class Language(GeneralType): - iso_code = models.CharField(_("ISO code"), null=True, blank=True, - max_length=2) + iso_code = models.CharField( + _("ISO code"), null=True, blank=True, max_length=2 + ) class Meta: verbose_name = _("Language") verbose_name_plural = _("Languages") -CURRENCY = (("€", _("Euro")), - ("$", _("US dollar"))) -FIND_INDEX_SOURCE = (("O", _("Operations")), - ("CR", _("Context records"))) -SITE_LABELS = [('site', _("Site")), ('entity', _("Archaeological entity"))] +CURRENCY = (("€", _("Euro")), ("$", _("US dollar"))) +FIND_INDEX_SOURCE = (("O", _("Operations")), ("CR", _("Context records"))) +SITE_LABELS = [("site", _("Site")), ("entity", _("Archaeological entity"))] TRANSLATED_SITE_LABELS = { - 'site': { - 'search': _("Site search"), - 'new': _("New site"), - 'modification': _("Site modification"), - 'deletion': _("Site deletion"), + "site": { + "search": _("Site search"), + "new": _("New site"), + "modification": _("Site modification"), + "deletion": _("Site deletion"), "attached-to-operation": _("Site (attached to the operation)"), - "name-attached-to-operation": - _("Site name (attached to the operation)"), + "name-attached-to-operation": _( + "Site name (attached to the operation)" + ), "attached-to-cr": _("Site (attached to the context record)"), "name-attached-to-cr": _("Site name (attached to the context record)"), }, - 'entity': { - 'search': _("Archaeological entity search"), - 'new': _("New archaeological entity"), - 'modification': _("Archaeological entity modification"), - 'deletion': _("Archaeological entity deletion"), - "attached-to-operation": _("Archaeological entity (attached to the " - "operation)"), - "name-attached-to-operation": _("Archaeological entity name (attached " - "to the operation)"), - "attached-to-cr": _("Archaeological entity (attached to the context " - "record)"), - "name-attached-to-cr": - _("Archaeological entity name (attached to the context record)"), + "entity": { + "search": _("Archaeological entity search"), + "new": _("New archaeological entity"), + "modification": _("Archaeological entity modification"), + "deletion": _("Archaeological entity deletion"), + "attached-to-operation": _( + "Archaeological entity (attached to the " "operation)" + ), + "name-attached-to-operation": _( + "Archaeological entity name (attached " "to the operation)" + ), + "attached-to-cr": _( + "Archaeological entity (attached to the context " "record)" + ), + "name-attached-to-cr": _( + "Archaeological entity name (attached to the context record)" + ), }, } ACCOUNT_NAMING_STYLE = ( - ('NF', _("name.firstname")), - ('FN', _("firstname.name")), + ("NF", _("name.firstname")), + ("FN", _("firstname.name")), ) class IshtarSiteProfile(models.Model, Cached): - slug_field = 'slug' + slug_field = "slug" label = models.TextField(_("Name")) slug = models.SlugField(_("Slug"), unique=True) active = models.BooleanField(_("Current active"), default=False) experimental_feature = models.BooleanField( - _("Activate experimental feature"), default=False) + _("Activate experimental feature"), default=False + ) description = models.TextField(_("Description"), blank=True, default="") warning_name = models.TextField(_("Warning name"), blank=True, default="") - warning_message = models.TextField(_("Warning message"), blank=True, - default="") + warning_message = models.TextField( + _("Warning message"), blank=True, default="" + ) delete_image_zip_on_archive = models.BooleanField( _("Import - Delete image/document zip on archive"), default=False ) clean_redundant_document_association = models.BooleanField( - _("Document - Remove redundant association"), default=False, - help_text=_("For instance, remove operation association of a " - "document also associated to a find of this operation. " - "Only manage association of operations, context records " - "and finds.") + _("Document - Remove redundant association"), + default=False, + help_text=_( + "For instance, remove operation association of a " + "document also associated to a find of this operation. " + "Only manage association of operations, context records " + "and finds." + ), ) calculate_weight_on_full = models.BooleanField( _("Container - calculate weight only when all find has a weight"), - default=False) + default=False, + ) config = models.CharField( - _("Alternate configuration"), max_length=200, + _("Alternate configuration"), + max_length=200, choices=ALTERNATE_CONFIGS_CHOICES, - help_text=_("Choose an alternate configuration for label, " - "index management"), - null=True, blank=True + help_text=_( + "Choose an alternate configuration for label, " "index management" + ), + null=True, + blank=True, ) files = models.BooleanField(_("Files module"), default=False) archaeological_site = models.BooleanField( - _("Archaeological site module"), default=False) + _("Archaeological site module"), default=False + ) archaeological_site_label = models.CharField( - _("Archaeological site type"), max_length=200, + _("Archaeological site type"), + max_length=200, choices=SITE_LABELS, - default='site' + default="site", + ) + context_record = models.BooleanField( + _("Context records module"), default=False + ) + find = models.BooleanField( + _("Finds module"), + default=False, + help_text=_("Need context records module"), ) - context_record = models.BooleanField(_("Context records module"), - default=False) - find = models.BooleanField(_("Finds module"), default=False, - help_text=_("Need context records module")) find_index = models.CharField( - _("Find index is based on"), default='O', max_length=2, + _("Find index is based on"), + default="O", + max_length=2, choices=FIND_INDEX_SOURCE, - help_text=_("To prevent irrelevant indexes, change this parameter " - "only if there is no find in the database")) + help_text=_( + "To prevent irrelevant indexes, change this parameter " + "only if there is no find in the database" + ), + ) warehouse = models.BooleanField( - _("Warehouses module"), default=False, - help_text=_("Need finds module")) - preservation = models.BooleanField(_("Preservation module"), - default=False) + _("Warehouses module"), default=False, help_text=_("Need finds module") + ) + preservation = models.BooleanField(_("Preservation module"), default=False) mapping = models.BooleanField(_("Mapping module"), default=False) point_precision = models.IntegerField( - _("Point precision (search and sheets)"), null=True, blank=True, + _("Point precision (search and sheets)"), + null=True, + blank=True, help_text=_( "Number of digit to round from the decimal point for coordinates " "in WGS84 (latitude, longitude). Empty value means no round." - ) + ), ) locate_warehouses = models.BooleanField( - _("Locate warehouse and containers"), default=False, + _("Locate warehouse and containers"), + default=False, help_text=_( "Mapping module must be activated. With many containers and " "background task not activated, activating this option may " - "consume many resources.") + "consume many resources." + ), ) use_town_for_geo = models.BooleanField( - _("Use town to locate when coordinates are missing"), default=True) - relation_graph = models.BooleanField(_("Generate relation graph"), - default=False) + _("Use town to locate when coordinates are missing"), default=True + ) + relation_graph = models.BooleanField( + _("Generate relation graph"), default=False + ) underwater = models.BooleanField(_("Underwater module"), default=False) parcel_mandatory = models.BooleanField( - _("Parcel are mandatory for context records"), default=True) + _("Parcel are mandatory for context records"), default=True + ) homepage = models.TextField( - _("Home page"), blank=True, default="", - help_text=_("Homepage of Ishtar - if not defined a default homepage " - "will appear. Use the markdown syntax. {random_image} " - "can be used to display a random image.")) + _("Home page"), + blank=True, + default="", + help_text=_( + "Homepage of Ishtar - if not defined a default homepage " + "will appear. Use the markdown syntax. {random_image} " + "can be used to display a random image." + ), + ) operation_prefix = models.CharField( - _("Main operation code prefix"), default='OA', null=True, blank=True, - max_length=20 + _("Main operation code prefix"), + default="OA", + null=True, + blank=True, + max_length=20, ) default_operation_prefix = models.CharField( - _("Default operation code prefix"), default='OP', null=True, - blank=True, max_length=20 + _("Default operation code prefix"), + default="OP", + null=True, + blank=True, + max_length=20, ) operation_region_code = models.CharField( - _("Operation region code"), null=True, blank=True, - max_length=5 + _("Operation region code"), null=True, blank=True, max_length=5 ) operation_complete_identifier = models.TextField( _("Operation complete identifier"), - default="", blank=True, - help_text=_("Formula to manage operation complete identifier.")) + default="", + blank=True, + help_text=_("Formula to manage operation complete identifier."), + ) operation_custom_index = models.TextField( _("Operation custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage operation custom index. " - "Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage operation custom index. " + "Separate keys with a semicolon." + ), + ) site_complete_identifier = models.TextField( _("Archaeological site complete identifier"), - default="", blank=True, - help_text=_("Formula to manage archaeological site complete" - " identifier.")) + default="", + blank=True, + help_text=_( + "Formula to manage archaeological site complete" " identifier." + ), + ) site_custom_index = models.TextField( _("Archaeological site custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage archaeological site custom " - "index. Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage archaeological site custom " + "index. Separate keys with a semicolon." + ), + ) file_external_id = models.TextField( _("File external id"), default="{year}-{numeric_reference}", - help_text=_("Formula to manage file external ID. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) + help_text=_( + "Formula to manage file external ID. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) file_complete_identifier = models.TextField( _("Archaeological file complete identifier"), - default="", blank=True, - help_text=_("Formula to manage archaeological file complete " - "identifier.")) + default="", + blank=True, + help_text=_( + "Formula to manage archaeological file complete " "identifier." + ), + ) file_custom_index = models.TextField( _("Archaeological file custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage archaeological file custom " - "index. Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage archaeological file custom " + "index. Separate keys with a semicolon." + ), + ) parcel_external_id = models.TextField( _("Parcel external id"), default="{associated_file__external_id}{operation__code_patriarche}-" - "{town__numero_insee}-{section}{parcel_number}", - help_text=_("Formula to manage parcel external ID. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) + "{town__numero_insee}-{section}{parcel_number}", + help_text=_( + "Formula to manage parcel external ID. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) context_record_external_id = models.TextField( _("Context record external id"), default="{parcel__external_id}-{label}", - help_text=_("Formula to manage context record external ID. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) + help_text=_( + "Formula to manage context record external ID. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) contextrecord_complete_identifier = models.TextField( _("Context record complete identifier"), - default="", blank=True, - help_text=_("Formula to manage context record complete identifier.")) + default="", + blank=True, + help_text=_("Formula to manage context record complete identifier."), + ) contextrecord_custom_index = models.TextField( _("Context record custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage context record custom index. " - "Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage context record custom index. " + "Separate keys with a semicolon." + ), + ) base_find_external_id = models.TextField( _("Base find external id"), default="{context_record__external_id}-{label}", - help_text=_("Formula to manage base find external ID. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) + help_text=_( + "Formula to manage base find external ID. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) basefind_complete_identifier = models.TextField( _("Base find complete identifier"), - default="", blank=True, - help_text=_("Formula to manage base find complete identifier.")) + default="", + blank=True, + help_text=_("Formula to manage base find complete identifier."), + ) basefind_custom_index = models.TextField( _("Base find custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage base find custom index. " - "Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage base find custom index. " + "Separate keys with a semicolon." + ), + ) find_external_id = models.TextField( _("Find external id"), default="{get_first_base_find__context_record__external_id}-{label}", - help_text=_("Formula to manage find external ID. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) + help_text=_( + "Formula to manage find external ID. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) find_complete_identifier = models.TextField( _("Find complete identifier"), - default="", blank=True, - help_text=_("Formula to manage find complete identifier.")) + default="", + blank=True, + help_text=_("Formula to manage find complete identifier."), + ) find_custom_index = models.TextField( _("Find custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage find custom index. " - "Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage find custom index. " + "Separate keys with a semicolon." + ), + ) container_external_id = models.TextField( _("Container external id"), - default="{parent_external_id}-{container_type__txt_idx}-" - "{reference}", - help_text=_("Formula to manage container external ID. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) + default="{parent_external_id}-{container_type__txt_idx}-" "{reference}", + help_text=_( + "Formula to manage container external ID. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) container_complete_identifier = models.TextField( _("Container complete identifier"), - default="", blank=True, - help_text=_("Formula to manage container complete identifier.")) + default="", + blank=True, + help_text=_("Formula to manage container complete identifier."), + ) container_custom_index = models.TextField( _("Container custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage container custom index. " - "Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage container custom index. " + "Separate keys with a semicolon." + ), + ) warehouse_external_id = models.TextField( _("Warehouse external id"), default="{name|slug}", - help_text=_("Formula to manage warehouse external ID. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) + help_text=_( + "Formula to manage warehouse external ID. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) warehouse_complete_identifier = models.TextField( _("Warehouse complete identifier"), - default="", blank=True, - help_text=_("Formula to manage warehouse complete identifier.")) + default="", + blank=True, + help_text=_("Formula to manage warehouse complete identifier."), + ) warehouse_custom_index = models.TextField( _("Warehouse custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage warehouse custom index. " - "Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage warehouse custom index. " + "Separate keys with a semicolon." + ), + ) document_external_id = models.TextField( _("Document external id"), default="{index}", - help_text=_("Formula to manage document external ID. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) + help_text=_( + "Formula to manage document external ID. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) document_complete_identifier = models.TextField( _("Document complete identifier"), - default="", blank=True, - help_text=_("Formula to manage document complete identifier.")) + default="", + blank=True, + help_text=_("Formula to manage document complete identifier."), + ) document_custom_index = models.TextField( _("Document custom index key"), - default="", blank=True, - help_text=_("Keys to be used to manage document custom index. " - "Separate keys with a semicolon.")) + default="", + blank=True, + help_text=_( + "Keys to be used to manage document custom index. " + "Separate keys with a semicolon." + ), + ) person_raw_name = models.TextField( _("Raw name for person"), default="{name|upper} {surname}", - help_text=_("Formula to manage person raw_name. " - "Change this with care. With incorrect formula, the " - "application might be unusable and import of external " - "data can be destructive.")) - find_use_index = models.BooleanField(_("Use auto index for finds"), - default=True) - currency = models.CharField(_("Currency"), default="€", - choices=CURRENCY, max_length=5) + help_text=_( + "Formula to manage person raw_name. " + "Change this with care. With incorrect formula, the " + "application might be unusable and import of external " + "data can be destructive." + ), + ) + find_use_index = models.BooleanField( + _("Use auto index for finds"), default=True + ) + currency = models.CharField( + _("Currency"), default="€", choices=CURRENCY, max_length=5 + ) account_naming_style = models.CharField( - _("Naming style for accounts"), max_length=2, default="NF", - choices=ACCOUNT_NAMING_STYLE + _("Naming style for accounts"), + max_length=2, + default="NF", + choices=ACCOUNT_NAMING_STYLE, ) default_center = models.PointField( - _("Maps - default center"), - default='SRID=4326;POINT(2.4397 46.5528)') - default_zoom = models.IntegerField( - _("Maps - default zoom"), default=6) + _("Maps - default center"), default="SRID=4326;POINT(2.4397 46.5528)" + ) + default_zoom = models.IntegerField(_("Maps - default zoom"), default=6) display_srs = models.ForeignKey( SpatialReferenceSystem, verbose_name=_("Spatial Reference System for display"), - blank=True, null=True, - help_text=_("Spatial Reference System used for display when no SRS is " - "defined") + blank=True, + null=True, + help_text=_( + "Spatial Reference System used for display when no SRS is " + "defined" + ), ) default_language = models.ForeignKey( Language, verbose_name=_("Default language for documentation"), - blank=True, null=True, - help_text=_("If set, by default the selected language will be set for " - "localized documents.") + blank=True, + null=True, + help_text=_( + "If set, by default the selected language will be set for " + "localized documents." + ), ) objects = SlugModelManager() class Meta: verbose_name = _("Ishtar site profile") verbose_name_plural = _("Ishtar site profiles") - ordering = ['label'] + ordering = ["label"] def __str__(self): return str(self.label) @@ -1008,18 +1318,22 @@ class IshtarSiteProfile(models.Model, Cached): return (self.slug,) def has_overload(self, key): - return self.config and self.config in ALTERNATE_CONFIGS and \ - hasattr(ALTERNATE_CONFIGS[self.config], key) + return ( + self.config + and self.config in ALTERNATE_CONFIGS + and hasattr(ALTERNATE_CONFIGS[self.config], key) + ) @classmethod def get_current_profile(cls, force=False): - cache_key, value = get_cache(cls, ['is-current-profile']) + cache_key, value = get_cache(cls, ["is-current-profile"]) if value and not force: return value q = cls.objects.filter(active=True) if not q.count(): obj = cls.objects.create( - label="Default profile", slug='default', active=True) + label="Default profile", slug="default", active=True + ) else: obj = q.all()[0] cache.set(cache_key, obj, settings.CACHE_TIMEOUT) @@ -1032,14 +1346,12 @@ class IshtarSiteProfile(models.Model, Cached): def get_site_label(self, key=None): if not key: return str(dict(SITE_LABELS)[self.archaeological_site_label]) - return str( - TRANSLATED_SITE_LABELS[self.archaeological_site_label][key] - ) + return str(TRANSLATED_SITE_LABELS[self.archaeological_site_label][key]) def save(self, *args, **kwargs): raw = False - if 'raw' in kwargs: - raw = kwargs.pop('raw') + if "raw" in kwargs: + raw = kwargs.pop("raw") super(IshtarSiteProfile, self).save(*args, **kwargs) obj = self if raw: @@ -1074,6 +1386,7 @@ profile_mapping = lazy(_profile_mapping) def cached_site_changed(sender, **kwargs): get_current_profile(force=True) from ishtar_common.menus import Menu + MAIN_MENU = Menu(None) MAIN_MENU.init() MAIN_MENU.reinit_menu_for_all_user() @@ -1093,27 +1406,35 @@ class CustomForm(models.Model): form = models.CharField(_("Form"), max_length=250) available = models.BooleanField(_("Available"), default=True) enabled = models.BooleanField( - _("Enable this form"), default=True, - help_text=_("Disable with caution: disabling a form with mandatory " - "fields may lead to database errors.")) + _("Enable this form"), + default=True, + help_text=_( + "Disable with caution: disabling a form with mandatory " + "fields may lead to database errors." + ), + ) apply_to_all = models.BooleanField( - _("Apply to all"), default=False, - help_text=_("Apply this form to all users. If set to True, selecting " - "user and user type is useless.")) - users = models.ManyToManyField('IshtarUser', blank=True) + _("Apply to all"), + default=False, + help_text=_( + "Apply this form to all users. If set to True, selecting " + "user and user type is useless." + ), + ) + users = models.ManyToManyField("IshtarUser", blank=True) user_types = models.ManyToManyField( - 'PersonType', blank=True, - help_text=_("Deprecated - use profile types")) + "PersonType", blank=True, help_text=_("Deprecated - use profile types") + ) profile_types = models.ManyToManyField("ProfileType", blank=True) objects = CustomFormManager() - SERIALIZATION_EXCLUDE = ("users", ) + SERIALIZATION_EXCLUDE = ("users",) class Meta: verbose_name = _("Custom form") verbose_name_plural = _("Custom forms") - ordering = ['name', 'form'] - unique_together = (('name', 'form'),) + ordering = ["name", "form"] + unique_together = (("name", "form"),) def natural_key(self): return (self.name, self.form) @@ -1135,12 +1456,14 @@ class CustomForm(models.Model): @classmethod def register(cls): - if hasattr(cls, '_register') and hasattr(cls, '_register_fields'): + if hasattr(cls, "_register") and hasattr(cls, "_register_fields"): return cls._register, cls._register_fields - cache_key, value = get_cache(cls.__class__, ['dct-forms'], - app_label='ishtar_common') + cache_key, value = get_cache( + cls.__class__, ["dct-forms"], app_label="ishtar_common" + ) cache_key_fields, value_fields = get_cache( - cls.__class__, ['dct-fields'], app_label='ishtar_common') + cls.__class__, ["dct-fields"], app_label="ishtar_common" + ) if value and value_fields: cls._register = value cls._register_fields = value_fields @@ -1155,15 +1478,17 @@ class CustomForm(models.Model): if app_name == "archaeological_files_pdl": app_name = "archaeological_files" for form in dir(app_form): - if 'Form' not in form and 'Select' not in form: + if "Form" not in form and "Select" not in form: # not very clean... but do not treat inappropriate items continue form = getattr(app_form, form) - if not inspect.isclass(form) \ - or not issubclass(form, CustomFormForm) \ - or not getattr(form, 'form_slug', None): + if ( + not inspect.isclass(form) + or not issubclass(form, CustomFormForm) + or not getattr(form, "form_slug", None) + ): continue - model_name = form.form_slug.split('-')[0].replace('_', '') + model_name = form.form_slug.split("-")[0].replace("_", "") if app_name not in cls._register_fields: cls._register_fields[app_name] = [] if model_name not in cls._register_fields[app_name]: @@ -1182,36 +1507,43 @@ class CustomForm(models.Model): if self.form not in self._register: return [] current_form = register[self.form] - app_name = current_form.__module__.split('.')[0] + app_name = current_form.__module__.split(".")[0] if app_name == "archaeological_files_pdl": app_name = "archaeological_files" if app_name not in register_fields: return [] res = [] for model_name in register_fields[app_name]: - q = ContentType.objects.filter(app_label=app_name, - model=model_name) + q = ContentType.objects.filter(app_label=app_name, model=model_name) if not q.count(): continue ct = q.all()[0] for json_field in JsonDataField.objects.filter( - content_type=ct).all(): - res.append((json_field.pk, "{} ({})".format( - json_field.name, - dict(JSON_VALUE_TYPES)[json_field.value_type]))) + content_type=ct + ).all(): + res.append( + ( + json_field.pk, + "{} ({})".format( + json_field.name, + dict(JSON_VALUE_TYPES)[json_field.value_type], + ), + ) + ) return res class ExcludedFieldManager(models.Manager): - def get_by_natural_key(self, custom_form_name, custom_form_form, - field): - return self.get(custom_form__name=custom_form_name, - custom_form__form=custom_form_form, - field=field) + def get_by_natural_key(self, custom_form_name, custom_form_form, field): + return self.get( + custom_form__name=custom_form_name, + custom_form__form=custom_form_form, + field=field, + ) class ExcludedField(models.Model): - custom_form = models.ForeignKey(CustomForm, related_name='excluded_fields') + custom_form = models.ForeignKey(CustomForm, related_name="excluded_fields") field = models.CharField(_("Field"), max_length=250) objects = ExcludedFieldManager() @@ -1221,29 +1553,33 @@ class ExcludedField(models.Model): unique_together = ("custom_form", "field") def natural_key(self): - return (self.custom_form.name , self.custom_form.form, - self.field) + return (self.custom_form.name, self.custom_form.form, self.field) class CustomFormJsonFieldManager(models.Manager): - def get_by_natural_key(self, custom_form_name, custom_form_form, - json_field_key, json_field_app_label, - json_field_model): + def get_by_natural_key( + self, + custom_form_name, + custom_form_form, + json_field_key, + json_field_app_label, + json_field_model, + ): return self.get( custom_form__name=custom_form_name, custom_form__form=custom_form_form, json_field__key=json_field_key, json_field__content_type__app_label=json_field_app_label, - json_field__content_type__model=json_field_model + json_field__content_type__model=json_field_model, ) class CustomFormJsonField(models.Model): - custom_form = models.ForeignKey(CustomForm, related_name='json_fields') - json_field = models.ForeignKey(JsonDataField, - related_name='custom_form_details') - label = models.CharField(_("Label"), max_length=200, blank=True, - default='') + custom_form = models.ForeignKey(CustomForm, related_name="json_fields") + json_field = models.ForeignKey( + JsonDataField, related_name="custom_form_details" + ) + label = models.CharField(_("Label"), max_length=200, blank=True, default="") order = models.IntegerField(verbose_name=_("Order"), default=1) help_text = models.TextField(_("Help"), blank=True, default="") objects = CustomFormJsonFieldManager() @@ -1255,23 +1591,26 @@ class CustomFormJsonField(models.Model): def natural_key(self): return ( - self.custom_form.name, self.custom_form.form, - self.json_field.key, self.json_field.content_type.app_label, - self.json_field.content_type.model + self.custom_form.name, + self.custom_form.form, + self.json_field.key, + self.json_field.content_type.app_label, + self.json_field.content_type.model, ) class GlobalVar(models.Model, Cached): slug = models.SlugField(_("Variable name"), unique=True) description = models.TextField( - _("Description of the variable"), blank=True, default="") + _("Description of the variable"), blank=True, default="" + ) value = models.TextField(_("Value"), blank=True, default="") objects = SlugModelManager() class Meta: verbose_name = _("Global variable") verbose_name_plural = _("Global variables") - ordering = ['slug'] + ordering = ["slug"] def natural_key(self): return (self.slug,) @@ -1281,9 +1620,9 @@ class GlobalVar(models.Model, Cached): def cached_globalvar_changed(sender, **kwargs): - if not kwargs['instance']: + if not kwargs["instance"]: return - var = kwargs['instance'] + var = kwargs["instance"] cache_key, value = get_cache(GlobalVar, var.slug) cache.set(cache_key, var.value, settings.CACHE_TIMEOUT) @@ -1293,10 +1632,12 @@ post_save.connect(cached_globalvar_changed, sender=GlobalVar) class UserDashboard: def __init__(self): - types = IshtarUser.objects.values('person__person_types', - 'person__person_types__label') - self.types = types.annotate(number=Count('pk')) \ - .order_by('person__person_types') + types = IshtarUser.objects.values( + "person__person_types", "person__person_types__label" + ) + self.types = types.annotate(number=Count("pk")).order_by( + "person__person_types" + ) class StatsCache(models.Model): @@ -1312,8 +1653,9 @@ class StatsCache(models.Model): class Dashboard(object): - def __init__(self, model, slice='year', date_source=None, show_detail=None, - fltr=None): + def __init__( + self, model, slice="year", date_source=None, show_detail=None, fltr=None + ): if not fltr: fltr = {} # don't provide date_source if it is not relevant @@ -1323,32 +1665,34 @@ class Dashboard(object): history_model = self.model.history.model # last edited - created self.recents, self.lasts = [], [] - for last_lst, modif_type in ((self.lasts, '+'), (self.recents, '~')): - last_ids = history_model.objects.values('id') \ - .annotate(hd=Max('history_date')) + for last_lst, modif_type in ((self.lasts, "+"), (self.recents, "~")): + last_ids = history_model.objects.values("id").annotate( + hd=Max("history_date") + ) last_ids = last_ids.filter(history_type=modif_type) from archaeological_finds.models import Find + if self.model == Find: - last_ids = last_ids.filter( - downstream_treatment_id__isnull=True) - if modif_type == '+': + last_ids = last_ids.filter(downstream_treatment_id__isnull=True) + if modif_type == "+": last_ids = last_ids.filter( - upstream_treatment_id__isnull=True) - last_ids = last_ids.order_by('-hd').distinct().all()[:5] + upstream_treatment_id__isnull=True + ) + last_ids = last_ids.order_by("-hd").distinct().all()[:5] for idx in last_ids: try: - obj = self.model.objects.get(pk=idx['id']) + obj = self.model.objects.get(pk=idx["id"]) except self.model.DoesNotExist: # deleted object are always referenced in history continue - obj.history_date = idx['hd'] + obj.history_date = idx["hd"] last_lst.append(obj) # years - base_kwargs = {'fltr': fltr.copy()} + base_kwargs = {"fltr": fltr.copy()} if date_source: - base_kwargs['date_source'] = date_source + base_kwargs["date_source"] = date_source periods_kwargs = copy.deepcopy(base_kwargs) - periods_kwargs['slice'] = slice + periods_kwargs["slice"] = slice self.periods = model.get_periods(**periods_kwargs) self.periods = list(set(self.periods)) self.periods.sort() @@ -1357,36 +1701,45 @@ class Dashboard(object): kwargs_num = copy.deepcopy(base_kwargs) self.serie_labels = [_("Total")] # numbers - if slice == 'year': - self.values = [('year', "", - list(reversed(self.periods)))] - self.numbers = [model.get_by_year(year, **kwargs_num).count() - for year in self.periods] - self.values += [('number', _("Number"), - list(reversed(self.numbers)))] - if slice == 'month': + if slice == "year": + self.values = [("year", "", list(reversed(self.periods)))] + self.numbers = [ + model.get_by_year(year, **kwargs_num).count() + for year in self.periods + ] + self.values += [ + ("number", _("Number"), list(reversed(self.numbers))) + ] + if slice == "month": periods = list(reversed(self.periods)) - self.periods = ["%d-%s-01" % (p[0], ('0' + str(p[1])) - if len(str(p[1])) == 1 else p[1]) for p in periods] - self.values = [('month', "", self.periods)] + self.periods = [ + "%d-%s-01" + % (p[0], ("0" + str(p[1])) if len(str(p[1])) == 1 else p[1]) + for p in periods + ] + self.values = [("month", "", self.periods)] if show_detail: for dpt, lbl in settings.ISHTAR_DPTS: self.serie_labels.append(str(dpt)) - idx = 'number_' + str(dpt) - kwargs_num['fltr']["towns__numero_insee__startswith"] = \ - str(dpt) - numbers = [model.get_by_month(*p.split('-')[:2], - **kwargs_num).count() - for p in self.periods] + idx = "number_" + str(dpt) + kwargs_num["fltr"]["towns__numero_insee__startswith"] = str( + dpt + ) + numbers = [ + model.get_by_month( + *p.split("-")[:2], **kwargs_num + ).count() + for p in self.periods + ] self.values += [(idx, dpt, list(numbers))] # put "Total" at the end self.serie_labels.append(self.serie_labels.pop(0)) kwargs_num = base_kwargs.copy() - self.numbers = [model.get_by_month(*p.split('-')[:2], - **kwargs_num).count() - for p in self.periods] - self.values += [('number', _("Total"), - list(self.numbers))] + self.numbers = [ + model.get_by_month(*p.split("-")[:2], **kwargs_num).count() + for p in self.periods + ] + self.values += [("number", _("Total"), list(self.numbers))] # calculate self.average = self.get_average() self.variance = self.get_variance() @@ -1394,23 +1747,28 @@ class Dashboard(object): self.median = self.get_median() self.mode = self.get_mode() # by operation - if not hasattr(model, 'get_by_operation'): + if not hasattr(model, "get_by_operation"): return operations = model.get_operations() - operation_numbers = [model.get_by_operation(op).count() - for op in operations] + operation_numbers = [ + model.get_by_operation(op).count() for op in operations + ] # calculate self.operation_average = self.get_average(operation_numbers) self.operation_variance = self.get_variance(operation_numbers) self.operation_standard_deviation = self.get_standard_deviation( - operation_numbers) + operation_numbers + ) self.operation_median = self.get_median(operation_numbers) - operation_mode_pk = self.get_mode(dict(zip(operations, - operation_numbers))) + operation_mode_pk = self.get_mode( + dict(zip(operations, operation_numbers)) + ) if operation_mode_pk: from archaeological_operations.models import Operation + self.operation_mode = str( - Operation.objects.get(pk=operation_mode_pk)) + Operation.objects.get(pk=operation_mode_pk) + ) def get_average(self, vals=None): if not vals: @@ -1436,8 +1794,7 @@ class Dashboard(object): if (len_vals % 2) == 1: return vals[int(len_vals / 2)] else: - return (vals[int(len_vals / 2) - 1] + - vals[int(len_vals / 2)]) / 2.0 + return (vals[int(len_vals / 2) - 1] + vals[int(len_vals / 2)]) / 2.0 def get_mode(self, vals=None): if not vals: @@ -1453,34 +1810,47 @@ class DocumentTemplate(models.Model): slug = models.SlugField(_("Slug"), max_length=100, unique=True) associated_model = models.ForeignKey(ImporterModel) template = models.FileField( - _("Template"), upload_to="templates/%Y/", blank=True, null=True, - help_text=max_size_help()) + _("Template"), + upload_to="templates/%Y/", + blank=True, + null=True, + help_text=max_size_help(), + ) label_template = models.FileField( - _("Base template for labels"), upload_to="templates/%Y/", - blank=True, null=True, help_text=max_size_help()) + _("Base template for labels"), + upload_to="templates/%Y/", + blank=True, + null=True, + help_text=max_size_help(), + ) label_targets = models.TextField( _("Labels: targets for labels in the LibreOffice file"), - blank=True, null=True, - help_text=_("Each target is separated by a semi-colon. The first " - "target is the name of the object including the data in " - "base template. Following targets will be filled with the " - "content of the first target. For instance: " - "\"Cadre1;Cadre2;Cadre3;Cadre4;Cadre5;Cadre6\" for a " - "sheet with 6 labels.") + blank=True, + null=True, + help_text=_( + "Each target is separated by a semi-colon. The first " + "target is the name of the object including the data in " + "base template. Following targets will be filled with the " + "content of the first target. For instance: " + '"Cadre1;Cadre2;Cadre3;Cadre4;Cadre5;Cadre6" for a ' + "sheet with 6 labels." + ), ) available = models.BooleanField(_("Available"), default=True) for_labels = models.BooleanField(_("Used for labels"), default=False) label_per_page = models.IntegerField( - _("Number of label per page"), blank=True, null=True, - help_text=_("Only relevant for label template") + _("Number of label per page"), + blank=True, + null=True, + help_text=_("Only relevant for label template"), ) objects = SlugModelManager() - SERIALIZATION_FILES = ("template", ) + SERIALIZATION_FILES = ("template",) class Meta: verbose_name = _("Document template") verbose_name_plural = _("Document templates") - ordering = ['associated_model', 'name'] + ordering = ["associated_model", "name"] def __str__(self): return self.name @@ -1490,8 +1860,12 @@ class DocumentTemplate(models.Model): def clean(self): if self.for_labels and not self.label_per_page: - raise ValidationError(_("For label template, you must provide " - "number of label per page.")) + raise ValidationError( + _( + "For label template, you must provide " + "number of label per page." + ) + ) def generate_label_template(self): if not self.label_template.name or not self.label_targets: @@ -1500,10 +1874,11 @@ class DocumentTemplate(models.Model): base_target = targets[0] try: with zipfile.ZipFile(self.label_template.path) as zip: - with zip.open('content.xml') as content: - soup = BeautifulSoup(content.read(), 'xml') + with zip.open("content.xml") as content: + soup = BeautifulSoup(content.read(), "xml") base_content = soup.find( - "draw:frame", attrs={"draw:name": base_target}) + "draw:frame", attrs={"draw:name": base_target} + ) if not base_content: return base_content = base_content.contents @@ -1520,12 +1895,13 @@ class DocumentTemplate(models.Model): fixed_text = text.replace("items.0", replace_str) text.replace_with(fixed_text) for image in content.find_all( - attrs={"draw:name": re.compile("items.0")}): - image["draw:name"] = image["draw:name"].replace("items.0", - replace_str) + attrs={"draw:name": re.compile("items.0")} + ): + image["draw:name"] = image["draw:name"].replace( + "items.0", replace_str + ) new_content.append(content) - next_target = soup.find( - "draw:frame", attrs={"draw:name": target}) + next_target = soup.find("draw:frame", attrs={"draw:name": target}) if next_target: next_target.contents = new_content @@ -1536,15 +1912,15 @@ class DocumentTemplate(models.Model): sp[-2] += "-label" new_filename = ".".join(sp) new_file = os.path.join(tmp, new_filename) - with zipfile.ZipFile(new_file, 'w') as zip_out: - with zipfile.ZipFile(self.label_template.path, 'r') as zip_in: + with zipfile.ZipFile(new_file, "w") as zip_out: + with zipfile.ZipFile(self.label_template.path, "r") as zip_in: zip_out.comment = zip_in.comment for item in zip_in.infolist(): if item.filename != "content.xml": - zip_out.writestr(item, - zip_in.read(item.filename)) - with zipfile.ZipFile(new_file, mode='a', - compression=zipfile.ZIP_DEFLATED) as zf: + zip_out.writestr(item, zip_in.read(item.filename)) + with zipfile.ZipFile( + new_file, mode="a", compression=zipfile.ZIP_DEFLATED + ) as zf: zf.writestr("content.xml", str(soup)) media_dir = "templates/{}/".format(datetime.date.today().year) @@ -1569,23 +1945,26 @@ class DocumentTemplate(models.Model): if not self.slug: self.slug = create_slug(DocumentTemplate, self.name) super(DocumentTemplate, self).save(*args, **kwargs) - if self.label_template.name and self.label_targets and not \ - self.template: + if ( + self.label_template.name + and self.label_targets + and not self.template + ): self.generate_label_template() @classmethod def get_tuples(cls, dct=None, empty_first=True): if not dct: dct = {} - dct['available'] = True + dct["available"] = True if empty_first: - yield '', '----------' + yield "", "----------" items = cls.objects.filter(**dct) for item in items.distinct().order_by(*cls._meta.ordering).all(): yield item.pk, _(str(item)) def get_baselink_for_labels(self): - return reverse('generate-labels', args=[self.slug]) + return reverse("generate-labels", args=[self.slug]) def _exclude_filter(self, value): """ @@ -1604,36 +1983,48 @@ class DocumentTemplate(models.Model): if not regexp_list: return None z = zipfile.ZipFile(template) - content = z.open('content.xml') + content = z.open("content.xml") full_content = content.read().decode("utf-8") filtr = [] for regexp in regexp_list: - iter = re.finditer( - regexp, - full_content) + iter = re.finditer(regexp, full_content) for s in iter: key = s.groups()[0] if key not in filtr: filtr.append(key) new_filter = [] - OPERATORS = ["==", "not", "in", ">", "<", "!=", ">", "<", ">=", - "<=", "or", ">=", "<="] + OPERATORS = [ + "==", + "not", + "in", + ">", + "<", + "!=", + ">", + "<", + ">=", + "<=", + "or", + ">=", + "<=", + ] for fil in filtr: if not fil: continue - new_filter += [f for f in fil.split(" ") - if f and f not in OPERATORS] + new_filter += [ + f for f in fil.split(" ") if f and f not in OPERATORS + ] filtr = new_filter new_filter = [] for fil in filtr: - keys = fil.strip().split("|")[0].split('.') + keys = fil.strip().split("|")[0].split(".") new_filter += [k for k in keys if not self._exclude_filter(k)] prefix = "" for k in keys: if self._exclude_filter(k): continue if prefix: - prefix += '_' + prefix += "_" if prefix + k in new_filter: continue new_filter.append(prefix + k) @@ -1643,28 +2034,36 @@ class DocumentTemplate(models.Model): ITEM_RE = r"([A-Za-z0-9_.]*)(?:[\[\]0-9-:])*(?:\|[^}]+)*" BASE_RE = [ # {{ key1.key2 }} - r'{{ *' + ITEM_RE + ' *}}', + r"{{ *" + ITEM_RE + " *}}", # {% for item in key1.key2 %} - r'{% *for +[A-Za-z0-9_]+ +in +' + ITEM_RE + r' *%}', + r"{% *for +[A-Za-z0-9_]+ +in +" + ITEM_RE + r" *%}", # {% if ** %} - r'{% (?:el)*if ([^}]*)%}', - ] + r"{% (?:el)*if ([^}]*)%}", + ] def publish(self, c_object): tempdir = tempfile.mkdtemp("-ishtardocs") - output_name = tempdir + os.path.sep + \ - slugify(self.name.replace(' ', '_').lower()) + '-' + \ - datetime.date.today().strftime('%Y-%m-%d') + \ - "." + self.template.name.split('.')[-1] + output_name = ( + tempdir + + os.path.sep + + slugify(self.name.replace(" ", "_").lower()) + + "-" + + datetime.date.today().strftime("%Y-%m-%d") + + "." + + self.template.name.split(".")[-1] + ) filtr = self.get_filter(self.template, self.BASE_RE) # values = c_object.get_values(filtr=[]) if "VALUES" in filtr: filtr = [] values = c_object.get_values(filtr=filtr) - if not filtr or 'VALUES' in filtr: - values['VALUES'] = json.dumps( - values, indent=4, sort_keys=True, - skipkeys=True, ensure_ascii=False, + if not filtr or "VALUES" in filtr: + values["VALUES"] = json.dumps( + values, + indent=4, + sort_keys=True, + skipkeys=True, + ensure_ascii=False, separators=("", " : "), ).replace(" " * 4, "\t") engine = IshtarSecretaryRenderer() @@ -1676,28 +2075,32 @@ class DocumentTemplate(models.Model): raise TemplateSyntaxError(str(e), 0) except Exception as e: raise TemplateSyntaxError(str(e), 0) - with open(output_name, 'wb') as output: + with open(output_name, "wb") as output: output.write(result) return output_name LABEL_ITEM_RE = r"items\.\d\.([A-Za-z0-9_.]*)(?:[\[\]0-9-:])*(?:\|[^}]+)*" LABEL_RE = [ # {{items.4.key}} - r'{{ *' + LABEL_ITEM_RE + r' *}}', + r"{{ *" + LABEL_ITEM_RE + r" *}}", # {% if ** %} - r'{% (?:el)*if ([^}]*)%}', + r"{% (?:el)*if ([^}]*)%}", # {% for item in items.42.another_keys %} - r'{% *for +[A-Za-z0-9_]+ +in +' + LABEL_ITEM_RE + r' *%}', + r"{% *for +[A-Za-z0-9_]+ +in +" + LABEL_ITEM_RE + r" *%}", ] def publish_labels(self, objects): if not objects: return tempdir = tempfile.mkdtemp("-ishtarlabels") - main_output_name = tempdir + os.path.sep + \ - slugify(self.name.replace(' ', '_').lower()) + '-' + \ - datetime.datetime.now().strftime('%Y-%m-%d-%H%M%S') - suffix = "." + self.template.name.split('.')[-1] + main_output_name = ( + tempdir + + os.path.sep + + slugify(self.name.replace(" ", "_").lower()) + + "-" + + datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S") + ) + suffix = "." + self.template.name.split(".")[-1] len_objects = len(objects) names = [] @@ -1719,7 +2122,7 @@ class DocumentTemplate(models.Model): raise TemplateSyntaxError(str(e), e.lineno) output_name = main_output_name + "-" + str(idx) + suffix names.append(output_name) - with open(output_name, 'wb') as output: + with open(output_name, "wb") as output: output.write(result) output_name = main_output_name + suffix o = OOoPy(infile=names[0], outfile=output_name) @@ -1731,28 +2134,34 @@ class DocumentTemplate(models.Model): OOTransforms.renumber_all(o.mimetype), OOTransforms.set_meta(o.mimetype), OOTransforms.Fix_OOo_Tag(), - OOTransforms.Manifest_Append() + OOTransforms.Manifest_Append(), ) - t.transform (o) + t.transform(o) o.close() return output_name class Area(HierarchicalType): - towns = models.ManyToManyField(Town, verbose_name=_("Towns"), blank=True, - related_name='areas') - reference = models.CharField(_("Reference"), max_length=200, blank=True, - null=True) + towns = models.ManyToManyField( + Town, verbose_name=_("Towns"), blank=True, related_name="areas" + ) + reference = models.CharField( + _("Reference"), max_length=200, blank=True, null=True + ) parent = models.ForeignKey( - 'self', blank=True, null=True, verbose_name=_("Parent"), + "self", + blank=True, + null=True, + verbose_name=_("Parent"), help_text=_("Only four level of parent are managed."), - related_name='children', on_delete=models.SET_NULL + related_name="children", + on_delete=models.SET_NULL, ) class Meta: verbose_name = _("Area") verbose_name_plural = _("Areas") - ordering = ('label',) + ordering = ("label",) def __str__(self): if not self.reference: @@ -1785,7 +2194,7 @@ def documentation_get_gender_values(): class BaseGenderedType(ValueGetter): - def get_values(self, prefix='', **kwargs): + def get_values(self, prefix="", **kwargs): dct = super(BaseGenderedType, self).get_values(prefix=prefix, **kwargs) assert hasattr(self, "grammatical_gender") dct[prefix + "grammatical_gender"] = self.grammatical_gender @@ -1794,8 +2203,13 @@ class BaseGenderedType(ValueGetter): class GenderedType(BaseGenderedType, GeneralType): grammatical_gender = models.CharField( - _("Grammatical gender"), max_length=1, choices=GENDER, - blank=True, default="", help_text=documentation_get_gender_values) + _("Grammatical gender"), + max_length=1, + choices=GENDER, + blank=True, + default="", + help_text=documentation_get_gender_values, + ) class Meta: abstract = True @@ -1815,7 +2229,7 @@ class OrganizationType(GenderedType): class Meta: verbose_name = _("Organization type") verbose_name_plural = _("Organization types") - ordering = ('label',) + ordering = ("label",) def get_orga_planning_service_label(): @@ -1841,52 +2255,62 @@ organization_type_pk_lazy = lazy(OrganizationType.get_or_create_pk, str) organization_type_pks_lazy = lazy(OrganizationType.get_or_create_pks, str) -class Organization(Address, Merge, OwnPerms, BaseGenderedType, ValueGetter, - MainItem): - TABLE_COLS = ('name', 'organization_type', 'town') +class Organization( + Address, Merge, OwnPerms, BaseGenderedType, ValueGetter, MainItem +): + TABLE_COLS = ("name", "organization_type", "town") SLUG = "organization" - SHOW_URL = 'show-organization' - DELETE_URL = 'delete-organization' + SHOW_URL = "show-organization" + DELETE_URL = "delete-organization" # search parameters EXTRA_REQUEST_KEYS = {} - BASE_SEARCH_VECTORS = [SearchVectorConfig('name'), - SearchVectorConfig('town')] + BASE_SEARCH_VECTORS = [ + SearchVectorConfig("name"), + SearchVectorConfig("town"), + ] # alternative names of fields for searches ALT_NAMES = { - 'name': SearchAltName( - pgettext_lazy("key for text search", "name"), - 'name__iexact' + "name": SearchAltName( + pgettext_lazy("key for text search", "name"), "name__iexact" ), - 'organization_type': SearchAltName( + "organization_type": SearchAltName( pgettext_lazy("key for text search", "type"), - 'organization_type__label__iexact' + "organization_type__label__iexact", ), } QA_EDIT = QuickAction( - url="organization-qa-bulk-update", icon_class="fa fa-pencil", - text=_("Bulk update"), target="many", - rights=['change_organization']) - QUICK_ACTIONS = [ - QA_EDIT - ] + url="organization-qa-bulk-update", + icon_class="fa fa-pencil", + text=_("Bulk update"), + target="many", + rights=["change_organization"], + ) + QUICK_ACTIONS = [QA_EDIT] objects = UUIDModelManager() # fields uuid = models.UUIDField(default=uuid.uuid4) name = models.CharField(_("Name"), max_length=500) - organization_type = models.ForeignKey(OrganizationType, - verbose_name=_("Type")) + organization_type = models.ForeignKey( + OrganizationType, verbose_name=_("Type") + ) url = models.URLField(verbose_name=_("Web address"), blank=True, null=True) grammatical_gender = models.CharField( - _("Grammatical gender"), max_length=1, choices=GENDER, - blank=True, default="", help_text=documentation_get_gender_values) - cached_label = models.TextField(_("Cached name"), blank=True, default="", - db_index=True) + _("Grammatical gender"), + max_length=1, + choices=GENDER, + blank=True, + default="", + help_text=documentation_get_gender_values, + ) + cached_label = models.TextField( + _("Cached name"), blank=True, default="", db_index=True + ) - DOWN_MODEL_UPDATE = ['members'] + DOWN_MODEL_UPDATE = ["members"] class Meta: verbose_name = _("Organization") @@ -1899,14 +2323,13 @@ class Organization(Address, Merge, OwnPerms, BaseGenderedType, ValueGetter, ("delete_own_organization", "Can delete own Organization"), ) indexes = [ - GinIndex(fields=['data']), + GinIndex(fields=["data"]), ] def simple_lbl(self): if self.name: return self.name - return "{} - {}".format(self.organization_type, - self.town or "") + return "{} - {}".format(self.organization_type, self.town or "") def natural_key(self): return (self.uuid,) @@ -1918,33 +2341,36 @@ class Organization(Address, Merge, OwnPerms, BaseGenderedType, ValueGetter, if self.name: return self.name attrs = ["organization_type", "address", "town"] - items = [str(getattr(self, attr)) - for attr in attrs if getattr(self, attr)] + items = [ + str(getattr(self, attr)) for attr in attrs if getattr(self, attr) + ] if not items: items = [str(_("unknown organization"))] return " - ".join(items) def generate_merge_key(self): - self.merge_key = slugify(self.name or '') + self.merge_key = slugify(self.name or "") if not self.merge_key: self.merge_key = self.EMPTY_MERGE_KEY if self.town: - self.merge_key += "-" + slugify(self.town or '') + self.merge_key += "-" + slugify(self.town or "") if self.address: - self.merge_key += "-" + slugify(self.address or '') + self.merge_key += "-" + slugify(self.address or "") @property def associated_filename(self): - values = [str(getattr(self, attr)) - for attr in ('organization_type', 'name') - if getattr(self, attr)] + values = [ + str(getattr(self, attr)) + for attr in ("organization_type", "name") + if getattr(self, attr) + ] return slugify("-".join(values)) @classmethod @pre_importer_action def import_get_publisher_type(cls, context, value): if context["name"]: - q = OrganizationType.objects.filter(txt_idx='publisher') + q = OrganizationType.objects.filter(txt_idx="publisher") if not q.count(): return context["organization_type"] = q.all()[0] @@ -1957,7 +2383,7 @@ class PersonType(GeneralType): class Meta: verbose_name = _("Person type") verbose_name_plural = _("Person types") - ordering = ('label',) + ordering = ("label",) post_save.connect(post_save_cache, sender=PersonType) @@ -2030,7 +2456,7 @@ class TitleType(GenderedType): class Meta: verbose_name = _("Title type") verbose_name_plural = _("Title types") - ordering = ('label',) + ordering = ("label",) @classmethod def get_documentation_string(cls): @@ -2041,7 +2467,7 @@ class TitleType(GenderedType): doc += ", **long_title** {}".format(_("Long title")) return doc - def get_values(self, prefix='', **kwargs): + def get_values(self, prefix="", **kwargs): dct = super(TitleType, self).get_values(prefix=prefix, **kwargs) dct[prefix + "long_title"] = self.long_title return dct @@ -2053,109 +2479,139 @@ post_delete.connect(post_save_cache, sender=TitleType) class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): SLUG = "person" - _prefix = 'person_' + _prefix = "person_" TYPE = ( - ('Mr', _('Mr')), - ('Ms', _('Miss')), - ('Mr and Miss', _('Mr and Mrs')), - ('Md', _('Mrs')), - ('Dr', _('Doctor')), - ) - TABLE_COLS = ('name', 'surname', 'raw_name', 'email', 'person_types_list', - 'attached_to', 'town') - TABLE_COLS_ACCOUNT = ('name', 'surname', 'raw_name', 'email', - 'profiles_list', 'attached_to', 'town') - SHOW_URL = 'show-person' - MODIFY_URL = 'person_modify' - DELETE_URL = 'person_delete' + ("Mr", _("Mr")), + ("Ms", _("Miss")), + ("Mr and Miss", _("Mr and Mrs")), + ("Md", _("Mrs")), + ("Dr", _("Doctor")), + ) + TABLE_COLS = ( + "name", + "surname", + "raw_name", + "email", + "person_types_list", + "attached_to", + "town", + ) + TABLE_COLS_ACCOUNT = ( + "name", + "surname", + "raw_name", + "email", + "profiles_list", + "attached_to", + "town", + ) + SHOW_URL = "show-person" + MODIFY_URL = "person_modify" + DELETE_URL = "person_delete" BASE_SEARCH_VECTORS = [ - SearchVectorConfig('name'), - SearchVectorConfig('surname'), - SearchVectorConfig('raw_name'), - SearchVectorConfig('town'), - SearchVectorConfig('attached_to__name'), - SearchVectorConfig('email')] + SearchVectorConfig("name"), + SearchVectorConfig("surname"), + SearchVectorConfig("raw_name"), + SearchVectorConfig("town"), + SearchVectorConfig("attached_to__name"), + SearchVectorConfig("email"), + ] # search parameters - REVERSED_BOOL_FIELDS = ['ishtaruser__isnull'] + REVERSED_BOOL_FIELDS = ["ishtaruser__isnull"] EXTRA_REQUEST_KEYS = { - 'ishtaruser__isnull': 'ishtaruser__isnull', - 'attached_to': 'attached_to', - } - COL_LABELS = { - 'attached_to': _("Organization") + "ishtaruser__isnull": "ishtaruser__isnull", + "attached_to": "attached_to", } + COL_LABELS = {"attached_to": _("Organization")} # alternative names of fields for searches ALT_NAMES = { - 'name': SearchAltName( - pgettext_lazy("key for text search", "name"), - 'name__iexact' + "name": SearchAltName( + pgettext_lazy("key for text search", "name"), "name__iexact" ), - 'surname': SearchAltName( - pgettext_lazy("key for text search", "surname"), - 'surname__iexact' + "surname": SearchAltName( + pgettext_lazy("key for text search", "surname"), "surname__iexact" ), - 'email': SearchAltName( - pgettext_lazy("key for text search", "email"), - 'email__iexact' + "email": SearchAltName( + pgettext_lazy("key for text search", "email"), "email__iexact" ), - 'person_types': SearchAltName( + "person_types": SearchAltName( pgettext_lazy("key for text search", "type"), - 'person_types__label__iexact' + "person_types__label__iexact", ), - 'attached_to': SearchAltName( + "attached_to": SearchAltName( pgettext_lazy("key for text search", "organization"), - 'attached_to__cached_label__iexact' + "attached_to__cached_label__iexact", ), - 'ishtaruser__isnull': SearchAltName( + "ishtaruser__isnull": SearchAltName( pgettext_lazy("key for text search", "has-account"), - 'ishtaruser__isnull' + "ishtaruser__isnull", ), } QA_EDIT = QuickAction( - url="person-qa-bulk-update", icon_class="fa fa-pencil", - text=_("Bulk update"), target="many", - rights=['change_person']) - QUICK_ACTIONS = [ - QA_EDIT - ] + url="person-qa-bulk-update", + icon_class="fa fa-pencil", + text=_("Bulk update"), + target="many", + rights=["change_person"], + ) + QUICK_ACTIONS = [QA_EDIT] objects = UUIDModelManager() # fields uuid = models.UUIDField(default=uuid.uuid4) - old_title = models.CharField(_("Title"), max_length=100, choices=TYPE, - blank=True, null=True) - title = models.ForeignKey(TitleType, verbose_name=_("Title"), - on_delete=models.SET_NULL, - blank=True, null=True) - salutation = models.CharField(_("Salutation"), max_length=200, - blank=True, null=True) + old_title = models.CharField( + _("Title"), max_length=100, choices=TYPE, blank=True, null=True + ) + title = models.ForeignKey( + TitleType, + verbose_name=_("Title"), + on_delete=models.SET_NULL, + blank=True, + null=True, + ) + salutation = models.CharField( + _("Salutation"), max_length=200, blank=True, null=True + ) surname = models.CharField( - _("Surname"), max_length=50, blank=True, null=True, - help_text=_("Attention, historical and unfortunate residue in the " - "code of an initial translation error.")) - name = models.CharField(_("Name"), max_length=200, blank=True, - null=True) - raw_name = models.CharField(_("Raw name"), max_length=300, blank=True, - null=True) - contact_type = models.CharField(_("Contact type"), max_length=300, - blank=True, null=True) + _("Surname"), + max_length=50, + blank=True, + null=True, + help_text=_( + "Attention, historical and unfortunate residue in the " + "code of an initial translation error." + ), + ) + name = models.CharField(_("Name"), max_length=200, blank=True, null=True) + raw_name = models.CharField( + _("Raw name"), max_length=300, blank=True, null=True + ) + contact_type = models.CharField( + _("Contact type"), max_length=300, blank=True, null=True + ) comment = models.TextField(_("Comment"), blank=True, default="") person_types = models.ManyToManyField(PersonType, verbose_name=_("Types")) attached_to = models.ForeignKey( - 'Organization', related_name='members', on_delete=models.SET_NULL, - verbose_name=_("Is attached to"), blank=True, null=True) - cached_label = models.TextField(_("Cached name"), blank=True, default="", - db_index=True) + "Organization", + related_name="members", + on_delete=models.SET_NULL, + verbose_name=_("Is attached to"), + blank=True, + null=True, + ) + cached_label = models.TextField( + _("Cached name"), blank=True, default="", db_index=True + ) DOWN_MODEL_UPDATE = ["author"] class Meta: verbose_name = _("Person") verbose_name_plural = _("Persons") indexes = [ - GinIndex(fields=['data']), + GinIndex(fields=["data"]), ] permissions = ( ("view_person", "Can view all Persons"), @@ -2176,7 +2632,9 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): def full_title(self): return " ".join( str(getattr(self, attr)) - for attr in ['title', 'salutation'] if getattr(self, attr)) + for attr in ["title", "salutation"] + if getattr(self, attr) + ) @property def current_profile(self): @@ -2202,13 +2660,18 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): return [] items.append( """{}""".format( - self.attached_to.name)) + self.attached_to.name + ) + ) items += orga_address return items def simple_lbl(self): - values = [str(getattr(self, attr)) for attr in ('surname', 'name') - if getattr(self, attr)] + values = [ + str(getattr(self, attr)) + for attr in ("surname", "name") + if getattr(self, attr) + ] if not values and self.raw_name: values = [self.raw_name] return " ".join(values) @@ -2217,7 +2680,7 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): return self.cached_label or "" def _generate_cached_label(self): - lbl = get_generated_id('person_raw_name', self) + lbl = get_generated_id("person_raw_name", self) if not lbl: return "-" if self.attached_to: @@ -2227,8 +2690,11 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): def fancy_str(self): values = [""] - values += [str(getattr(self, attr)) for attr in ('surname', 'name') - if getattr(self, attr)] + values += [ + str(getattr(self, attr)) + for attr in ("surname", "name") + if getattr(self, attr) + ] if not values and self.raw_name: values += [self.raw_name] values += [""] @@ -2236,16 +2702,18 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): if self.attached_to: attached_to = str(self.attached_to) if values: - values.append('-') + values.append("-") values.append(attached_to) return " ".join(values) - def get_values(self, prefix='', no_values=False, filtr=None, **kwargs): + def get_values(self, prefix="", no_values=False, filtr=None, **kwargs): values = super(Person, self).get_values( - prefix=prefix, no_values=no_values, filtr=filtr, **kwargs) + prefix=prefix, no_values=no_values, filtr=filtr, **kwargs + ) if not self.attached_to: values.update( - Person.get_empty_values(prefix=prefix + 'attached_to_')) + Person.get_empty_values(prefix=prefix + "attached_to_") + ) return values person_types_list_lbl = _("Types") @@ -2262,9 +2730,9 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): def generate_merge_key(self): if self.name and self.name.strip(): - self.merge_key = slugify(self.name.strip()) + \ - (('-' + slugify(self.surname.strip())) - if self.surname else '') + self.merge_key = slugify(self.name.strip()) + ( + ("-" + slugify(self.surname.strip())) if self.surname else "" + ) elif self.raw_name and self.raw_name.strip(): self.merge_key = slugify(self.raw_name.strip()) elif self.attached_to: @@ -2278,46 +2746,67 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): return not self.attached_to def has_right(self, right_name, session=None, obj=None): - if '.' in right_name: - right_name = right_name.split('.')[-1] + if "." in right_name: + right_name = right_name.split(".")[-1] res, cache_key = "", "" if session: - cache_key = 'session-{}-{}'.format(session.session_key, right_name) + cache_key = "session-{}-{}".format(session.session_key, right_name) res = cache.get(cache_key) if res in (True, False): return res # list all cache key in order to clean them on profile change - cache_key_list = 'sessionlist-{}'.format(session.session_key) + cache_key_list = "sessionlist-{}".format(session.session_key) key_list = cache.get(cache_key_list, []) key_list.append(cache_key) cache.set(cache_key_list, key_list, settings.CACHE_TIMEOUT) if type(right_name) in (list, tuple): - res = bool(self.profiles.filter( - current=True, - profile_type__txt_idx__in=right_name).count()) or \ - bool(self.profiles.filter( - current=True, - profile_type__groups__permissions__codename__in=right_name - ).count()) or \ - bool(self.ishtaruser.user_ptr.groups.filter( - permissions__codename__in=right_name - ).count()) or \ - bool(self.ishtaruser.user_ptr.user_permissions.filter( - codename__in=right_name).count()) + res = ( + bool( + self.profiles.filter( + current=True, profile_type__txt_idx__in=right_name + ).count() + ) + or bool( + self.profiles.filter( + current=True, + profile_type__groups__permissions__codename__in=right_name, + ).count() + ) + or bool( + self.ishtaruser.user_ptr.groups.filter( + permissions__codename__in=right_name + ).count() + ) + or bool( + self.ishtaruser.user_ptr.user_permissions.filter( + codename__in=right_name + ).count() + ) + ) else: - res = bool( - self.profiles.filter( - current=True, - profile_type__txt_idx=right_name).count()) or \ - bool(self.profiles.filter( - current=True, - profile_type__groups__permissions__codename=right_name - ).count()) or \ - bool(self.ishtaruser.user_ptr.groups.filter( - permissions__codename__in=[right_name] - ).count()) or \ - bool(self.ishtaruser.user_ptr.user_permissions.filter( - codename__in=[right_name]).count()) + res = ( + bool( + self.profiles.filter( + current=True, profile_type__txt_idx=right_name + ).count() + ) + or bool( + self.profiles.filter( + current=True, + profile_type__groups__permissions__codename=right_name, + ).count() + ) + or bool( + self.ishtaruser.user_ptr.groups.filter( + permissions__codename__in=[right_name] + ).count() + ) + or bool( + self.ishtaruser.user_ptr.user_permissions.filter( + codename__in=[right_name] + ).count() + ) + ) if session: cache.set(cache_key, res, settings.CACHE_TIMEOUT) return res @@ -2326,9 +2815,11 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): values = [] if self.title: values = [self.title.label] - values += [str(getattr(self, attr)) - for attr in ('salutation', 'surname', 'name') - if getattr(self, attr)] + values += [ + str(getattr(self, attr)) + for attr in ("salutation", "surname", "name") + if getattr(self, attr) + ] if not values and self.raw_name: values = [self.raw_name] if self.attached_to: @@ -2337,9 +2828,11 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): @property def associated_filename(self): - values = [str(getattr(self, attr)) - for attr in ('surname', 'name', 'attached_to') - if getattr(self, attr)] + values = [ + str(getattr(self, attr)) + for attr in ("surname", "name", "attached_to") + if getattr(self, attr) + ] return slugify("-".join(values)) def docs_q(self): @@ -2347,52 +2840,56 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): def operation_docs_q(self): return Document.objects.filter( - authors__person=self, operations__pk__isnull=False) + authors__person=self, operations__pk__isnull=False + ) def contextrecord_docs_q(self): return Document.objects.filter( - authors__person=self, context_records__pk__isnull=False) + authors__person=self, context_records__pk__isnull=False + ) def find_docs_q(self): return Document.objects.filter( - authors__person=self, finds__pk__isnull=False) + authors__person=self, finds__pk__isnull=False + ) def save(self, *args, **kwargs): super(Person, self).save(*args, **kwargs) - raw_name = get_generated_id('person_raw_name', self) + raw_name = get_generated_id("person_raw_name", self) if raw_name and self.raw_name != raw_name: self.raw_name = raw_name self.save() - if hasattr(self, 'responsible_town_planning_service'): + if hasattr(self, "responsible_town_planning_service"): for fle in self.responsible_town_planning_service.all(): fle.save() # force update of raw_town_planning_service - if hasattr(self, 'general_contractor'): + if hasattr(self, "general_contractor"): for fle in self.general_contractor.all(): fle.save() # force update of raw_general_contractor @classmethod def get_query_owns(cls, ishtaruser): - return \ - Q(operation_scientist_responsability__collaborators__ishtaruser - =ishtaruser) | \ - Q(operation_scientist_responsability__scientist__ishtaruser - =ishtaruser) | \ - Q(operation_collaborator__collaborators__ishtaruser - =ishtaruser) | \ - Q(operation_collaborator__scientist__ishtaruser=ishtaruser) + return ( + Q( + operation_scientist_responsability__collaborators__ishtaruser=ishtaruser + ) + | Q( + operation_scientist_responsability__scientist__ishtaruser=ishtaruser + ) + | Q(operation_collaborator__collaborators__ishtaruser=ishtaruser) + | Q(operation_collaborator__scientist__ishtaruser=ishtaruser) + ) post_save.connect(cached_label_changed, sender=Person) class ProfileType(GeneralType): - groups = models.ManyToManyField(Group, verbose_name=_("Groups"), - blank=True) + groups = models.ManyToManyField(Group, verbose_name=_("Groups"), blank=True) class Meta: verbose_name = _("Profile type") verbose_name_plural = _("Profile types") - ordering = ('label',) + ordering = ("label",) post_save.connect(post_save_cache, sender=ProfileType) @@ -2409,36 +2906,42 @@ class ProfileTypeSummary(ProfileType): class UserProfile(models.Model): name = models.CharField(_("Name"), blank=True, default="", max_length=100) profile_type = models.ForeignKey( - ProfileType, verbose_name=_("Profile type")) - areas = models.ManyToManyField("Area", verbose_name=_("Areas"), - blank=True, related_name='profiles') + ProfileType, verbose_name=_("Profile type") + ) + areas = models.ManyToManyField( + "Area", verbose_name=_("Areas"), blank=True, related_name="profiles" + ) current = models.BooleanField(_("Current profile"), default=False) show_field_number = models.BooleanField( - _("Show field number"), default=False) + _("Show field number"), default=False + ) auto_pin = models.BooleanField(_("Automatically pin"), default=False) - display_pin_menu = models.BooleanField(_("Display pin menu"), - default=False) + display_pin_menu = models.BooleanField(_("Display pin menu"), default=False) person = models.ForeignKey( - Person, verbose_name=_("Person"), related_name='profiles') + Person, verbose_name=_("Person"), related_name="profiles" + ) class Meta: verbose_name = _("User profile") verbose_name_plural = _("User profiles") - unique_together = (('name', 'profile_type', 'person'),) + unique_together = (("name", "profile_type", "person"),) def __str__(self): lbl = self.name or str(self.profile_type) if not self.areas.count(): return lbl - return "{} ({})".format(lbl, ", ".join(str(area) for area in self.areas.all())) + return "{} ({})".format( + lbl, ", ".join(str(area) for area in self.areas.all()) + ) @property def query_towns(self): return Town.objects.filter( - Q(areas__profiles=self) | Q(areas__parent__profiles=self) | - Q(areas__parent__parent__profiles=self) | - Q(areas__parent__parent__parent__profiles=self) | - Q(areas__parent__parent__parent__parent__profiles=self) + Q(areas__profiles=self) + | Q(areas__parent__profiles=self) + | Q(areas__parent__parent__profiles=self) + | Q(areas__parent__parent__parent__profiles=self) + | Q(areas__parent__parent__parent__parent__profiles=self) ) @property @@ -2451,12 +2954,12 @@ class UserProfile(models.Model): new_item.pk = None name = self.name for key in kwargs: - if key == 'name': + if key == "name": name = kwargs[key] setattr(new_item, key, kwargs[key]) while UserProfile.objects.filter( - name=name, profile_type=self.profile_type, - person=self.person).count(): + name=name, profile_type=self.profile_type, person=self.person + ).count(): name += str(_(" - duplicate")) new_item.name = name new_item.save() @@ -2464,17 +2967,26 @@ class UserProfile(models.Model): new_item.areas.add(area) return new_item - def save(self, force_insert=False, force_update=False, using=None, - update_fields=None): + def save( + self, + force_insert=False, + force_update=False, + using=None, + update_fields=None, + ): super(UserProfile, self).save( - force_insert=force_insert, force_update=force_update, using=using, - update_fields=update_fields) + force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields, + ) # only one current profile per user if not self.current: return q = UserProfile.objects.filter( - person=self.person, current=True).exclude(pk=self.pk) + person=self.person, current=True + ).exclude(pk=self.pk) if not q.count(): return for p in q.all(): @@ -2483,9 +2995,9 @@ class UserProfile(models.Model): def post_save_userprofile(sender, **kwargs): - if not kwargs.get('instance'): + if not kwargs.get("instance"): return - instance = kwargs.get('instance') + instance = kwargs.get("instance") try: instance.person.ishtaruser.show_field_number(update=True) except IshtarUser.DoesNotExist: @@ -2497,64 +3009,72 @@ post_save.connect(post_save_userprofile, sender=UserProfile) class IshtarUser(FullSearch): SLUG = "ishtaruser" - TABLE_COLS = ('username', 'person__name', 'person__surname', - 'person__email', 'person__person_types_list', - 'person__attached_to__name') + TABLE_COLS = ( + "username", + "person__name", + "person__surname", + "person__email", + "person__person_types_list", + "person__attached_to__name", + ) BASE_SEARCH_VECTORS = [ - SearchVectorConfig('user_ptr__username'), - SearchVectorConfig('person__name'), - SearchVectorConfig('person__surname'), - SearchVectorConfig('person__email'), - SearchVectorConfig('person__town'), - SearchVectorConfig('person__attached_to__name')] + SearchVectorConfig("user_ptr__username"), + SearchVectorConfig("person__name"), + SearchVectorConfig("person__surname"), + SearchVectorConfig("person__email"), + SearchVectorConfig("person__town"), + SearchVectorConfig("person__attached_to__name"), + ] CACHED_LABELS = [] # needed to force search vector update # search parameters EXTRA_REQUEST_KEYS = { - 'person__person_types_list': 'person__person_types__label' + "person__person_types_list": "person__person_types__label" } COL_LABELS = { - 'person__attached_to__name': _("Organization"), - 'username': _("Username") + "person__attached_to__name": _("Organization"), + "username": _("Username"), } # alternative names of fields for searches ALT_NAMES = { - 'username': SearchAltName( + "username": SearchAltName( pgettext_lazy("key for text search", "username"), - 'user_ptr__username__iexact' + "user_ptr__username__iexact", ), - 'name': SearchAltName( - pgettext_lazy("key for text search", "name"), - 'person__name__iexact' + "name": SearchAltName( + pgettext_lazy("key for text search", "name"), "person__name__iexact" ), - 'surname': SearchAltName( + "surname": SearchAltName( pgettext_lazy("key for text search", "surname"), - 'person__surname__iexact' + "person__surname__iexact", ), - 'email': SearchAltName( + "email": SearchAltName( pgettext_lazy("key for text search", "email"), - 'person__email__iexact' + "person__email__iexact", ), - 'person_types': SearchAltName( + "person_types": SearchAltName( pgettext_lazy("key for text search", "type"), - 'person__person_types__label__iexact' + "person__person_types__label__iexact", ), - 'attached_to': SearchAltName( + "attached_to": SearchAltName( pgettext_lazy("key for text search", "organization"), - 'person__attached_to__cached_label__iexact' + "person__attached_to__cached_label__iexact", ), } # fields - user_ptr = models.OneToOneField(User, primary_key=True, - related_name='ishtaruser') - person = models.OneToOneField(Person, verbose_name=_("Person"), - related_name='ishtaruser') + user_ptr = models.OneToOneField( + User, primary_key=True, related_name="ishtaruser" + ) + person = models.OneToOneField( + Person, verbose_name=_("Person"), related_name="ishtaruser" + ) advanced_shortcut_menu = models.BooleanField( - _("Advanced shortcut menu"), default=False) + _("Advanced shortcut menu"), default=False + ) class Meta: verbose_name = _("Ishtar user") @@ -2564,7 +3084,7 @@ class IshtarUser(FullSearch): return str(self.person) def show_field_number(self, update=False): - cache_key, value = get_cache(self.__class__, ['show_field_number']) + cache_key, value = get_cache(self.__class__, ["show_field_number"]) if not update and value is not None: return value value = False @@ -2577,8 +3097,8 @@ class IshtarUser(FullSearch): def current_profile_name(self): q = UserProfile.objects.filter(current=True, person__ishtaruser=self) if q.count(): - vals = q.values('profile_type__label', 'name').all()[0] - return vals['name'] or vals['profile_type__label'] + vals = q.values("profile_type__label", "name").all()[0] + return vals["name"] or vals["profile_type__label"] profile = self.person.current_profile if not profile: return "" @@ -2596,14 +3116,16 @@ class IshtarUser(FullSearch): ishtaruser = q.all()[0] person = ishtaruser.person admin, created = ProfileType.objects.get_or_create( - txt_idx='administrator') + txt_idx="administrator" + ) if user.is_superuser: if UserProfile.objects.filter( - profile_type=admin, person=person).count(): + profile_type=admin, person=person + ).count(): return UserProfile.objects.get_or_create( - profile_type=admin, person=person, - defaults={'current': True}) + profile_type=admin, person=person, defaults={"current": True} + ) @classmethod def create_from_user(cls, user): @@ -2611,9 +3133,9 @@ class IshtarUser(FullSearch): surname = user.first_name or default name = user.last_name or default email = user.email - person = Person.objects.create(surname=surname, - name=name, email=email, - history_modifier=user) + person = Person.objects.create( + surname=surname, name=name, email=email, history_modifier=user + ) return cls.objects.create(user_ptr=user, person=person) def has_right(self, right_name, session=None): @@ -2634,6 +3156,7 @@ class Basket(FullSearch, OwnPerms, ValueGetter, TemplateItem): Abstract class for a basket Subclass must be defined with an "items" ManyToManyField """ + IS_BASKET = True uuid = models.UUIDField(default=uuid.uuid4) label = models.CharField(_("Label"), max_length=1000) @@ -2641,47 +3164,59 @@ class Basket(FullSearch, OwnPerms, ValueGetter, TemplateItem): slug = models.SlugField(_("Slug"), blank=True, null=True) public = models.BooleanField(_("Public"), default=False) user = models.ForeignKey( - IshtarUser, blank=True, null=True, related_name='%(class)ss', + IshtarUser, + blank=True, + null=True, + related_name="%(class)ss", on_delete=models.SET_NULL, - verbose_name=_("Owner")) + verbose_name=_("Owner"), + ) available = models.BooleanField(_("Available"), default=True) shared_with = models.ManyToManyField( - IshtarUser, verbose_name=_("Shared (read) with"), blank=True, - related_name='shared_%(class)ss' + IshtarUser, + verbose_name=_("Shared (read) with"), + blank=True, + related_name="shared_%(class)ss", ) shared_write_with = models.ManyToManyField( - IshtarUser, verbose_name=_("Shared (read/edit) with"), blank=True, - related_name='shared_write_%(class)ss' + IshtarUser, + verbose_name=_("Shared (read/edit) with"), + blank=True, + related_name="shared_write_%(class)ss", ) objects = UUIDModelManager() - TABLE_COLS = ['label', 'user'] + TABLE_COLS = ["label", "user"] BASE_SEARCH_VECTORS = [ - SearchVectorConfig('label'), SearchVectorConfig('comment', 'local'), + SearchVectorConfig("label"), + SearchVectorConfig("comment", "local"), ] - PARENT_SEARCH_VECTORS = ['user'] + PARENT_SEARCH_VECTORS = ["user"] # M2M_SEARCH_VECTORS = [SearchVectorConfig('items')] CACHED_LABELS = [] # needed to force search vector update class Meta: abstract = True - ordering = ('label', ) - unique_together = (('label', 'user'),) + ordering = ("label",) + unique_together = (("label", "user"),) def __str__(self): return self.label def natural_key(self): - return (self.uuid, ) + return (self.uuid,) @classmethod def BASE_REQUEST(cls, request): - if not request.user or not getattr(request.user, 'ishtaruser', None): + if not request.user or not getattr(request.user, "ishtaruser", None): return Q(pk=None) ishtaruser = request.user.ishtaruser - return Q(user=ishtaruser) | Q(shared_with=ishtaruser) | Q( - shared_write_with=ishtaruser) + return ( + Q(user=ishtaruser) + | Q(shared_with=ishtaruser) + | Q(shared_write_with=ishtaruser) + ) @property def cached_label(self): @@ -2693,22 +3228,25 @@ class Basket(FullSearch, OwnPerms, ValueGetter, TemplateItem): @classmethod def get_short_menu_class(cls, pk): - return 'basket' + return "basket" @property def associated_filename(self): - return "{}-{}".format(datetime.date.today().strftime( - "%Y-%m-%d"), slugify(self.label)) + return "{}-{}".format( + datetime.date.today().strftime("%Y-%m-%d"), slugify(self.label) + ) @classmethod def get_query_owns(cls, ishtaruser): - return Q(user=ishtaruser) | Q(shared_with=ishtaruser) | Q( - shared_write_with=ishtaruser) + return ( + Q(user=ishtaruser) + | Q(shared_with=ishtaruser) + | Q(shared_write_with=ishtaruser) + ) @classmethod def get_write_query_owns(cls, ishtaruser): - return Q(user=ishtaruser) | Q( - shared_write_with=ishtaruser) + return Q(user=ishtaruser) | Q(shared_write_with=ishtaruser) def duplicate(self, label=None, ishtaruser=None): """ @@ -2721,7 +3259,9 @@ class Basket(FullSearch, OwnPerms, ValueGetter, TemplateItem): basket_pk = "{}_id".format(self.SLUG) item_pk = "{}_id".format(self.items.model.SLUG) q = through.objects.filter(**{basket_pk: self.pk}) - items = [r[item_pk] for r in q.values("pk", item_pk).order_by("pk").all()] + items = [ + r[item_pk] for r in q.values("pk", item_pk).order_by("pk").all() + ] new_item = self new_item.pk = None if ishtaruser: @@ -2729,7 +3269,8 @@ class Basket(FullSearch, OwnPerms, ValueGetter, TemplateItem): if not label: label = new_item.label while self.__class__.objects.filter( - label=label, user=new_item.user).count(): + label=label, user=new_item.user + ).count(): label += str(_(" - duplicate")) new_item.label = label new_item.save() @@ -2744,7 +3285,7 @@ class AuthorType(GeneralType): class Meta: verbose_name = _("Author type") verbose_name_plural = _("Author types") - ordering = ['order', 'label'] + ordering = ["order", "label"] post_save.connect(post_save_cache, sender=AuthorType) @@ -2753,20 +3294,22 @@ post_delete.connect(post_save_cache, sender=AuthorType) class Author(FullSearch): SLUG = "author" - PARENT_SEARCH_VECTORS = ['person'] + PARENT_SEARCH_VECTORS = ["person"] uuid = models.UUIDField(default=uuid.uuid4) - person = models.ForeignKey(Person, verbose_name=_("Person"), - related_name='author') + person = models.ForeignKey( + Person, verbose_name=_("Person"), related_name="author" + ) author_type = models.ForeignKey(AuthorType, verbose_name=_("Author type")) - cached_label = models.TextField(_("Cached name"), blank=True, default="", - db_index=True) + cached_label = models.TextField( + _("Cached name"), blank=True, default="", db_index=True + ) objects = UUIDModelManager() class Meta: verbose_name = _("Author") verbose_name_plural = _("Authors") - ordering = ('author_type__order', 'person__name') + ordering = ("author_type__order", "person__name") permissions = ( ("view_author", "Can view all Authors"), ("view_own_author", "Can view own Author"), @@ -2779,39 +3322,37 @@ class Author(FullSearch): return self.cached_label or "" def natural_key(self): - return self.uuid, + return (self.uuid,) def _generate_cached_label(self): - return str(self.person) + settings.JOINT + \ - str(self.author_type) + return str(self.person) + settings.JOINT + str(self.author_type) def fancy_str(self): - return self.person.fancy_str() + settings.JOINT + \ - str(self.author_type) + return self.person.fancy_str() + settings.JOINT + str(self.author_type) def related_sources(self): - return list(self.treatmentsource_related.all()) + \ - list(self.operationsource_related.all()) + \ - list(self.findsource_related.all()) + \ - list(self.contextrecordsource_related.all()) + return ( + list(self.treatmentsource_related.all()) + + list(self.operationsource_related.all()) + + list(self.findsource_related.all()) + + list(self.contextrecordsource_related.all()) + ) def public_representation(self): - return { - "type": str(self.author_type), - "person": str(self.person) - } + return {"type": str(self.author_type), "person": str(self.person)} def merge(self, item, keep_old=False): merge_model_objects(self, item, keep_old=keep_old) def author_post_save(sender, **kwargs): - if not kwargs.get('instance'): + if not kwargs.get("instance"): return cached_label_changed(sender, **kwargs) - instance = kwargs.get('instance') - q = Author.objects.filter(person=instance.person, - author_type=instance.author_type) + instance = kwargs.get("instance") + q = Author.objects.filter( + person=instance.person, author_type=instance.author_type + ) if q.count() <= 1: return authors = list(q.all()) @@ -2823,20 +3364,23 @@ post_save.connect(author_post_save, sender=Author) class SourceType(HierarchicalType): - coins_type = models.CharField(_("COInS export - type"), default='document', - max_length=100) - coins_genre = models.CharField(_("COInS export - genre"), blank=True, - default='', max_length=100) + coins_type = models.CharField( + _("COInS export - type"), default="document", max_length=100 + ) + coins_genre = models.CharField( + _("COInS export - genre"), blank=True, default="", max_length=100 + ) is_localized = models.BooleanField( - _("Is localized"), default=False, - help_text=_("Setting a language for this type of document is relevant") + _("Is localized"), + default=False, + help_text=_("Setting a language for this type of document is relevant"), ) - code = models.CharField(_("Code"), blank=True, default='', max_length=100) + code = models.CharField(_("Code"), blank=True, default="", max_length=100) class Meta: verbose_name = _("Document type") verbose_name_plural = _("Document types") - ordering = ['label'] + ordering = ["label"] post_save.connect(post_save_cache, sender=SourceType) @@ -2845,8 +3389,10 @@ post_delete.connect(post_save_cache, sender=SourceType) class SupportType(GeneralType): document_types = models.ManyToManyField( - "SourceType", blank=True, related_name='supports', - help_text=_("Only available for these document types") + "SourceType", + blank=True, + related_name="supports", + help_text=_("Only available for these document types"), ) class Meta: @@ -2860,19 +3406,26 @@ post_delete.connect(post_save_cache, sender=SupportType) class Format(GeneralType): iframe_template = models.TextField( - _("Iframe template"), blank=True, default="", - help_text=_("Template to insert an iframe for this format. Use django " - "template with a {{document}} variable matching the " - "current document.")) + _("Iframe template"), + blank=True, + default="", + help_text=_( + "Template to insert an iframe for this format. Use django " + "template with a {{document}} variable matching the " + "current document." + ), + ) document_types = models.ManyToManyField( - "SourceType", blank=True, related_name='formats', - help_text=_("Only available for these document types") + "SourceType", + blank=True, + related_name="formats", + help_text=_("Only available for these document types"), ) class Meta: verbose_name = _("Format type") verbose_name_plural = _("Format types") - ordering = ['label'] + ordering = ["label"] post_save.connect(post_save_cache, sender=Format) @@ -2885,7 +3438,7 @@ class LicenseType(GeneralType): class Meta: verbose_name = _("License type") verbose_name_plural = _("License types") - ordering = ('label',) + ordering = ("label",) class DocumentTag(GeneralType): @@ -2894,51 +3447,89 @@ class DocumentTag(GeneralType): class Meta: verbose_name = _("Document tag") verbose_name_plural = _("Document tags") - ordering = ('label',) + ordering = ("label",) post_save.connect(post_save_cache, sender=LicenseType) post_delete.connect(post_save_cache, sender=LicenseType) -class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, - ValueGetter, MainItem): +class Document( + BaseHistorizedItem, + CompleteIdentifierItem, + OwnPerms, + ImageModel, + ValueGetter, + MainItem, +): APP = "ishtar-common" MODEL = "document" - EXTERNAL_ID_KEY = 'document_external_id' - DELETE_URL = 'delete-document' + EXTERNAL_ID_KEY = "document_external_id" + DELETE_URL = "delete-document" # order is important: put the image in the first match found # other will be symbolic links RELATED_MODELS = [ - 'treatment_files', 'treatments', 'finds', 'context_records', - 'operations', 'sites', 'warehouses', 'containers', 'files', - 'administrativeacts', + "treatment_files", + "treatments", + "finds", + "context_records", + "operations", + "sites", + "warehouses", + "containers", + "files", + "administrativeacts", ] # same fields but in order for forms RELATED_MODELS_ALT = [ - 'finds', 'context_records', 'operations', 'sites', 'files', - 'administrativeacts', 'warehouses', 'containers', 'treatments', - 'treatment_files', + "finds", + "context_records", + "operations", + "sites", + "files", + "administrativeacts", + "warehouses", + "containers", + "treatments", + "treatment_files", ] - SLUG = 'document' + SLUG = "document" LINK_SPLIT = "<||>" GET_VALUES_EXCLUDE_FIELDS = ValueGetter.GET_VALUES_EXCLUDE_FIELDS + [ - "warehouses", "operations", "treatments", - "files", "treatment_files", "administrativeacts", "id", - "associated_links", "source_type_id", - "history_creator_id", "containers", "sites", - "main_image_warehouses", "main_image_operations", - "main_image_treatments", "main_image_files", - "main_image_treatment_files", "main_image_id", - "main_image_associated_links", "main_image_source_type_id", - "main_image_history_creator_id", "main_image_containers", + "warehouses", + "operations", + "treatments", + "files", + "treatment_files", + "administrativeacts", + "id", + "associated_links", + "source_type_id", + "history_creator_id", + "containers", + "sites", + "main_image_warehouses", + "main_image_operations", + "main_image_treatments", + "main_image_files", + "main_image_treatment_files", + "main_image_id", + "main_image_associated_links", + "main_image_source_type_id", + "main_image_history_creator_id", + "main_image_containers", "main_image_sites", ] - _TABLE_COLS = ['title', 'source_type', 'cache_related_label', - 'authors__cached_label', 'associated_url'] - COL_LINK = ['associated_url'] + _TABLE_COLS = [ + "title", + "source_type", + "cache_related_label", + "authors__cached_label", + "associated_url", + ] + COL_LINK = ["associated_url"] BASE_SEARCH_VECTORS = [ SearchVectorConfig("title"), SearchVectorConfig("source_type__label"), @@ -2950,368 +3541,428 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, SearchVectorConfig("additional_information", "local"), ] BASE_SEARCH_VECTORS += [ - SearchVectorConfig('treatment_files__name'), - SearchVectorConfig('treatments__cached_label'), - SearchVectorConfig('finds__cached_label'), - SearchVectorConfig('context_records__cached_label'), - SearchVectorConfig('operations__cached_label'), - SearchVectorConfig('sites__cached_label'), - SearchVectorConfig('warehouses__name'), - SearchVectorConfig('containers__cached_label'), - SearchVectorConfig('files__cached_label') + SearchVectorConfig("treatment_files__name"), + SearchVectorConfig("treatments__cached_label"), + SearchVectorConfig("finds__cached_label"), + SearchVectorConfig("context_records__cached_label"), + SearchVectorConfig("operations__cached_label"), + SearchVectorConfig("sites__cached_label"), + SearchVectorConfig("warehouses__name"), + SearchVectorConfig("containers__cached_label"), + SearchVectorConfig("files__cached_label"), + ] + PARENT_SEARCH_VECTORS = [ + "authors", + ] + M2M_SEARCH_VECTORS = [ + SearchVectorConfig("tags__label"), ] - PARENT_SEARCH_VECTORS = ['authors', ] - M2M_SEARCH_VECTORS = [SearchVectorConfig("tags__label"), ] - BOOL_FIELDS = ['duplicate'] + BOOL_FIELDS = ["duplicate"] COL_LABELS = { "authors__cached_label": _("Authors"), "complete_identifier": _("Identifier"), } - CACHED_LABELS = ['cache_related_label'] + CACHED_LABELS = ["cache_related_label"] CACHED_COMPLETE_ID = "" EXTRA_REQUEST_KEYS = { "operations": "operations__pk", "context_records": "context_records__pk", "context_records__operation": "context_records__operation__pk", "finds": "finds__pk", - "finds__base_finds__context_record": - "finds__base_finds__context_record__pk", - "finds__base_finds__context_record__operation": - "finds__base_finds__context_record__operation__pk", - 'authors__cached_label': 'authors__cached_label', - 'complete_identifier': 'complete_identifier', - 'authors__person__pk': 'authors__person__pk', + "finds__base_finds__context_record": "finds__base_finds__context_record__pk", + "finds__base_finds__context_record__operation": "finds__base_finds__context_record__operation__pk", + "authors__cached_label": "authors__cached_label", + "complete_identifier": "complete_identifier", + "authors__person__pk": "authors__person__pk", "container_id": "container_id", - 'publisher__pk': 'publisher__pk' + "publisher__pk": "publisher__pk", } # alternative names of fields for searches ALT_NAMES = { - 'authors': SearchAltName( + "authors": SearchAltName( pgettext_lazy("key for text search", "author"), - 'authors__cached_label__iexact' + "authors__cached_label__iexact", ), - 'publisher': SearchAltName( + "publisher": SearchAltName( pgettext_lazy("key for text search", "publisher"), - 'publisher__name__iexact' + "publisher__name__iexact", ), - 'publishing_year': SearchAltName( + "publishing_year": SearchAltName( pgettext_lazy("key for text search", "publishing-year"), - 'publishing_year' + "publishing_year", ), - 'title': SearchAltName( - pgettext_lazy("key for text search", "title"), - 'title__iexact' + "title": SearchAltName( + pgettext_lazy("key for text search", "title"), "title__iexact" ), - 'source_type': SearchAltName( + "source_type": SearchAltName( pgettext_lazy("key for text search", "type"), - 'source_type__label__iexact' + "source_type__label__iexact", ), - 'reference': SearchAltName( + "reference": SearchAltName( pgettext_lazy("key for text search", "reference"), - 'reference__iexact' + "reference__iexact", ), - 'internal_reference': SearchAltName( + "internal_reference": SearchAltName( pgettext_lazy("key for text search", "internal-reference"), - 'internal_reference__iexact' + "internal_reference__iexact", ), - 'description': SearchAltName( + "description": SearchAltName( pgettext_lazy("key for text search", "description"), - 'description__iexact' + "description__iexact", ), - 'tag': SearchAltName( - pgettext_lazy("key for text search", "tag"), - 'tags__label__iexact' + "tag": SearchAltName( + pgettext_lazy("key for text search", "tag"), "tags__label__iexact" ), - 'format': SearchAltName( + "format": SearchAltName( pgettext_lazy("key for text search", "format"), - 'format_type__label__iexact' + "format_type__label__iexact", ), - 'support': SearchAltName( + "support": SearchAltName( pgettext_lazy("key for text search", "medium"), - 'support_type__label__iexact' + "support_type__label__iexact", ), - 'language': SearchAltName( + "language": SearchAltName( pgettext_lazy("key for text search", "language"), - 'language__label__iexact' + "language__label__iexact", ), - 'licenses': SearchAltName( + "licenses": SearchAltName( pgettext_lazy("key for text search", "license"), - 'licenses__label__iexact' + "licenses__label__iexact", ), - 'scale': SearchAltName( - pgettext_lazy("key for text search", "scale"), - 'scale__iexact' + "scale": SearchAltName( + pgettext_lazy("key for text search", "scale"), "scale__iexact" ), - 'associated_url': SearchAltName( + "associated_url": SearchAltName( pgettext_lazy("key for text search", "url"), - 'associated_url__iexact' + "associated_url__iexact", ), - 'isbn': SearchAltName( - pgettext_lazy("key for text search", "isbn"), - 'isbn__iexact' + "isbn": SearchAltName( + pgettext_lazy("key for text search", "isbn"), "isbn__iexact" ), - 'issn': SearchAltName( - pgettext_lazy("key for text search", "issn"), - 'issn__iexact' + "issn": SearchAltName( + pgettext_lazy("key for text search", "issn"), "issn__iexact" ), - 'source': SearchAltName( + "source": SearchAltName( pgettext_lazy("key for text search", "source"), - 'source__title__iexact' + "source__title__iexact", ), - 'source_free_input': SearchAltName( + "source_free_input": SearchAltName( pgettext_lazy("key for text search", "source-free-input"), - 'source_free_input__iexact' + "source_free_input__iexact", ), - 'warehouse_container': SearchAltName( + "warehouse_container": SearchAltName( pgettext_lazy("key for text search", "warehouse-container"), - 'container__cached_label__iexact' + "container__cached_label__iexact", ), - 'warehouse_container_ref': SearchAltName( - pgettext_lazy("key for text search", - "warehouse-container-reference"), - 'container_ref__cached_label__iexact' + "warehouse_container_ref": SearchAltName( + pgettext_lazy( + "key for text search", "warehouse-container-reference" + ), + "container_ref__cached_label__iexact", ), - 'comment': SearchAltName( - pgettext_lazy("key for text search", "comment"), - 'comment__iexact' + "comment": SearchAltName( + pgettext_lazy("key for text search", "comment"), "comment__iexact" ), - 'additional_information': SearchAltName( + "additional_information": SearchAltName( pgettext_lazy("key for text search", "additional-information"), - 'additional_information__iexact' + "additional_information__iexact", ), - 'duplicate': SearchAltName( - pgettext_lazy("key for text search", "has-duplicate"), - 'duplicate' + "duplicate": SearchAltName( + pgettext_lazy("key for text search", "has-duplicate"), "duplicate" ), - 'operation': SearchAltName( + "operation": SearchAltName( pgettext_lazy("key for text search", "operation"), - 'operations__cached_label__iexact' + "operations__cached_label__iexact", ), - 'context_record': SearchAltName( + "context_record": SearchAltName( pgettext_lazy("key for text search", "context-record"), - 'context_records__cached_label__iexact' + "context_records__cached_label__iexact", ), - 'find_basket': SearchAltName( + "find_basket": SearchAltName( pgettext_lazy("key for text search", "basket-finds"), - 'finds__basket__label__iexact' + "finds__basket__label__iexact", ), - 'find': SearchAltName( + "find": SearchAltName( pgettext_lazy("key for text search", "find"), - 'finds__cached_label__iexact' + "finds__cached_label__iexact", ), - 'find__denomination': SearchAltName( + "find__denomination": SearchAltName( pgettext_lazy("key for text search", "find-denomination"), - 'finds__denomination__iexact' + "finds__denomination__iexact", ), - 'file': SearchAltName( + "file": SearchAltName( pgettext_lazy("key for text search", "file"), - 'files__cached_label__iexact' + "files__cached_label__iexact", ), - 'containers': SearchAltName( + "containers": SearchAltName( pgettext_lazy("key for text search", "container"), - 'containers__cached_label__iexact' + "containers__cached_label__iexact", ), - 'site': SearchAltName( + "site": SearchAltName( pgettext_lazy("key for text search", "site"), - 'sites__cached_label__iexact' + "sites__cached_label__iexact", ), - 'warehouse': SearchAltName( + "warehouse": SearchAltName( pgettext_lazy("key for text search", "warehouse"), - 'warehouses__name__iexact' + "warehouses__name__iexact", + ), + "image__isnull": SearchAltName( + pgettext_lazy("key for text search", "has-image"), "image__isnull" + ), + "associated_file__isnull": SearchAltName( + pgettext_lazy("key for text search", "has-file"), + "associated_file__isnull", + ), + "receipt_date__before": SearchAltName( + pgettext_lazy("key for text search", "receipt-date-before"), + "receipt_date__lte", + ), + "receipt_date__after": SearchAltName( + pgettext_lazy("key for text search", "receipt-date-after"), + "receipt_date__gte", + ), + "receipt_date_in_documentation__before": SearchAltName( + pgettext_lazy( + "key for text search", "receipt-in-documentation-date-before" + ), + "receipt_date_in_documentation__lte", + ), + "receipt_date_in_documentation__after": SearchAltName( + pgettext_lazy( + "key for text search", "receipt-in-documentation-date-after" + ), + "receipt_date_in_documentation__gte", + ), + "creation_date__before": SearchAltName( + pgettext_lazy("key for text search", "creation-date-before"), + "creation_date__lte", + ), + "creation_date__after": SearchAltName( + pgettext_lazy("key for text search", "creation-date-after"), + "creation_date__gte", ), - 'image__isnull': - SearchAltName( - pgettext_lazy("key for text search", "has-image"), - 'image__isnull'), - 'associated_file__isnull': - SearchAltName( - pgettext_lazy("key for text search", "has-file"), - 'associated_file__isnull'), - 'receipt_date__before': - SearchAltName( - pgettext_lazy("key for text search", "receipt-date-before"), - 'receipt_date__lte'), - 'receipt_date__after': - SearchAltName( - pgettext_lazy("key for text search", "receipt-date-after"), - 'receipt_date__gte'), - 'receipt_date_in_documentation__before': - SearchAltName( - pgettext_lazy("key for text search", - "receipt-in-documentation-date-before"), - 'receipt_date_in_documentation__lte'), - 'receipt_date_in_documentation__after': - SearchAltName( - pgettext_lazy("key for text search", - "receipt-in-documentation-date-after"), - 'receipt_date_in_documentation__gte'), - 'creation_date__before': - SearchAltName( - pgettext_lazy("key for text search", "creation-date-before"), - 'creation_date__lte'), - 'creation_date__after': - SearchAltName( - pgettext_lazy("key for text search", "creation-date-after"), - 'creation_date__gte'), } ALT_NAMES.update(BaseHistorizedItem.ALT_NAMES) # search parameters - REVERSED_BOOL_FIELDS = ['image__isnull', 'associated_file__isnull'] + REVERSED_BOOL_FIELDS = ["image__isnull", "associated_file__isnull"] DATED_FIELDS = [ - 'receipt_date__lte', - 'receipt_date__gte', - 'receipt_date_in_documentation__lte', - 'receipt_date_in_documentation__gte', - 'creation_date__lte', - 'creation_date__gte', + "receipt_date__lte", + "receipt_date__gte", + "receipt_date_in_documentation__lte", + "receipt_date_in_documentation__gte", + "creation_date__lte", + "creation_date__gte", ] objects = ExternalIdManager() RELATIVE_SESSION_NAMES = [ - ('find', 'finds__pk'), - ('contextrecord', 'context_records__pk'), - ('operation', 'operations__pk'), - ('site', 'sites__pk'), - ('file', 'files__pk'), - ('warehouse', 'warehouses__pk'), - ('treatment', 'treatments__pk'), - ('treatmentfile', 'treatment_files__pk'), - ('administrativeact', 'administrativeacts__pk'), + ("find", "finds__pk"), + ("contextrecord", "context_records__pk"), + ("operation", "operations__pk"), + ("site", "sites__pk"), + ("file", "files__pk"), + ("warehouse", "warehouses__pk"), + ("treatment", "treatments__pk"), + ("treatmentfile", "treatment_files__pk"), + ("administrativeact", "administrativeacts__pk"), ] UP_MODEL_QUERY = { - "operation": (pgettext_lazy("key for text search", "operation"), - 'cached_label'), - "contextrecord": (pgettext_lazy("key for text search", - "context-record"), 'cached_label'), - "file": (pgettext_lazy("key for text search", "file"), 'cached_label'), - "find": (pgettext_lazy("key for text search", "find"), 'cached_label'), - "site": (pgettext_lazy("key for text search", "site"), 'cached_label'), - "warehouse": (pgettext_lazy("key for text search", "warehouse"), - 'cached_label'), - "treatment": (pgettext_lazy("key for text search", "treatment"), - 'cached_label'), - "treatmentfile": (pgettext_lazy("key for text search", - "treatment-file"), 'cached_label'), + "operation": ( + pgettext_lazy("key for text search", "operation"), + "cached_label", + ), + "contextrecord": ( + pgettext_lazy("key for text search", "context-record"), + "cached_label", + ), + "file": (pgettext_lazy("key for text search", "file"), "cached_label"), + "find": (pgettext_lazy("key for text search", "find"), "cached_label"), + "site": (pgettext_lazy("key for text search", "site"), "cached_label"), + "warehouse": ( + pgettext_lazy("key for text search", "warehouse"), + "cached_label", + ), + "treatment": ( + pgettext_lazy("key for text search", "treatment"), + "cached_label", + ), + "treatmentfile": ( + pgettext_lazy("key for text search", "treatment-file"), + "cached_label", + ), } QA_EDIT = QuickAction( - url="document-qa-bulk-update", icon_class="fa fa-pencil", - text=_("Bulk update"), target="many", - rights=['change_document', 'change_own_document']) + url="document-qa-bulk-update", + icon_class="fa fa-pencil", + text=_("Bulk update"), + target="many", + rights=["change_document", "change_own_document"], + ) QUICK_ACTIONS = [ QA_EDIT, QuickAction( - url="document-qa-duplicate", icon_class="fa fa-clone", - text=_("Duplicate"), target="one", - rights=['change_document', 'change_own_document']), + url="document-qa-duplicate", + icon_class="fa fa-clone", + text=_("Duplicate"), + target="one", + rights=["change_document", "change_own_document"], + ), QuickAction( - url="document-qa-packaging", icon_class="fa fa-gift", - text=_("Packaging"), target="many", - rights=['change_document', 'change_own_document'], - module='warehouse' + url="document-qa-packaging", + icon_class="fa fa-gift", + text=_("Packaging"), + target="many", + rights=["change_document", "change_own_document"], + module="warehouse", ), ] SERIALIZATION_FILES = ["image", "thumbnail", "associated_file"] - title = models.TextField(_("Title"), blank=True, default='') + title = models.TextField(_("Title"), blank=True, default="") associated_file = models.FileField( verbose_name=_("Associated file"), - upload_to=get_image_path, blank=True, null=True, max_length=255, - help_text=max_size_help()) - index = models.IntegerField(verbose_name=_("Index"), blank=True, - null=True) + upload_to=get_image_path, + blank=True, + null=True, + max_length=255, + help_text=max_size_help(), + ) + index = models.IntegerField(verbose_name=_("Index"), blank=True, null=True) external_id = models.TextField(_("External ID"), blank=True, default="") reference = models.TextField(_("Ref."), blank=True, default="") - internal_reference = models.TextField(_("Internal ref."), blank=True, - default="") - source_type = models.ForeignKey(SourceType, verbose_name=_("Type"), - on_delete=models.SET_NULL, - null=True, blank=True) + internal_reference = models.TextField( + _("Internal ref."), blank=True, default="" + ) + source_type = models.ForeignKey( + SourceType, + verbose_name=_("Type"), + on_delete=models.SET_NULL, + null=True, + blank=True, + ) publisher = models.ForeignKey( - Organization, verbose_name=_("Publisher"), blank=True, null=True, - related_name='publish') + Organization, + verbose_name=_("Publisher"), + blank=True, + null=True, + related_name="publish", + ) publishing_year = models.PositiveIntegerField( - _("Year of publication"), blank=True, null=True) - licenses = models.ManyToManyField(LicenseType, verbose_name=_("License"), - blank=True) - tags = models.ManyToManyField(DocumentTag, verbose_name=_("Tags"), - blank=True) + _("Year of publication"), blank=True, null=True + ) + licenses = models.ManyToManyField( + LicenseType, verbose_name=_("License"), blank=True + ) + tags = models.ManyToManyField( + DocumentTag, verbose_name=_("Tags"), blank=True + ) language = models.ForeignKey( - Language, verbose_name=_("Language"), blank=True, null=True) + Language, verbose_name=_("Language"), blank=True, null=True + ) issn = models.CharField(_("ISSN"), blank=True, null=True, max_length=10) isbn = models.CharField(_("ISBN"), blank=True, null=True, max_length=17) - source = models.ForeignKey("Document", verbose_name=_("Source"), - blank=True, null=True, related_name="children") + source = models.ForeignKey( + "Document", + verbose_name=_("Source"), + blank=True, + null=True, + related_name="children", + ) source_free_input = models.CharField( - verbose_name=_("Source - free input"), blank=True, null=True, - max_length=500) + verbose_name=_("Source - free input"), + blank=True, + null=True, + max_length=500, + ) source_page_range = models.CharField( - verbose_name=_("Source - page range"), blank=True, null=True, - max_length=500) - support_type = models.ForeignKey(SupportType, verbose_name=_("Medium"), - on_delete=models.SET_NULL, - blank=True, null=True, ) - format_type = models.ForeignKey(Format, verbose_name=_("Format"), - on_delete=models.SET_NULL, - blank=True, null=True) - scale = models.CharField(_("Scale"), max_length=30, null=True, - blank=True) - authors = models.ManyToManyField(Author, verbose_name=_("Authors"), - related_name="documents") - authors_raw = models.CharField(verbose_name=_("Authors (raw)"), - blank=True, null=True, max_length=250) + verbose_name=_("Source - page range"), + blank=True, + null=True, + max_length=500, + ) + support_type = models.ForeignKey( + SupportType, + verbose_name=_("Medium"), + on_delete=models.SET_NULL, + blank=True, + null=True, + ) + format_type = models.ForeignKey( + Format, + verbose_name=_("Format"), + on_delete=models.SET_NULL, + blank=True, + null=True, + ) + scale = models.CharField(_("Scale"), max_length=30, null=True, blank=True) + authors = models.ManyToManyField( + Author, verbose_name=_("Authors"), related_name="documents" + ) + authors_raw = models.CharField( + verbose_name=_("Authors (raw)"), blank=True, null=True, max_length=250 + ) associated_url = models.URLField( - blank=True, null=True, max_length=1000, - verbose_name=_("Numerical ressource (web address)")) - receipt_date = models.DateField(blank=True, null=True, - verbose_name=_("Receipt date")) - creation_date = models.DateField(blank=True, null=True, - verbose_name=_("Creation date")) + blank=True, + null=True, + max_length=1000, + verbose_name=_("Numerical ressource (web address)"), + ) + receipt_date = models.DateField( + blank=True, null=True, verbose_name=_("Receipt date") + ) + creation_date = models.DateField( + blank=True, null=True, verbose_name=_("Creation date") + ) receipt_date_in_documentation = models.DateField( - blank=True, null=True, - verbose_name=_("Receipt date in documentation")) + blank=True, null=True, verbose_name=_("Receipt date in documentation") + ) item_number = models.IntegerField(_("Number of items"), default=1) description = models.TextField(_("Description"), blank=True, default="") container_id = models.PositiveIntegerField( - verbose_name=_("Container ID"), blank=True, null=True) + verbose_name=_("Container ID"), blank=True, null=True + ) # container = models.ForeignKey("archaeological_warehouse.Container") container_ref_id = models.PositiveIntegerField( - verbose_name=_("Container ID"), blank=True, null=True) + verbose_name=_("Container ID"), blank=True, null=True + ) # container_ref = models.ForeignKey("archaeological_warehouse.Container") comment = models.TextField(_("Comment"), blank=True, default="") - additional_information = models.TextField(_("Additional information"), - blank=True, default="") - duplicate = models.NullBooleanField(_("Has a duplicate"), blank=True, - null=True) - associated_links = models.TextField(_("Symbolic links"), blank=True, - default="") + additional_information = models.TextField( + _("Additional information"), blank=True, default="" + ) + duplicate = models.NullBooleanField( + _("Has a duplicate"), blank=True, null=True + ) + associated_links = models.TextField( + _("Symbolic links"), blank=True, default="" + ) cache_related_label = models.TextField( - _("Related"), blank=True, default="", db_index=True, - help_text=_("Cached value - do not edit")) + _("Related"), + blank=True, + default="", + db_index=True, + help_text=_("Cached value - do not edit"), + ) class Meta: verbose_name = _("Document") verbose_name_plural = _("Documents") - ordering = ('title',) + ordering = ("title",) permissions = ( - ("view_document", - ugettext("Can view all Documents")), - ("view_own_document", - ugettext("Can view own Document")), - ("add_own_document", - ugettext("Can add own Document")), - ("change_own_document", - ugettext("Can change own Document")), - ("delete_own_document", - ugettext("Can delete own Document")), + ("view_document", ugettext("Can view all Documents")), + ("view_own_document", ugettext("Can view own Document")), + ("add_own_document", ugettext("Can add own Document")), + ("change_own_document", ugettext("Can change own Document")), + ("delete_own_document", ugettext("Can delete own Document")), ) indexes = [ - GinIndex(fields=['data']), + GinIndex(fields=["data"]), ] def __str__(self): @@ -3329,18 +3980,24 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, def operation_codes(self): Operation = apps.get_model("archaeological_operations", "Operation") return "|".join( - sorted([Operation.objects.get(pk=ope_id).code_patriarche - for ope_id in self.get_related_operation_ids()])) + sorted( + [ + Operation.objects.get(pk=ope_id).code_patriarche + for ope_id in self.get_related_operation_ids() + ] + ) + ) def get_related_operation_ids(self): - operations = list( - self.operations.values_list("id", flat=True).all()) + operations = list(self.operations.values_list("id", flat=True).all()) operations += list( - self.context_records.values_list( - "operation_id", flat=True).all()) + self.context_records.values_list("operation_id", flat=True).all() + ) operations += list( self.finds.values_list( - "base_finds__context_record__operation_id", flat=True).all()) + "base_finds__context_record__operation_id", flat=True + ).all() + ) return list(set(operations)) def get_index_operation(self): @@ -3348,11 +4005,17 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, if len(operations) != 1: return current_operation = operations[0] - q = Document.objects.exclude(pk=self.pk).filter( - Q(operations__id=current_operation) | - Q(context_records__operation_id=current_operation) | - Q(finds__base_finds__context_record__operation_id=current_operation) - ).order_by("-custom_index") + q = ( + Document.objects.exclude(pk=self.pk) + .filter( + Q(operations__id=current_operation) + | Q(context_records__operation_id=current_operation) + | Q( + finds__base_finds__context_record__operation_id=current_operation + ) + ) + .order_by("-custom_index") + ) current_index = None for doc in q.all(): if not doc.custom_index: @@ -3385,7 +4048,8 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, if not self.has_iframe: return "" return Template(self.format_type.iframe_template).render( - Context({"document": self})) + Context({"document": self}) + ) @property def container(self): @@ -3409,8 +4073,9 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, @property def pdf_attached(self): - if not self.associated_file and (not self.source - or not self.source.associated_file): + if not self.associated_file and ( + not self.source or not self.source.associated_file + ): return extra = "" if self.associated_file: @@ -3432,6 +4097,7 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, return "{}-{:04d}".format(self.operation.code_patriarche or '', self.index) """ + def duplicate_item(self, user=None, data=None): return duplicate_item(self, user, data) @@ -3479,18 +4145,29 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, actions = super(Document, self).get_extra_actions(request) # is_locked = self.is_locked(request.user) - can_edit_document = self.can_do(request, 'change_document') + can_edit_document = self.can_do(request, "change_document") if not can_edit_document: return actions actions += [ - (reverse("document-qa-duplicate", args=[self.pk]), - _("Duplicate"), "fa fa-clone", "", "", True), + ( + reverse("document-qa-duplicate", args=[self.pk]), + _("Duplicate"), + "fa fa-clone", + "", + "", + True, + ), ] if get_current_profile().warehouse: actions.append( - (reverse("document-qa-packaging", args=[self.pk]), - _("Packaging"), - "fa fa-gift", "", "", True) + ( + reverse("document-qa-packaging", args=[self.pk]), + _("Packaging"), + "fa fa-gift", + "", + "", + True, + ) ) return actions @@ -3508,7 +4185,8 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, def get_values(self, prefix="", no_values=False, filtr=None, **kwargs): values = super(Document, self).get_values( - prefix=prefix, no_values=no_values, filtr=filtr, **kwargs) + prefix=prefix, no_values=no_values, filtr=filtr, **kwargs + ) if not filtr or prefix + "image_path" in filtr: values[prefix + "image_path"] = self.image_path if not filtr or prefix + "thumbnail_path" in filtr: @@ -3528,8 +4206,9 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, @property def images(self): # mimic a queryset pointing to himself - return Document.objects.filter( - pk=self.pk, image__isnull=False).exclude(image='') + return Document.objects.filter(pk=self.pk, image__isnull=False).exclude( + image="" + ) @property def main_image(self): @@ -3547,9 +4226,7 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, klass = getattr(cls, rel_model).rel.related_model q_own_dct = klass._get_query_owns_dicts(ishtaruser) if q_own_dct: - query_own_list.append( - (rel_model + "__", q_own_dct) - ) + query_own_list.append((rel_model + "__", q_own_dct)) q = None for prefix, owns in query_own_list: subq = cls._construct_query_own(prefix, owns) @@ -3558,9 +4235,9 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, q = subq else: q |= subq - q |= cls._construct_query_own('', [ - {'history_creator': ishtaruser.user_ptr} - ]) + q |= cls._construct_query_own( + "", [{"history_creator": ishtaruser.user_ptr}] + ) return q def get_associated_operation(self): @@ -3568,9 +4245,11 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, @property def associated_filename(self): - values = [str(getattr(self, attr)) - for attr in ('source_type', 'title') - if getattr(self, attr)] + values = [ + str(getattr(self, attr)) + for attr in ("source_type", "title") + if getattr(self, attr) + ] return slugify("-".join(values)) def _get_base_image_paths(self): @@ -3600,14 +4279,14 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, the new_path """ - file_split = path.split('.') + file_split = path.split(".") suffix, base = "", "" if len(file_split) > 1: base = ".".join(file_split[0:-1]) suffix = file_split[-1] else: base = path - base_split = base.split('-') + base_split = base.split("-") current_nb = 0 if len(base_split) > 1: try: @@ -3617,8 +4296,11 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, pass while os.path.exists(path): - if test_link and os.path.islink(path) \ - and os.readlink(path) == test_link: + if ( + test_link + and os.path.islink(path) + and os.readlink(path) == test_link + ): return path, True current_nb += 1 path = "{}-{}.{}".format(base, current_nb, suffix) @@ -3649,7 +4331,8 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, if not os.path.exists(os.path.dirname(new_path)): os.makedirs(os.path.dirname(new_path)) new_path, match = self._get_available_filename( - new_path, test_link=reference_path) + new_path, test_link=reference_path + ) links.append(new_path) if match: # the current link is correct continue @@ -3671,11 +4354,14 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, @classmethod def get_next_index(cls): - q = cls.objects.values('index').filter( - index__isnull=False).order_by("-index") + q = ( + cls.objects.values("index") + .filter(index__isnull=False) + .order_by("-index") + ) if not q.count(): return 1 - cid = q.all()[0]['index'] + cid = q.all()[0]["index"] if not cid: cid = 0 return cid + 1 @@ -3705,23 +4391,34 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, identifier = self.associated_url elif Site.objects.count(): identifier = "http://{}{}".format( - Site.objects.all()[0].domain, self.get_absolute_url()) + Site.objects.all()[0].domain, self.get_absolute_url() + ) return identifier def dublin_core_tags(self): if not self.title: return "" tags = [ - ("link", - {"rel": "schema.DC", "href": "http://purl.org/dc/elements/1.1/"}), - ("link", - {"rel": "schema.DCTERMS", "href": "http://purl.org/dc/terms/"}), + ( + "link", + { + "rel": "schema.DC", + "href": "http://purl.org/dc/elements/1.1/", + }, + ), + ( + "link", + {"rel": "schema.DCTERMS", "href": "http://purl.org/dc/terms/"}, + ), ] title = {"name": "DC.title", "content": self.title} tags.append(("meta", title)) if self.creation_date: - date = {"name": "DC.date", "scheme": "DCTERMS.W3CDTF", - "content": self.creation_date.strftime("%Y-%m-%d")} + date = { + "name": "DC.date", + "scheme": "DCTERMS.W3CDTF", + "content": self.creation_date.strftime("%Y-%m-%d"), + } tags.append(("meta", date)) if self.tags.count(): content = ", ".join(str(t) for t in self.tags.all()) @@ -3729,55 +4426,58 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, tags.append(("meta", tg)) if self.description: tags.append( - ("meta", {"name": "DC.description", - "content": self.description})) + ( + "meta", + {"name": "DC.description", "content": self.description}, + ) + ) if self.publisher: tags.append( - ("meta", {"name": "DC.publisher", - "content": self.publisher.name})) + ( + "meta", + {"name": "DC.publisher", "content": self.publisher.name}, + ) + ) if self.authors.count(): - content = ", ".join(str(t.person.raw_name) for t in - self.authors.all()) - tags.append( - ("meta", {"name": "DC.creator", - "content": content})) + content = ", ".join( + str(t.person.raw_name) for t in self.authors.all() + ) + tags.append(("meta", {"name": "DC.creator", "content": content})) if self.source_type: tags.append( - ("meta", {"name": "DC.type", - "content": str(self.source_type)})) + ("meta", {"name": "DC.type", "content": str(self.source_type)}) + ) if self.format_type: tags.append( - ("meta", {"name": "DC.format", - "content": str(self.format_type)})) + ( + "meta", + {"name": "DC.format", "content": str(self.format_type)}, + ) + ) identifier = self.dublin_core_identifier if identifier: tags.append( - ("meta", {"name": "DC.identifier", - "content": identifier})) + ("meta", {"name": "DC.identifier", "content": identifier}) + ) if self.language: lang = self.language.iso_code - tags.append( - ("meta", {"name": "DC.language", - "content": lang})) + tags.append(("meta", {"name": "DC.language", "content": lang})) if self.licenses.count(): licences = ", ".join(str(l) for l in self.licenses.all()) - tags.append( - ("meta", {"name": "DC.rights", - "content": licences})) + tags.append(("meta", {"name": "DC.rights", "content": licences})) src = None if self.source: src = self.source.dublin_core_identifier if src: - tags.append( - ("meta", {"name": "DC.relation", - "content": src})) - tags.append( - ("meta", {"name": "DC.source", - "content": src})) + tags.append(("meta", {"name": "DC.relation", "content": src})) + tags.append(("meta", {"name": "DC.source", "content": src})) elif self.source_free_input: tags.append( - ("meta", {"name": "DC.source", - "content": self.source_free_input})) + ( + "meta", + {"name": "DC.source", "content": self.source_free_input}, + ) + ) html = "" for tag, attrs in tags: et = ET.Element(tag, attrib=attrs) @@ -3796,13 +4496,19 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, ("ctx_ver", "Z39.88-2004"), ("rft_val_fmt", "info:ofi/fmt:kev:mtx:dc"), ("rft.title", self.title), - ("rft.btitle", self.title) + ("rft.btitle", self.title), ] if self.associated_url: info.append(("rft.identifier", self.associated_url)) elif Site.objects.count(): - info.append(("rft.identifier", "http://{}{}".format( - Site.objects.all()[0].domain, self.get_absolute_url()))) + info.append( + ( + "rft.identifier", + "http://{}{}".format( + Site.objects.all()[0].domain, self.get_absolute_url() + ), + ) + ) for author in self.authors.all(): person = author.person if not person.raw_name: @@ -3810,8 +4516,9 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, if person.first_name and person.name: info.append(("rft.aulast", person.name)) info.append(("rft.aufirst", person.first_name)) - info.append(("rft.au", "{}+{}".format(person.first_name, - person.name))) + info.append( + ("rft.au", "{}+{}".format(person.first_name, person.name)) + ) else: info.append(("rft.au", person.raw_name)) if self.source_type: @@ -3826,8 +4533,9 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, if self.creation_date.day == 1 and self.creation_date.month == 1: info.append(("rft.date", self.creation_date.year)) else: - info.append(("rft.date", - self.creation_date.strftime("%Y-%m-%d"))) + info.append( + ("rft.date", self.creation_date.strftime("%Y-%m-%d")) + ) if self.source and self.source.title: info.append(("rft.source", self.source.title)) elif self.source_free_input: @@ -3844,8 +4552,9 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, return ''.format(urlencode(info)) def save(self, *args, **kwargs): - no_path_change = 'no_path_change' in kwargs \ - and kwargs.pop('no_path_change') + no_path_change = "no_path_change" in kwargs and kwargs.pop( + "no_path_change" + ) self.set_index() if not self.associated_url: self.associated_url = None @@ -3861,8 +4570,11 @@ class Document(BaseHistorizedItem, CompleteIdentifierItem, OwnPerms, ImageModel, self.container_ref_id = container_ref.pk super(Document, self).save(*args, **kwargs) - if self.image and not no_path_change and \ - not getattr(self, '_no_path_change', False): + if ( + self.image + and not no_path_change + and not getattr(self, "_no_path_change", False) + ): links = self._move_image() if not links: return @@ -3883,18 +4595,25 @@ class OperationType(GeneralType): class Meta: verbose_name = _("Operation type") verbose_name_plural = _("Operation types") - ordering = ['judiciary', '-preventive', 'order', 'label'] + ordering = ["judiciary", "-preventive", "order", "label"] @classmethod - def get_types(cls, dct=None, instances=False, exclude=None, - empty_first=True, default=None, initial=None): + def get_types( + cls, + dct=None, + instances=False, + exclude=None, + empty_first=True, + default=None, + initial=None, + ): dct = dct or {} exclude = exclude or [] initial = initial or [] tuples = [] - dct['available'] = True + dct["available"] = True if not instances and empty_first and not default: - tuples.append(('', '--')) + tuples.append(("", "--")) if default and not instances: try: default = cls.objects.get(txt_idx=default) @@ -3908,16 +4627,20 @@ class OperationType(GeneralType): items = items.exclude(txt_idx__in=exclude) current_preventive, current_judiciary, current_lst = None, None, None item_list = list(items.order_by(*cls._meta.ordering).all()) - new_vals = cls._get_initial_types(initial, [i.pk for i in item_list], - instance=True) + new_vals = cls._get_initial_types( + initial, [i.pk for i in item_list], instance=True + ) item_list += new_vals for item in item_list: item.rank = 0 if instances: return item_list for item in item_list: - if not current_lst or item.preventive != current_preventive \ - or item.judiciary != current_judiciary: + if ( + not current_lst + or item.preventive != current_preventive + or item.judiciary != current_judiciary + ): if current_lst: tuples.append(current_lst) if item.judiciary: @@ -3935,7 +4658,7 @@ class OperationType(GeneralType): return tuples @classmethod - def is_preventive(cls, ope_type_id, key=''): + def is_preventive(cls, ope_type_id, key=""): try: op_type = cls.objects.get(pk=ope_type_id) except cls.DoesNotExist: @@ -3964,25 +4687,27 @@ class AdministrationScript(models.Model): class Meta: verbose_name = _("Administration script") verbose_name_plural = _("Administration scripts") - ordering = ['name'] + ordering = ["name"] def __str__(self): return str(self.name) -SCRIPT_STATE = (("S", _("Scheduled")), - ("P", _("In progress")), - ("FE", _("Finished with errors")), - ("F", _("Finished")), - ) +SCRIPT_STATE = ( + ("S", _("Scheduled")), + ("P", _("In progress")), + ("FE", _("Finished with errors")), + ("F", _("Finished")), +) SCRIPT_STATE_DCT = dict(SCRIPT_STATE) class AdministrationTask(models.Model): script = models.ForeignKey(AdministrationScript) - state = models.CharField(_("State"), max_length=2, choices=SCRIPT_STATE, - default='S') + state = models.CharField( + _("State"), max_length=2, choices=SCRIPT_STATE, default="S" + ) creation_date = models.DateTimeField(default=datetime.datetime.now) launch_date = models.DateTimeField(null=True, blank=True) finished_date = models.DateTimeField(null=True, blank=True) @@ -3991,17 +4716,16 @@ class AdministrationTask(models.Model): class Meta: verbose_name = _("Administration task") verbose_name_plural = _("Administration tasks") - ordering = ['script'] + ordering = ["script"] def __str__(self): state = _("Unknown") if self.state in SCRIPT_STATE_DCT: state = str(SCRIPT_STATE_DCT[self.state]) - return "{} - {} - {}".format(self.script, self.creation_date, - state) + return "{} - {} - {}".format(self.script, self.creation_date, state) def execute(self): - if self.state != 'S': + if self.state != "S": return self.launch_date = datetime.datetime.now() @@ -4009,29 +4733,35 @@ class AdministrationTask(models.Model): if not script_dir: self.result = str( - _("ISHTAR_SCRIPT_DIR is not set in your " - "local_settings. Contact your administrator.")) - self.state = 'FE' + _( + "ISHTAR_SCRIPT_DIR is not set in your " + "local_settings. Contact your administrator." + ) + ) + self.state = "FE" self.finished_date = datetime.datetime.now() self.save() return - if '..' in script_dir: + if ".." in script_dir: self.result = str( - _("Your ISHTAR_SCRIPT_DIR is containing " - "dots \"..\". As it can refer to relative " - "paths, it can be a security issue and this is " - "not allowed. Only put a full path.")) - self.state = 'FE' + _( + "Your ISHTAR_SCRIPT_DIR is containing " + 'dots "..". As it can refer to relative ' + "paths, it can be a security issue and this is " + "not allowed. Only put a full path." + ) + ) + self.state = "FE" self.finished_date = datetime.datetime.now() self.save() return if not os.path.isdir(script_dir): self.result = str( - _("Your ISHTAR_SCRIPT_DIR: \"{}\" is not a valid directory.") + _('Your ISHTAR_SCRIPT_DIR: "{}" is not a valid directory.') ).format(script_dir) - self.state = 'FE' + self.state = "FE" self.finished_date = datetime.datetime.now() self.save() return @@ -4045,14 +4775,16 @@ class AdministrationTask(models.Model): break if not script_name: self.result = str( - _("Script \"{}\" is not available in your script directory. " - "Check your configuration.") + _( + 'Script "{}" is not available in your script directory. ' + "Check your configuration." + ) ).format(self.script.path) - self.state = 'FE' + self.state = "FE" self.finished_date = datetime.datetime.now() self.save() return - self.state = 'P' + self.state = "P" self.save() self.finished_date = datetime.datetime.now() @@ -4060,19 +4792,20 @@ class AdministrationTask(models.Model): session = Popen([script_name], stdout=PIPE, stderr=PIPE) stdout, stderr = session.communicate() except OSError as e: - self.state = 'FE' - self.result = "Error executing \"{}\" script: {}".format( - self.script.path, e) + self.state = "FE" + self.result = 'Error executing "{}" script: {}'.format( + self.script.path, e + ) self.save() return self.finished_date = datetime.datetime.now() if stderr: - self.state = 'FE' - self.result = "Error: {}".format(stderr.decode('utf-8')) + self.state = "FE" + self.result = "Error: {}".format(stderr.decode("utf-8")) else: - self.state = 'F' - self.result = "{}".format(stdout.decode('utf-8')) + self.state = "F" + self.result = "{}".format(stdout.decode("utf-8")) self.save() @@ -4093,24 +4826,40 @@ class ExportTask(models.Model): _("Filter on"), max_length=2, choices=ITEM_TYPES, null=True, blank=True ) filter_text = models.TextField( - _("Filter query"), blank=True, default="", - help_text=_("Textual query on this item (try it on the main " - "interface)")) + _("Filter query"), + blank=True, + default="", + help_text=_( + "Textual query on this item (try it on the main " "interface)" + ), + ) geo = models.BooleanField( - _("Export geographic data"), default=True, - help_text=_("Geographic data can represent large volume of " - "information. Geographic data can be excluded from the " - "export")) - state = models.CharField(_("State"), max_length=2, choices=EXPORT_STATE, - default='C') - put_locks = models.BooleanField(_("Put locks on associated items"), - default=False) + _("Export geographic data"), + default=True, + help_text=_( + "Geographic data can represent large volume of " + "information. Geographic data can be excluded from the " + "export" + ), + ) + state = models.CharField( + _("State"), max_length=2, choices=EXPORT_STATE, default="C" + ) + put_locks = models.BooleanField( + _("Put locks on associated items"), default=False + ) lock_user = models.ForeignKey( - User, related_name='+', on_delete=models.SET_NULL, - verbose_name=_("Lock user"), blank=True, null=True, - help_text=_("Owner of the lock if item are locked. Warning: if no " - "user is provided the locks can be remove by any user " - "with the permission to edit.") + User, + related_name="+", + on_delete=models.SET_NULL, + verbose_name=_("Lock user"), + blank=True, + null=True, + help_text=_( + "Owner of the lock if item are locked. Warning: if no " + "user is provided the locks can be remove by any user " + "with the permission to edit." + ), ) export_types = models.BooleanField(_("Export types"), default=True) export_conf = models.BooleanField(_("Export configuration"), default=True) @@ -4122,15 +4871,17 @@ class ExportTask(models.Model): creation_date = models.DateTimeField(default=datetime.datetime.now) launch_date = models.DateTimeField(null=True, blank=True) finished_date = models.DateTimeField(null=True, blank=True) - result = models.FileField(_("Result"), null=True, blank=True, - upload_to="exports/%Y/%m/") - result_info = models.TextField(_("Result information"), blank=True, - default="") + result = models.FileField( + _("Result"), null=True, blank=True, upload_to="exports/%Y/%m/" + ) + result_info = models.TextField( + _("Result information"), blank=True, default="" + ) class Meta: verbose_name = _("Archive - Export") verbose_name_plural = _("Archive - Exports") - ordering = ['creation_date'] + ordering = ["creation_date"] def __str__(self): state = _("Unknown") @@ -4149,7 +4900,8 @@ class ExportTask(models.Model): def clean(self): if (self.filter_text and not self.filter_type) or ( - self.filter_type and not self.filter_text): + self.filter_type and not self.filter_text + ): raise ValidationError( _("To filter filter type and filter text must be filled.") ) @@ -4160,25 +4912,35 @@ class ImportTask(models.Model): launch_date = models.DateTimeField(null=True, blank=True) finished_date = models.DateTimeField(null=True, blank=True) import_user = models.ForeignKey( - User, related_name='+', on_delete=models.SET_NULL, - verbose_name=_("Import user"), blank=True, null=True, - help_text=_("If set the \"Import user\" will be the editor for last " - "version. If the field is left empty no history will be " - "recorded.") - ) - state = models.CharField(_("State"), max_length=2, choices=EXPORT_STATE, - default='C') + User, + related_name="+", + on_delete=models.SET_NULL, + verbose_name=_("Import user"), + blank=True, + null=True, + help_text=_( + 'If set the "Import user" will be the editor for last ' + "version. If the field is left empty no history will be " + "recorded." + ), + ) + state = models.CharField( + _("State"), max_length=2, choices=EXPORT_STATE, default="C" + ) delete_before = models.BooleanField( - _("Delete before adding"), default=False, - help_text=_("Delete existing items before adding")) + _("Delete before adding"), + default=False, + help_text=_("Delete existing items before adding"), + ) releasing_locks = models.BooleanField( - _("Releasing locks on associated items"), default=False) + _("Releasing locks on associated items"), default=False + ) source = models.FileField(_("Source"), upload_to="imports/%Y/%m/") class Meta: verbose_name = _("Archive - Import") verbose_name_plural = _("Archive - Imports") - ordering = ['creation_date'] + ordering = ["creation_date"] def __str__(self): state = _("Unknown") -- cgit v1.2.3