diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-03-19 11:05:22 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-03-19 11:05:22 +0100 |
commit | e2d6c50f231f636fed362be37e7bf3319fc5d6b8 (patch) | |
tree | 5d7fde3628825aebeeef3d85d2dfcf09a52116de /ishtar_common/models_common.py | |
parent | e6af0225df8f539308bc3fd8c9dbc967bba5a807 (diff) | |
download | Ishtar-e2d6c50f231f636fed362be37e7bf3319fc5d6b8.tar.bz2 Ishtar-e2d6c50f231f636fed362be37e7bf3319fc5d6b8.zip |
Format - black: ishtar_common
Diffstat (limited to 'ishtar_common/models_common.py')
-rw-r--r-- | ishtar_common/models_common.py | 1390 |
1 files changed, 799 insertions, 591 deletions
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index e0685c064..7a0397c36 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -37,20 +37,30 @@ from django.db.models.signals import post_save, post_delete, m2m_changed from django.template.defaultfilters import slugify from django.utils.safestring import SafeText, mark_safe from django.utils.translation import activate, deactivate -from ishtar_common.utils import ugettext_lazy as _, \ - pgettext_lazy, get_image_path +from ishtar_common.utils import ugettext_lazy as _, pgettext_lazy, get_image_path from simple_history.models import HistoricalRecords as BaseHistoricalRecords -from simple_history.signals import post_create_historical_record, \ - pre_create_historical_record +from simple_history.signals import ( + post_create_historical_record, + pre_create_historical_record, +) from unidecode import unidecode from ishtar_common.model_managers import TypeManager from ishtar_common.model_merging import merge_model_objects from ishtar_common.models_imports import Import from ishtar_common.templatetags.link_to_window import simple_link_to_window -from ishtar_common.utils import get_cache, disable_for_loaddata, \ - get_all_field_names, merge_tsvectors, cached_label_changed, post_save_geo, \ - task, duplicate_item, get_generated_id, get_current_profile +from ishtar_common.utils import ( + get_cache, + disable_for_loaddata, + get_all_field_names, + merge_tsvectors, + cached_label_changed, + post_save_geo, + task, + duplicate_item, + get_generated_id, + get_current_profile, +) logger = logging.getLogger(__name__) @@ -73,7 +83,7 @@ class CachedGen(object): @classmethod def _add_cache_key_to_refresh(cls, keys): - cache_ckey, current_keys = get_cache(cls, ['_current_keys']) + cache_ckey, current_keys = get_cache(cls, ["_current_keys"]) if type(current_keys) != list: current_keys = [] if keys not in current_keys: @@ -82,17 +92,17 @@ class CachedGen(object): class Cached(CachedGen): - slug_field = 'txt_idx' + slug_field = "txt_idx" @classmethod def refresh_cache(cls): - cache_ckey, current_keys = get_cache(cls, ['_current_keys']) + cache_ckey, current_keys = get_cache(cls, ["_current_keys"]) if not current_keys: return for keys in current_keys: - if len(keys) == 2 and keys[0] == '__slug': + if len(keys) == 2 and keys[0] == "__slug": cls.get_cache(keys[1], force=True) - elif keys[0] == '__get_types': + elif keys[0] == "__get_types": default = None empty_first = True exclude = [] @@ -102,14 +112,17 @@ class Cached(CachedGen): empty_first = bool(keys.pop()) exclude = keys[1:] cls.get_types( - exclude=exclude, empty_first=empty_first, default=default, - force=True) - elif keys[0] == '__get_help': + exclude=exclude, + empty_first=empty_first, + default=default, + force=True, + ) + elif keys[0] == "__get_help": cls.get_help(force=True) @classmethod def _add_cache_key_to_refresh(cls, keys): - cache_ckey, current_keys = get_cache(cls, ['_current_keys']) + cache_ckey, current_keys = get_cache(cls, ["_current_keys"]) if type(current_keys) != list: current_keys = [] if keys not in current_keys: @@ -118,7 +131,7 @@ class Cached(CachedGen): @classmethod def get_cache(cls, slug, force=False): - cache_key, value = get_cache(cls, ['__slug', slug]) + cache_key, value = get_cache(cls, ["__slug", slug]) if not force and value: return value try: @@ -140,14 +153,18 @@ class GeneralType(Cached, models.Model): """ Abstract class for "types" """ + label = models.TextField(_("Label")) txt_idx = models.TextField( - _("Textual ID"), validators=[validate_slug], + _("Textual ID"), + validators=[validate_slug], unique=True, help_text=_( "The slug is the standardized version of the name. It contains " "only lowercase letters, numbers and hyphens. Each slug must " - "be unique.")) + "be unique." + ), + ) comment = models.TextField(_("Comment"), blank=True, default="") available = models.BooleanField(_("Available"), default=True) HELP_TEXT = "" @@ -170,16 +187,20 @@ class GeneralType(Cached, models.Model): """ Used for automatic documentation generation """ - s = "**label** {}, **txt_idx** {}".format(str(_("Label")), - str(_("Textual ID"))) + s = "**label** {}, **txt_idx** {}".format(str(_("Label")), str(_("Textual ID"))) if hasattr(cls, "extra_documentation_string"): s += cls.extra_documentation_string() return s @classmethod def admin_url(cls): - return str(reverse('admin:{}_{}_changelist'.format( - cls._meta.app_label, cls._meta.model_name))) + return str( + reverse( + "admin:{}_{}_changelist".format( + cls._meta.app_label, cls._meta.model_name + ) + ) + ) @classmethod def history_decompress(cls, value, create=False): @@ -199,7 +220,7 @@ class GeneralType(Cached, models.Model): @classmethod def create_default_for_test(cls): - return [cls.objects.create(label='Test %d' % i) for i in range(5)] + return [cls.objects.create(label="Test %d" % i) for i in range(5)] @property def short_label(self): @@ -210,7 +231,7 @@ class GeneralType(Cached, models.Model): return self.label @classmethod - def get_or_create(cls, slug, label=''): + def get_or_create(cls, slug, label=""): """ Get or create a new item. @@ -225,7 +246,8 @@ class GeneralType(Cached, models.Model): if item: return item item, created = cls.objects.get_or_create( - txt_idx=slug, defaults={'label': label}) + txt_idx=slug, defaults={"label": label} + ) return item @classmethod @@ -260,9 +282,9 @@ class GeneralType(Cached, models.Model): dct = {} if not exclude: exclude = [] - keys = ['__get_help'] + keys = ["__get_help"] keys += ["{}".format(ex) for ex in exclude] - keys += ['{}-{}'.format(str(k), dct[k]) for k in dct] + keys += ["{}-{}".format(str(k), dct[k]) for k in dct] cache_key, value = get_cache(cls, keys) if value and not force: return mark_safe(value) @@ -270,11 +292,11 @@ class GeneralType(Cached, models.Model): c_rank = -1 help_items = "\n" for item in cls.get_types(dct=dct, instances=True, exclude=exclude): - if hasattr(item, '__iter__'): + if hasattr(item, "__iter__"): pk = item[0] item = cls.objects.get(pk=pk) item.rank = c_rank + 1 - if hasattr(item, 'parent'): + if hasattr(item, "parent"): c_item = item parents = [] while c_item.parent: @@ -291,11 +313,13 @@ class GeneralType(Cached, models.Model): help_items += "<dl>\n" c_rank = item.rank help_items += "<dt>%s</dt><dd>%s</dd>" % ( - item.label, "<br/>".join(item.comment.split('\n'))) + item.label, + "<br/>".join(item.comment.split("\n")), + ) c_rank += 1 if c_rank: help_items += c_rank * "</dl>" - if help_text or help_items != '\n': + if help_text or help_items != "\n": help_text = help_text + help_items else: help_text = "" @@ -327,19 +351,27 @@ class GeneralType(Cached, models.Model): return new_vals @classmethod - def get_types(cls, dct=None, instances=False, exclude=None, - empty_first=True, default=None, initial=None, force=False, - full_hierarchy=False): + def get_types( + cls, + dct=None, + instances=False, + exclude=None, + empty_first=True, + default=None, + initial=None, + force=False, + full_hierarchy=False, + ): if not dct: dct = {} if not exclude: exclude = [] types = [] if not instances and empty_first and not default: - types = [('', '--')] - types += cls._pre_get_types(dct, instances, exclude, - default, force, - get_full_hierarchy=full_hierarchy) + types = [("", "--")] + types += cls._pre_get_types( + dct, instances, exclude, default, force, get_full_hierarchy=full_hierarchy + ) if not initial: return types new_vals = cls._get_initial_types(initial, [idx for idx, lbl in types]) @@ -347,8 +379,15 @@ class GeneralType(Cached, models.Model): return types @classmethod - def _pre_get_types(cls, dct=None, instances=False, exclude=None, - default=None, force=False, get_full_hierarchy=False): + def _pre_get_types( + cls, + dct=None, + instances=False, + exclude=None, + default=None, + force=False, + get_full_hierarchy=False, + ): if not dct: dct = {} if not exclude: @@ -356,31 +395,42 @@ class GeneralType(Cached, models.Model): # cache cache_key = None if not instances: - keys = ['__get_types'] - keys += ["{}".format(ex) for ex in exclude] + \ - ["{}".format(default)] - keys += ['{}-{}'.format(str(k), dct[k]) for k in dct] + keys = ["__get_types"] + keys += ["{}".format(ex) for ex in exclude] + ["{}".format(default)] + keys += ["{}-{}".format(str(k), dct[k]) for k in dct] cache_key, value = get_cache(cls, keys) if value and not force: return value base_dct = dct.copy() - if hasattr(cls, 'parent'): + if hasattr(cls, "parent"): if not cache_key: return cls._get_parent_types( - base_dct, instances, exclude=exclude, - default=default, get_full_hierarchy=get_full_hierarchy) - vals = [v for v in cls._get_parent_types( - base_dct, instances, exclude=exclude, - default=default, get_full_hierarchy=get_full_hierarchy)] + base_dct, + instances, + exclude=exclude, + default=default, + get_full_hierarchy=get_full_hierarchy, + ) + vals = [ + v + for v in cls._get_parent_types( + base_dct, + instances, + exclude=exclude, + default=default, + get_full_hierarchy=get_full_hierarchy, + ) + ] cache.set(cache_key, vals, settings.CACHE_TIMEOUT) return vals if not cache_key: - return cls._get_types(base_dct, instances, exclude=exclude, - default=default) + return cls._get_types(base_dct, instances, exclude=exclude, default=default) vals = [ - v for v in cls._get_types(base_dct, instances, exclude=exclude, - default=default) + v + for v in cls._get_types( + base_dct, instances, exclude=exclude, default=default + ) ] cache.set(cache_key, vals, settings.CACHE_TIMEOUT) return vals @@ -391,7 +441,7 @@ class GeneralType(Cached, models.Model): dct = {} if not exclude: exclude = [] - dct['available'] = True + dct["available"] = True if default: try: default = cls.objects.get(txt_idx=default) @@ -400,7 +450,7 @@ class GeneralType(Cached, models.Model): pass items = cls.objects.filter(**dct) if default and default != "None": - if hasattr(default, 'txt_idx'): + if hasattr(default, "txt_idx"): exclude.append(default.txt_idx) else: exclude.append(default) @@ -411,7 +461,7 @@ class GeneralType(Cached, models.Model): item.rank = 0 yield item else: - yield (item.pk, _(str(item)) if item and str(item) else '') + yield (item.pk, _(str(item)) if item and str(item) else "") @classmethod def _get_childs_list(cls, dct=None, exclude=None, instances=False): @@ -419,13 +469,13 @@ class GeneralType(Cached, models.Model): dct = {} if not exclude: exclude = [] - if 'parent' in dct: - dct.pop('parent') + if "parent" in dct: + dct.pop("parent") childs = cls.objects.filter(**dct) if exclude: childs = childs.exclude(txt_idx__in=exclude) - if hasattr(cls, 'order'): - childs = childs.order_by('order') + if hasattr(cls, "order"): + childs = childs.order_by("order") res = {} if instances: for item in childs.all(): @@ -450,8 +500,16 @@ class GeneralType(Cached, models.Model): PREFIX_CODES = ["\u2502", "\u251C", "\u2514"] @classmethod - def _get_childs(cls, item, child_list, prefix=0, instances=False, - is_last=False, last_of=None, get_full_hierarchy=False): + def _get_childs( + cls, + item, + child_list, + prefix=0, + instances=False, + is_last=False, + last_of=None, + get_full_hierarchy=False, + ): if not last_of: last_of = [] @@ -465,7 +523,7 @@ class GeneralType(Cached, models.Model): full_hierarchy_initial = get_full_hierarchy for idx, child in enumerate(current_child_lst): mylast_of = last_of[:] - p = '' + p = "" if instances: child.rank = prefix lst.append(child) @@ -495,9 +553,7 @@ class GeneralType(Cached, models.Model): p += cls.PREFIX_EMPTY else: p += cls.PREFIX - lst.append(( - child[0], SafeText(p + str(_(child[1]))) - )) + lst.append((child[0], SafeText(p + str(_(child[1]))))) clast_of = last_of[:] clast_of.append(idx + 1 == total) if instances: @@ -512,20 +568,31 @@ class GeneralType(Cached, models.Model): else: get_full_hierarchy = child[1] for sub_child in cls._get_childs( - child_id, child_list, prefix, instances, - is_last=((idx + 1) == total), last_of=clast_of, - get_full_hierarchy=get_full_hierarchy): + child_id, + child_list, + prefix, + instances, + is_last=((idx + 1) == total), + last_of=clast_of, + get_full_hierarchy=get_full_hierarchy, + ): lst.append(sub_child) return lst @classmethod - def _get_parent_types(cls, dct=None, instances=False, exclude=None, - default=None, get_full_hierarchy=False): + def _get_parent_types( + cls, + dct=None, + instances=False, + exclude=None, + default=None, + get_full_hierarchy=False, + ): if not dct: dct = {} if not exclude: exclude = [] - dct['available'] = True + dct["available"] = True child_list = cls._get_childs_list(dct, exclude, instances) if 0 in child_list: @@ -540,8 +607,11 @@ class GeneralType(Cached, models.Model): if get_full_hierarchy: get_full_hierarchy = item[1] for child in cls._get_childs( - item_id, child_list, instances=instances, - get_full_hierarchy=get_full_hierarchy): + item_id, + child_list, + instances=instances, + get_full_hierarchy=get_full_hierarchy, + ): yield child def save(self, *args, **kwargs): @@ -551,8 +621,7 @@ class GeneralType(Cached, models.Model): if isinstance(txt_idx, list): txt_idx = txt_idx[0] self.txt_idx = txt_idx - self.label = " ".join(" ".join(self.txt_idx.split('-')) - .split('_')).title() + self.label = " ".join(" ".join(self.txt_idx.split("-")).split("_")).title() if not self.txt_idx: self.txt_idx = slugify(self.label)[:100] @@ -562,34 +631,36 @@ class GeneralType(Cached, models.Model): content_type = ContentType.objects.get_for_model(self.__class__) if slugify(self.label) != slugify(old.label): ItemKey.objects.filter( - object_id=self.pk, key=slugify(old.label), - content_type=content_type).delete() + object_id=self.pk, key=slugify(old.label), content_type=content_type + ).delete() if self.txt_idx != old.txt_idx: ItemKey.objects.filter( - object_id=self.pk, key=old.txt_idx, - content_type=content_type).delete() + object_id=self.pk, key=old.txt_idx, content_type=content_type + ).delete() obj = super(GeneralType, self).save(*args, **kwargs) self.generate_key(force=True) return obj - def add_key(self, key, force=False, importer=None, group=None, - user=None): + def add_key(self, key, force=False, importer=None, group=None, user=None): ItemKey = apps.get_model("ishtar_common", "ItemKey") content_type = ContentType.objects.get_for_model(self.__class__) - if not importer and not force and ItemKey.objects.filter( - key=key, content_type=content_type).count(): + if ( + not importer + and not force + and ItemKey.objects.filter(key=key, content_type=content_type).count() + ): return - filtr = {'key': key, 'content_type': content_type} + filtr = {"key": key, "content_type": content_type} if group: - filtr['group'] = group + filtr["group"] = group elif user: - filtr['user'] = user + filtr["user"] = user else: - filtr['importer'] = importer + filtr["importer"] = importer if force: ItemKey.objects.filter(**filtr).exclude(object_id=self.pk).delete() - filtr['object_id'] = self.pk + filtr["object_id"] = self.pk ItemKey.objects.get_or_create(**filtr) def generate_key(self, force=False): @@ -601,16 +672,14 @@ class GeneralType(Cached, models.Model): keys = [self.txt_idx] content_type = ContentType.objects.get_for_model(self.__class__) base_q = Q(content_type=content_type, object_id=self.pk) - subquery = Q(importer__isnull=True, user__isnull=True, - group__isnull=True) - subquery |= Q(user__isnull=True, group__isnull=True, - importer=importer) + subquery = Q(importer__isnull=True, user__isnull=True, group__isnull=True) + subquery |= Q(user__isnull=True, group__isnull=True, importer=importer) if importer.user: - subquery |= Q(user=importer.user, group__isnull=True, - importer=importer) + subquery |= Q(user=importer.user, group__isnull=True, importer=importer) if importer.associated_group: - subquery |= Q(user__isnull=True, group=importer.associated_group, - importer=importer) + subquery |= Q( + user__isnull=True, group=importer.associated_group, importer=importer + ) q = ItemKey.objects.filter(base_q & subquery) for ik in q.exclude(key=self.txt_idx).all(): keys.append(ik.key) @@ -624,9 +693,13 @@ class GeneralType(Cached, models.Model): class HierarchicalType(GeneralType): - parent = models.ForeignKey('self', blank=True, null=True, - on_delete=models.SET_NULL, - verbose_name=_("Parent")) + parent = models.ForeignKey( + "self", + blank=True, + null=True, + on_delete=models.SET_NULL, + verbose_name=_("Parent"), + ) class Meta: abstract = True @@ -665,16 +738,13 @@ class StatisticItem: class TemplateItem: @classmethod def _label_templates_q(cls): - model_name = "{}.{}".format( - cls.__module__, cls.__name__) - q = Q(associated_model__klass=model_name, - for_labels=True, available=True) - alt_model_name = model_name.replace( - "models_finds", "models").replace( - "models_treatments", "models") + model_name = "{}.{}".format(cls.__module__, cls.__name__) + q = Q(associated_model__klass=model_name, for_labels=True, available=True) + alt_model_name = model_name.replace("models_finds", "models").replace( + "models_treatments", "models" + ) if alt_model_name != model_name: - q |= Q(associated_model__klass=model_name, - for_labels=True, available=True) + q |= Q(associated_model__klass=model_name, for_labels=True, available=True) DocumentTemplate = apps.get_model("ishtar_common", "DocumentTemplate") return DocumentTemplate.objects.filter(q) @@ -695,33 +765,35 @@ class TemplateItem: if "models_finds" in name or "models_treatments" in name: names = [ name, - name.replace("models_finds", "models" - ).replace("models_treatments", "models") + name.replace("models_finds", "models").replace( + "models_treatments", "models" + ), ] else: - names = [name, name.replace("models", "models_finds"), - name.replace("models", "models_treatments")] + names = [ + name, + name.replace("models", "models_finds"), + name.replace("models", "models_treatments"), + ] else: names = [name] - model_names = [ - "{}.{}".format(module, name) for name in names - ] + model_names = ["{}.{}".format(module, name) for name in names] DocumentTemplate = apps.get_model("ishtar_common", "DocumentTemplate") q = DocumentTemplate.objects.filter( - associated_model__klass__in=model_names, - for_labels=False, available=True) + associated_model__klass__in=model_names, for_labels=False, available=True + ) for template in q.all(): urlname = "generate-document" templates.append( - (template.name, reverse( - urlname, args=[template.slug, self.pk])) + (template.name, reverse(urlname, args=[template.slug, self.pk])) ) return templates class FullSearch(models.Model): - search_vector = SearchVectorField(_("Search vector"), blank=True, null=True, - help_text=_("Auto filled at save")) + search_vector = SearchVectorField( + _("Search vector"), blank=True, null=True, help_text=_("Auto filled at save") + ) EXTRA_REQUEST_KEYS = {} DYNAMIC_REQUESTS = {} @@ -742,7 +814,7 @@ class FullSearch(models.Model): def general_types(cls): for k in get_all_field_names(cls): field = cls._meta.get_field(k) - if not hasattr(field, 'rel') or not field.rel: + if not hasattr(field, "rel") or not field.rel: continue rel_model = field.rel.to if issubclass(rel_model, (GeneralType, HierarchicalType)): @@ -768,8 +840,9 @@ class FullSearch(models.Model): def _update_search_field(self, search_vector_conf, search_vectors, data): for value in search_vector_conf.format(data): with connection.cursor() as cursor: - cursor.execute("SELECT to_tsvector(%s, %s)", [ - search_vector_conf.language, value]) + cursor.execute( + "SELECT to_tsvector(%s, %s)", [search_vector_conf.language, value] + ) row = cursor.fetchone() search_vectors.append(row[0]) @@ -782,20 +855,22 @@ class FullSearch(models.Model): :param save: True if you want to save the object immediately :return: True if modified """ - if not hasattr(self, 'search_vector'): + if not hasattr(self, "search_vector"): return if not self.pk: # logger.warning("Cannot update search vector before save or " # "after deletion.") return - if not self.BASE_SEARCH_VECTORS and not self.M2M_SEARCH_VECTORS \ - and not self.INT_SEARCH_VECTORS \ - and not self.PROPERTY_SEARCH_VECTORS \ - and not self.PARENT_SEARCH_VECTORS: - logger.warning("No search_vectors defined for {}".format( - self.__class__)) + if ( + not self.BASE_SEARCH_VECTORS + and not self.M2M_SEARCH_VECTORS + and not self.INT_SEARCH_VECTORS + and not self.PROPERTY_SEARCH_VECTORS + and not self.PARENT_SEARCH_VECTORS + ): + logger.warning("No search_vectors defined for {}".format(self.__class__)) return - if getattr(self, '_search_updated', None): + if getattr(self, "_search_updated", None): return JsonDataField = apps.get_model("ishtar_common", "JsonDataField") self._search_updated = True @@ -808,30 +883,29 @@ class FullSearch(models.Model): # many to many have to be queried one by one otherwise only one is fetch for m2m_search_vector in self.M2M_SEARCH_VECTORS: - key = m2m_search_vector.key.split('__')[0] + key = m2m_search_vector.key.split("__")[0] rel_key = getattr(self, key) - for item in rel_key.values('pk').all(): - query_dct = {key + "__pk": item['pk']} + for item in rel_key.values("pk").all(): + query_dct = {key + "__pk": item["pk"]} q = copy.copy(base_q).filter(**query_dct) q = q.annotate( search=SearchVector( - m2m_search_vector.key, - config=m2m_search_vector.language) - ).values('search') - search_vectors.append(q.all()[0]['search']) + m2m_search_vector.key, config=m2m_search_vector.language + ) + ).values("search") + search_vectors.append(q.all()[0]["search"]) # int/float are not well managed by the SearchVector for int_search_vector in self.INT_SEARCH_VECTORS: q = base_q.values(int_search_vector.key) - for val in int_search_vector.format( - q.all()[0][int_search_vector.key]): + for val in int_search_vector.format(q.all()[0][int_search_vector.key]): self._update_search_number_field(search_vectors, val) if not exclude_parent: # copy parent vector fields for PARENT_SEARCH_VECTOR in self.PARENT_SEARCH_VECTORS: parent = getattr(self, PARENT_SEARCH_VECTOR) - if hasattr(parent, 'all'): # m2m + if hasattr(parent, "all"): # m2m for p in parent.all(): search_vectors.append(p.search_vector) elif parent: @@ -839,7 +913,7 @@ class FullSearch(models.Model): for PARENT_ONLY_SEARCH_VECTOR in self.PARENT_ONLY_SEARCH_VECTORS: parent = getattr(self, PARENT_ONLY_SEARCH_VECTOR) - if hasattr(parent, 'all'): # m2m + if hasattr(parent, "all"): # m2m for p in parent.all(): search_vectors.append( p.update_search_vector(save=False, exclude_parent=True) @@ -856,8 +930,7 @@ class FullSearch(models.Model): for base_search_vector in self.BASE_SEARCH_VECTORS: data = res[base_search_vector.key] data = unidecode(str(data)) - self._update_search_field(base_search_vector, - search_vectors, data) + self._update_search_field(base_search_vector, search_vectors, data) if self.PROPERTY_SEARCH_VECTORS: for property_search_vector in self.PROPERTY_SEARCH_VECTORS: @@ -867,17 +940,16 @@ class FullSearch(models.Model): if not data: continue data = str(data) - self._update_search_field(property_search_vector, - search_vectors, data) + self._update_search_field(property_search_vector, search_vectors, data) - if hasattr(self, 'data') and self.data: + if hasattr(self, "data") and self.data: content_type = ContentType.objects.get_for_model(self) for json_field in JsonDataField.objects.filter( - content_type=content_type, - search_index=True).all(): + content_type=content_type, search_index=True + ).all(): data = copy.deepcopy(self.data) no_data = False - for key in json_field.key.split('__'): + for key in json_field.key.split("__"): if key not in data: no_data = True break @@ -885,22 +957,21 @@ class FullSearch(models.Model): if no_data or not data: continue - if json_field.value_type == 'B': + if json_field.value_type == "B": if data is True: data = json_field.name else: continue - elif json_field.value_type in ('I', 'F'): + elif json_field.value_type in ("I", "F"): self._update_search_number_field(search_vectors, data) continue - elif json_field.value_type == 'D': + elif json_field.value_type == "D": # only index year self._update_search_number_field(search_vectors, data.year) continue for lang in ("simple", settings.ISHTAR_SEARCH_LANGUAGE): with connection.cursor() as cursor: - cursor.execute("SELECT to_tsvector(%s, %s)", - [lang, data]) + cursor.execute("SELECT to_tsvector(%s, %s)", [lang, data]) row = cursor.fetchone() search_vectors.append(row[0]) new_search_vector = merge_tsvectors(search_vectors) @@ -908,7 +979,8 @@ class FullSearch(models.Model): self.search_vector = new_search_vector if save and changed: self.__class__.objects.filter(pk=self.pk).update( - search_vector=new_search_vector) + search_vector=new_search_vector + ) elif not save: return new_search_vector return changed @@ -916,8 +988,8 @@ class FullSearch(models.Model): class Imported(models.Model): imports = models.ManyToManyField( - Import, blank=True, - related_name="imported_%(app_label)s_%(class)s") + Import, blank=True, related_name="imported_%(app_label)s_%(class)s" + ) class Meta: abstract = True @@ -941,18 +1013,24 @@ class JsonData(models.Model, CachedGen): except ContentType.DoesNotExists: return sections JsonDataField = apps.get_model("ishtar_common", "JsonDataField") - fields = list(JsonDataField.objects.filter( - content_type=content_type, display=True, section__isnull=True - ).all()) # no section fields - - fields += list(JsonDataField.objects.filter( - content_type=content_type, display=True, section__isnull=False - ).order_by('section__order', 'order').all()) + fields = list( + JsonDataField.objects.filter( + content_type=content_type, display=True, section__isnull=True + ).all() + ) # no section fields + + fields += list( + JsonDataField.objects.filter( + content_type=content_type, display=True, section__isnull=False + ) + .order_by("section__order", "order") + .all() + ) for field in fields: value = None data = self.data.copy() - for key in field.key.split('__'): + for key in field.key.split("__"): if key in data: value = copy.copy(data[key]) data = data[key] @@ -972,14 +1050,14 @@ class JsonData(models.Model, CachedGen): @classmethod def refresh_cache(cls): - __, refreshed = get_cache(cls, ['cache_refreshed']) + __, refreshed = get_cache(cls, ["cache_refreshed"]) if refreshed and time.time() - refreshed < 1: return - cache_ckey, current_keys = get_cache(cls, ['_current_keys']) + cache_ckey, current_keys = get_cache(cls, ["_current_keys"]) if not current_keys: return for keys in current_keys: - if keys[0] == '__get_dynamic_choices': + if keys[0] == "__get_dynamic_choices": cls._get_dynamic_choices(keys[1], force=True) @classmethod @@ -990,18 +1068,19 @@ class JsonData(models.Model, CachedGen): :param force: if set to True do not use cache :return: tuple of choices (id, value) """ - cache_key, value = get_cache(cls, ['__get_dynamic_choices', key]) + cache_key, value = get_cache(cls, ["__get_dynamic_choices", key]) if not force and value: return value choices = set() - splitted_key = key[len('data__'):].split('__') - q = cls.objects.filter( - data__has_key=key[len('data__'):]).values_list('data', flat=True) + splitted_key = key[len("data__") :].split("__") + q = cls.objects.filter(data__has_key=key[len("data__") :]).values_list( + "data", flat=True + ) for value in q.all(): for k in splitted_key: value = value[k] choices.add(value) - choices = [('', '')] + [(v, v) for v in sorted(list(choices))] + choices = [("", "")] + [(v, v) for v in sorted(list(choices))] cache.set(cache_key, choices, settings.CACHE_SMALLTIMEOUT) return choices @@ -1022,8 +1101,9 @@ class FixAssociated: expected_values = [expected_values] if hasattr(ctype, "txt_idx"): try: - expected_values = [ctype.objects.get(txt_idx=v) - for v in expected_values] + expected_values = [ + ctype.objects.get(txt_idx=v) for v in expected_values + ] except ctype.DoesNotExist: # type not yet initialized return @@ -1066,8 +1146,9 @@ class CascasdeUpdate: class SearchAltName(object): - def __init__(self, search_key, search_query, extra_query=None, - distinct_query=False): + def __init__( + self, search_key, search_query, extra_query=None, distinct_query=False + ): self.search_key = search_key self.search_query = search_query self.extra_query = extra_query or {} @@ -1083,8 +1164,17 @@ class HistoryError(Exception): class HistoricalRecords(BaseHistoricalRecords): - def _save_historic(self, manager, instance, history_date, history_type, - history_user, history_change_reason, using, attrs): + def _save_historic( + self, + manager, + instance, + history_date, + history_type, + history_user, + history_change_reason, + using, + attrs, + ): history_instance = manager.model( history_date=history_date, history_type=history_type, @@ -1117,98 +1207,132 @@ class HistoricalRecords(BaseHistoricalRecords): def create_historical_record(self, instance, history_type, using=None): try: - history_modifier = getattr(instance, 'history_modifier', None) + history_modifier = getattr(instance, "history_modifier", None) assert history_modifier except (User.DoesNotExist, AssertionError): # on batch removing of users, user could have disappeared return - history_date = getattr(instance, "_history_date", - datetime.datetime.now()) + history_date = getattr(instance, "_history_date", datetime.datetime.now()) history_change_reason = getattr(instance, "changeReason", None) force = getattr(instance, "_force_history", False) manager = getattr(instance, self.manager_name) attrs = {} for field in instance._meta.fields: attrs[field.attname] = getattr(instance, field.attname) - q_history = instance.history \ - .filter(history_modifier_id=history_modifier.pk) \ - .order_by('-history_date', '-history_id') + q_history = instance.history.filter( + history_modifier_id=history_modifier.pk + ).order_by("-history_date", "-history_id") # instance.skip_history_when_saving = True if not q_history.count(): if force: - delattr(instance, '_force_history') + delattr(instance, "_force_history") self._save_historic( - manager, instance, history_date, history_type, history_modifier, - history_change_reason, using, attrs) + manager, + instance, + history_date, + history_type, + history_modifier, + history_change_reason, + using, + attrs, + ) return old_instance = q_history.all()[0] # multiple saving by the same user in a very short time are generaly # caused by post_save signals it is not relevant to keep them - min_history_date = datetime.datetime.now() \ - - datetime.timedelta(seconds=5) - q = q_history.filter(history_date__isnull=False, - history_date__gt=min_history_date) \ - .order_by('-history_date', '-history_id') + min_history_date = datetime.datetime.now() - datetime.timedelta(seconds=5) + q = q_history.filter( + history_date__isnull=False, history_date__gt=min_history_date + ).order_by("-history_date", "-history_id") if not force and q.count(): return if force: - delattr(instance, '_force_history') + delattr(instance, "_force_history") # record a new version only if data have been changed for field in instance._meta.fields: if getattr(old_instance, field.attname) != attrs[field.attname]: - self._save_historic(manager, instance, history_date, - history_type, history_modifier, - history_change_reason, using, attrs) + self._save_historic( + manager, + instance, + history_date, + history_type, + history_modifier, + history_change_reason, + using, + attrs, + ) return -class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, - JsonData, FixAssociated, CascasdeUpdate): +class BaseHistorizedItem( + StatisticItem, + TemplateItem, + FullSearch, + Imported, + JsonData, + FixAssociated, + CascasdeUpdate, +): """ Historized item with external ID management. All historized items are searchable and have a data json field. Historized items can be "locked" for edition. """ + IS_BASKET = False SHOW_URL = None - EXTERNAL_ID_KEY = '' + EXTERNAL_ID_KEY = "" EXTERNAL_ID_DEPENDENCIES = [] HISTORICAL_M2M = [] history_modifier = models.ForeignKey( - User, related_name='+', on_delete=models.SET_NULL, - verbose_name=_("Last editor"), blank=True, null=True) + User, + related_name="+", + on_delete=models.SET_NULL, + verbose_name=_("Last editor"), + blank=True, + null=True, + ) history_creator = models.ForeignKey( - User, related_name='+', on_delete=models.SET_NULL, - verbose_name=_("Creator"), blank=True, null=True) + User, + related_name="+", + on_delete=models.SET_NULL, + verbose_name=_("Creator"), + blank=True, + null=True, + ) last_modified = models.DateTimeField(auto_now=True) history_m2m = JSONField(default={}, blank=True) - need_update = models.BooleanField( - verbose_name=_("Need update"), default=False) + need_update = models.BooleanField(verbose_name=_("Need update"), default=False) locked = models.BooleanField( - verbose_name=_("Item locked for edition"), default=False) + verbose_name=_("Item locked for edition"), default=False + ) lock_user = models.ForeignKey( - User, related_name='+', on_delete=models.SET_NULL, - verbose_name=_("Locked by"), blank=True, null=True) + User, + related_name="+", + on_delete=models.SET_NULL, + verbose_name=_("Locked by"), + blank=True, + null=True, + ) ALT_NAMES = { - 'history_creator': SearchAltName( + "history_creator": SearchAltName( pgettext_lazy("key for text search", "created-by"), - 'history_creator__ishtaruser__person__cached_label__iexact' + "history_creator__ishtaruser__person__cached_label__iexact", ), - 'history_modifier': SearchAltName( + "history_modifier": SearchAltName( pgettext_lazy("key for text search", "modified-by"), - 'history_modifier__ishtaruser__person__cached_label__iexact' + "history_modifier__ishtaruser__person__cached_label__iexact", ), - 'modified_before': SearchAltName( + "modified_before": SearchAltName( pgettext_lazy("key for text search", "modified-before"), - 'last_modified__lte' + "last_modified__lte", ), - 'modified_after': SearchAltName( - pgettext_lazy("key for text search", "modified-after"), - 'last_modified__gte' + "modified_after": SearchAltName( + pgettext_lazy("key for text search", "modified-after"), "last_modified__gte" ), } @@ -1235,8 +1359,8 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, def update_external_id(self, save=False): if not self.EXTERNAL_ID_KEY or ( - self.external_id and - not getattr(self, 'auto_external_id', False)): + self.external_id and not getattr(self, "auto_external_id", False) + ): return external_id = get_generated_id(self.EXTERNAL_ID_KEY, self) if external_id == self.external_id: @@ -1250,10 +1374,10 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, return external_id def get_last_history_date(self): - q = self.history.values("history_date").order_by('-history_date') + q = self.history.values("history_date").order_by("-history_date") if not q.count(): return - return q.all()[0]['history_date'] + return q.all()[0]["history_date"] def get_previous(self, step=None, date=None, strict=False): """ @@ -1288,11 +1412,11 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, model = self.__class__ for k in get_all_field_names(model): field = model._meta.get_field(k) - if hasattr(field, 'rel') and field.rel: - if not hasattr(item, k + '_id'): + if hasattr(field, "rel") and field.rel: + if not hasattr(item, k + "_id"): setattr(item, k, getattr(self, k)) continue - val = getattr(item, k + '_id') + val = getattr(item, k + "_id") if not val: setattr(item, k, None) continue @@ -1301,8 +1425,9 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, setattr(item, k, val) except ObjectDoesNotExist: if strict: - raise HistoryError("The class %s has no pk %d" % ( - str(field.rel.to), val)) + raise HistoryError( + "The class %s has no pk %d" % (str(field.rel.to), val) + ) setattr(item, k, None) item.pk = self.pk return item @@ -1310,14 +1435,14 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, @property def last_edition_date(self): try: - return self.history.order_by('-history_date').all()[0].history_date + return self.history.order_by("-history_date").all()[0].history_date except (AttributeError, IndexError): return @property def history_creation_date(self): try: - return self.history.order_by('history_date').all()[0].history_date + return self.history.order_by("history_date").all()[0].history_date except (AttributeError, IndexError): return @@ -1336,14 +1461,15 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, try: field_keys = [f.name for f in self._meta.fields] for k in field_keys: - if k != 'id' and hasattr(self, k): + if k != "id" and hasattr(self, k): if not hasattr(new_item, k): k = k + "_id" setattr(self, k, getattr(new_item, k)) try: self.history_modifier = User.objects.get( - pk=new_item.history_modifier_id) + pk=new_item.history_modifier_id + ) except User.ObjectDoesNotExist: pass self.save() @@ -1373,51 +1499,64 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, values = {} for f in self._meta.fields: k = f.name - if k != 'id': + if k != "id": values[k] = getattr(self, k) return values def get_absolute_url(self): try: - return reverse('display-item', args=[self.SLUG, self.pk]) + return reverse("display-item", args=[self.SLUG, self.pk]) except NoReverseMatch: return def get_show_url(self): show_url = self.SHOW_URL if not show_url: - show_url = 'show-' + self.__class__.__name__.lower() + show_url = "show-" + self.__class__.__name__.lower() try: - return reverse(show_url, args=[self.pk, '']) + return reverse(show_url, args=[self.pk, ""]) except NoReverseMatch: return @property def associated_filename(self): - if [True for attr in ('get_town_label', 'get_department', 'reference', - 'short_class_name') if not hasattr(self, attr)]: - return '' - items = [slugify(self.get_department()), - slugify(self.get_town_label()).upper(), - slugify(self.short_class_name), - slugify(self.reference), - slugify(self.name or '').replace('-', '_').capitalize()] + if [ + True + for attr in ( + "get_town_label", + "get_department", + "reference", + "short_class_name", + ) + if not hasattr(self, attr) + ]: + return "" + items = [ + slugify(self.get_department()), + slugify(self.get_town_label()).upper(), + slugify(self.short_class_name), + slugify(self.reference), + slugify(self.name or "").replace("-", "_").capitalize(), + ] last_edition_date = self.last_edition_date if last_edition_date: - items.append(last_edition_date.strftime('%Y%m%d')) + items.append(last_edition_date.strftime("%Y%m%d")) else: - items.append('00000000') + items.append("00000000") return "-".join([str(item) for item in items]) def save(self, *args, **kwargs): created = not self.pk - if not getattr(self, 'skip_history_when_saving', False): - assert hasattr(self, 'history_modifier') + if not getattr(self, "skip_history_when_saving", False): + assert hasattr(self, "history_modifier") if created: self.history_creator = self.history_modifier # external ID can have related item not available before save - external_id_updated = kwargs.pop('external_id_updated') \ - if 'external_id_updated' in kwargs else False + external_id_updated = ( + kwargs.pop("external_id_updated") + if "external_id_updated" in kwargs + else False + ) if not created and not external_id_updated: self.update_external_id() super(BaseHistorizedItem, self).save(*args, **kwargs) @@ -1471,16 +1610,17 @@ class OwnPerms(object): checked :return: boolean """ - if not getattr(request.user, 'ishtaruser', None): + if not getattr(request.user, "ishtaruser", None): return False - splited = action_name.split('_') - action_own_name = splited[0] + '_own_' + '_'.join(splited[1:]) + splited = action_name.split("_") + action_own_name = splited[0] + "_own_" + "_".join(splited[1:]) user = request.user if action_own_name == "view_own_findbasket": action_own_name = "view_own_find" - return user.ishtaruser.has_right(action_name, request.session) or \ - (user.ishtaruser.has_right(action_own_name, request.session) - and self.is_own(user.ishtaruser)) + return user.ishtaruser.has_right(action_name, request.session) or ( + user.ishtaruser.has_right(action_own_name, request.session) + and self.is_own(user.ishtaruser) + ) def is_own(self, user, alt_query_own=None): """ @@ -1489,7 +1629,7 @@ class OwnPerms(object): IshtarUser = apps.get_model("ishtar_common", "IshtarUser") if isinstance(user, IshtarUser): ishtaruser = user - elif hasattr(user, 'ishtaruser'): + elif hasattr(user, "ishtaruser"): ishtaruser = user.ishtaruser else: return False @@ -1510,7 +1650,7 @@ class OwnPerms(object): IshtarUser = apps.get_model("ishtar_common", "IshtarUser") if isinstance(user, IshtarUser): ishtaruser = user - elif hasattr(user, 'ishtaruser'): + elif hasattr(user, "ishtaruser"): ishtaruser = user.ishtaruser else: return False @@ -1520,12 +1660,13 @@ class OwnPerms(object): return cls.objects.filter(query).count() @classmethod - def _return_get_owns(cls, owns, values, get_short_menu_class, - label_key='cached_label'): + def _return_get_owns( + cls, owns, values, get_short_menu_class, label_key="cached_label" + ): if not owns: return [] sorted_values = [] - if hasattr(cls, 'BASKET_MODEL'): + if hasattr(cls, "BASKET_MODEL"): owns_len = len(owns) for idx, item in enumerate(reversed(owns)): if get_short_menu_class: @@ -1537,24 +1678,31 @@ class OwnPerms(object): if not values: if not get_short_menu_class: return sorted_values + list( - sorted(owns, key=lambda x: getattr(x, label_key) or "")) + sorted(owns, key=lambda x: getattr(x, label_key) or "") + ) return sorted_values + list( - sorted(owns, key=lambda x: getattr(x[0], label_key) or "")) + sorted(owns, key=lambda x: getattr(x[0], label_key) or "") + ) if not get_short_menu_class: - return sorted_values + list( - sorted(owns, key=lambda x: x[label_key] or "")) - return sorted_values + list( - sorted(owns, key=lambda x: x[0][label_key] or "")) + return sorted_values + list(sorted(owns, key=lambda x: x[label_key] or "")) + return sorted_values + list(sorted(owns, key=lambda x: x[0][label_key] or "")) @classmethod - def get_owns(cls, user, replace_query=None, limit=None, values=None, - get_short_menu_class=False, menu_filtr=None): + def get_owns( + cls, + user, + replace_query=None, + limit=None, + values=None, + get_short_menu_class=False, + menu_filtr=None, + ): """ Get Own items """ if not replace_query: replace_query = {} - if hasattr(user, 'is_authenticated') and not user.is_authenticated(): + if hasattr(user, "is_authenticated") and not user.is_authenticated(): returned = cls.objects.filter(pk__isnull=True) if values: returned = [] @@ -1575,7 +1723,7 @@ class OwnPerms(object): return [] return cls.objects.filter(pk__isnull=True) items = [] - if hasattr(cls, 'BASKET_MODEL'): + if hasattr(cls, "BASKET_MODEL"): items = list(cls.BASKET_MODEL.objects.filter(user=ishtaruser).all()) query = cls.get_query_owns(ishtaruser) if not query and not replace_query: @@ -1590,24 +1738,25 @@ class OwnPerms(object): if values: q = q.values(*values) if limit: - items += list(q.order_by('-pk')[:limit]) + items += list(q.order_by("-pk")[:limit]) else: items += list(q.order_by(*cls._meta.ordering).all()) if get_short_menu_class: if values: - if 'id' not in values: + if "id" not in values: raise NotImplementedError( "Call of get_owns with get_short_menu_class option and" - " no 'id' in values is not implemented") + " no 'id' in values is not implemented" + ) my_items = [] for i in items: - if hasattr(cls, 'BASKET_MODEL') and \ - type(i) == cls.BASKET_MODEL: + if hasattr(cls, "BASKET_MODEL") and type(i) == cls.BASKET_MODEL: dct = dict([(k, getattr(i, k)) for k in values]) my_items.append( - (dct, cls.BASKET_MODEL.get_short_menu_class(i.pk))) + (dct, cls.BASKET_MODEL.get_short_menu_class(i.pk)) + ) else: - my_items.append((i, cls.get_short_menu_class(i['id']))) + my_items.append((i, cls.get_short_menu_class(i["id"]))) items = my_items else: items = [(i, cls.get_short_menu_class(i.pk)) for i in items] @@ -1654,7 +1803,7 @@ class State(models.Model): class Meta: verbose_name = _("State") - ordering = ['number'] + ordering = ["number"] def __str__(self): return self.label @@ -1667,7 +1816,10 @@ class Department(models.Model): label = models.CharField(_("Label"), max_length=30) number = models.CharField(_("Number"), unique=True, max_length=3) state = models.ForeignKey( - 'State', verbose_name=_("State"), blank=True, null=True, + "State", + verbose_name=_("State"), + blank=True, + null=True, on_delete=models.SET_NULL, ) objects = NumberManager() @@ -1675,7 +1827,7 @@ class Department(models.Model): class Meta: verbose_name = _("Department") verbose_name_plural = _("Departments") - ordering = ['number'] + ordering = ["number"] def __str__(self): return self.label @@ -1709,12 +1861,10 @@ class Arrondissement(models.Model): class Canton(models.Model): name = models.CharField("Nom", max_length=30) - arrondissement = models.ForeignKey(Arrondissement, - verbose_name="Arrondissement") + arrondissement = models.ForeignKey(Arrondissement, verbose_name="Arrondissement") def __str__(self): - return settings.JOINT.join( - (self.name, str(self.arrondissement))) + return settings.JOINT.join((self.name, str(self.arrondissement))) class TownManager(models.GeoManager): @@ -1725,37 +1875,46 @@ class TownManager(models.GeoManager): class Town(Imported, models.Model): name = models.CharField(_("Name"), max_length=100) surface = models.IntegerField(_("Surface (m2)"), blank=True, null=True) - center = models.PointField(_("Localisation"), srid=settings.SRID, - blank=True, null=True) + center = models.PointField( + _("Localisation"), srid=settings.SRID, blank=True, null=True + ) limit = models.MultiPolygonField(_("Limit"), blank=True, null=True) - numero_insee = models.CharField("Code commune (numéro INSEE)", - max_length=120) + numero_insee = models.CharField("Code commune (numéro INSEE)", max_length=120) departement = models.ForeignKey( - Department, verbose_name=_("Department"), - on_delete=models.SET_NULL, null=True, blank=True) + Department, + verbose_name=_("Department"), + on_delete=models.SET_NULL, + null=True, + blank=True, + ) year = models.IntegerField( - _("Year of creation"), null=True, blank=True, - help_text=_("Filling this field is relevant to distinguish old towns " - "from new towns.")) + _("Year of creation"), + null=True, + blank=True, + help_text=_( + "Filling this field is relevant to distinguish old towns " "from new towns." + ), + ) children = models.ManyToManyField( - 'Town', verbose_name=_("Town children"), blank=True, - related_name='parents') - cached_label = models.CharField(_("Cached name"), max_length=500, - null=True, blank=True, db_index=True) + "Town", verbose_name=_("Town children"), blank=True, related_name="parents" + ) + cached_label = models.CharField( + _("Cached name"), max_length=500, null=True, blank=True, db_index=True + ) objects = TownManager() class Meta: verbose_name = _("Town") verbose_name_plural = _("Towns") - if settings.COUNTRY == 'fr': - ordering = ['numero_insee'] - unique_together = (('numero_insee', 'year'),) + if settings.COUNTRY == "fr": + ordering = ["numero_insee"] + unique_together = (("numero_insee", "year"),) def natural_key(self): return (self.numero_insee, self.year) def history_compress(self): - return {'numero_insee': self.numero_insee, 'year': self.year or ""} + return {"numero_insee": self.numero_insee, "year": self.year or ""} @classmethod def get_documentation_string(cls): @@ -1763,13 +1922,14 @@ class Town(Imported, models.Model): Used for automatic documentation generation """ return "**name** {}, **numero_insee** {}, **cached_label** {}".format( - _("Name"), "Code commune (numéro INSEE)", _("Cached name")) + _("Name"), "Code commune (numéro INSEE)", _("Cached name") + ) - def get_values(self, prefix='', **kwargs): + def get_values(self, prefix="", **kwargs): return { prefix or "label": str(self), prefix + "name": self.name, - prefix + "numero_insee": self.numero_insee + prefix + "numero_insee": self.numero_insee, } @classmethod @@ -1780,8 +1940,10 @@ class Town(Imported, models.Model): for value in full_value: try: res.append( - cls.objects.get(numero_insee=value['numero_insee'], - year=value['year'] or None)) + cls.objects.get( + numero_insee=value["numero_insee"], year=value["year"] or None + ) + ) except cls.DoesNotExist: continue return res @@ -1818,8 +1980,8 @@ class Town(Imported, models.Model): else: parents = parents.union(parent.limit) # if union is a simple polygon make it a multi - if 'MULTI' not in parents.wkt: - parents = parents.wkt.replace('POLYGON', 'MULTIPOLYGON(') + ")" + if "MULTI" not in parents.wkt: + parents = parents.wkt.replace("POLYGON", "MULTIPOLYGON(") + ")" if not parents: return self.limit = parents @@ -1838,8 +2000,7 @@ class Town(Imported, models.Model): def generate_area(self, force=False): if not force and (self.surface or not self.limit): return - surface = self.limit.transform(settings.SURFACE_SRID, - clone=True).area + surface = self.limit.transform(settings.SURFACE_SRID, clone=True).area if surface > 214748364 or not surface: return False self.surface = surface @@ -1850,7 +2011,7 @@ class Town(Imported, models.Model): if not self.numero_insee or not self.children.count() or not self.year: return old_num = self.numero_insee[:] - numero = old_num.split('-')[0] + numero = old_num.split("-")[0] self.numero_insee = "{}-{}".format(numero, self.year) if self.numero_insee != old_num: return True @@ -1859,10 +2020,12 @@ class Town(Imported, models.Model): cached_label = self.name if settings.COUNTRY == "fr" and self.numero_insee: dpt_len = 2 - if self.numero_insee.startswith('97') or \ - self.numero_insee.startswith('98') or \ - self.numero_insee[0] not in ('0', '1', '2', '3', '4', '5', - '6', '7', '8', '9'): + if ( + self.numero_insee.startswith("97") + or self.numero_insee.startswith("98") + or self.numero_insee[0] + not in ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9") + ): dpt_len = 3 cached_label = "%s - %s" % (self.name, self.numero_insee[:dpt_len]) if self.year and self.children.count(): @@ -1872,7 +2035,7 @@ class Town(Imported, models.Model): def post_save_town(sender, **kwargs): cached_label_changed(sender, **kwargs) - town = kwargs['instance'] + town = kwargs["instance"] town.generate_geo() if town.update_town_code(): town.save() @@ -1882,7 +2045,7 @@ post_save.connect(post_save_town, sender=Town) def town_child_changed(sender, **kwargs): - town = kwargs['instance'] + town = kwargs["instance"] if town.update_town_code(): town.save() @@ -1892,53 +2055,75 @@ m2m_changed.connect(town_child_changed, sender=Town.children.through) class Address(BaseHistorizedItem): FIELDS = ( - "address", "address_complement", "postal_code", "town", - "precise_town", "country", - "alt_address", "alt_address_complement", "alt_postal_code", "alt_town", + "address", + "address_complement", + "postal_code", + "town", + "precise_town", + "country", + "alt_address", + "alt_address_complement", + "alt_postal_code", + "alt_town", "alt_country", - "phone", "phone_desc", "phone2", "phone_desc2", "phone3", "phone_desc3", - "raw_phone", "mobile_phone", "email", "alt_address_is_prefered" + "phone", + "phone_desc", + "phone2", + "phone_desc2", + "phone3", + "phone_desc3", + "raw_phone", + "mobile_phone", + "email", + "alt_address_is_prefered", ) address = models.TextField(_("Address"), blank=True, default="") address_complement = models.TextField( - _("Address complement"), blank=True, default="") - postal_code = models.CharField(_("Postal code"), max_length=10, null=True, - blank=True) - town = models.CharField(_("Town (freeform)"), max_length=150, null=True, - blank=True) + _("Address complement"), blank=True, default="" + ) + postal_code = models.CharField( + _("Postal code"), max_length=10, null=True, blank=True + ) + town = models.CharField(_("Town (freeform)"), max_length=150, null=True, blank=True) precise_town = models.ForeignKey( - Town, verbose_name=_("Town (precise)"), null=True, - blank=True) - country = models.CharField(_("Country"), max_length=30, null=True, - blank=True) - alt_address = models.TextField( - _("Other address: address"), blank=True, default="") + Town, verbose_name=_("Town (precise)"), null=True, blank=True + ) + country = models.CharField(_("Country"), max_length=30, null=True, blank=True) + alt_address = models.TextField(_("Other address: address"), blank=True, default="") alt_address_complement = models.TextField( - _("Other address: address complement"), blank=True, default="") - alt_postal_code = models.CharField(_("Other address: postal code"), - max_length=10, null=True, blank=True) - alt_town = models.CharField(_("Other address: town"), max_length=70, - null=True, blank=True) - alt_country = models.CharField(_("Other address: country"), - max_length=30, null=True, blank=True) + _("Other address: address complement"), blank=True, default="" + ) + alt_postal_code = models.CharField( + _("Other address: postal code"), max_length=10, null=True, blank=True + ) + alt_town = models.CharField( + _("Other address: town"), max_length=70, null=True, blank=True + ) + alt_country = models.CharField( + _("Other address: country"), max_length=30, null=True, blank=True + ) phone = models.CharField(_("Phone"), max_length=18, null=True, blank=True) - phone_desc = models.CharField(_("Phone description"), max_length=300, - null=True, blank=True) - phone2 = models.CharField(_("Phone description 2"), max_length=18, - null=True, blank=True) - phone_desc2 = models.CharField(_("Phone description 2"), max_length=300, - null=True, blank=True) - phone3 = models.CharField(_("Phone 3"), max_length=18, null=True, - blank=True) - phone_desc3 = models.CharField(_("Phone description 3"), max_length=300, - null=True, blank=True) + phone_desc = models.CharField( + _("Phone description"), max_length=300, null=True, blank=True + ) + phone2 = models.CharField( + _("Phone description 2"), max_length=18, null=True, blank=True + ) + phone_desc2 = models.CharField( + _("Phone description 2"), max_length=300, null=True, blank=True + ) + phone3 = models.CharField(_("Phone 3"), max_length=18, null=True, blank=True) + phone_desc3 = models.CharField( + _("Phone description 3"), max_length=300, null=True, blank=True + ) raw_phone = models.TextField(_("Raw phone"), blank=True, default="") - mobile_phone = models.CharField(_("Mobile phone"), max_length=18, - null=True, blank=True) - email = models.EmailField( - _("Email"), max_length=300, blank=True, null=True) + mobile_phone = models.CharField( + _("Mobile phone"), max_length=18, null=True, blank=True + ) + email = models.EmailField(_("Email"), max_length=300, blank=True, null=True) alt_address_is_prefered = models.BooleanField( - _("Alternative address is prefered"), default=False) + _("Alternative address is prefered"), default=False + ) history = HistoricalRecords(inherit=True) SUB_ADDRESSES = [] @@ -1948,28 +2133,25 @@ class Address(BaseHistorizedItem): def get_short_html_items(self): items = [] if self.address: - items.append( - """<span class="subadress">{}</span>""".format(self.address)) + items.append("""<span class="subadress">{}</span>""".format(self.address)) if self.address_complement: items.append( """<span class="subadress-complement">{}</span>""".format( - self.address_complement)) + self.address_complement + ) + ) if self.postal_code: items.append( - """<span class="postal-code">{}</span>""".format( - self.postal_code)) + """<span class="postal-code">{}</span>""".format(self.postal_code) + ) if self.precise_town: items.append( - """<span class="town">{}</span>""".format( - self.precise_town.name)) + """<span class="town">{}</span>""".format(self.precise_town.name) + ) elif self.town: - items.append( - """<span class="town">{}</span>""".format( - self.town)) + items.append("""<span class="town">{}</span>""".format(self.town)) if self.country: - items.append( - """<span class="country">{}</span>""".format( - self.country)) + items.append("""<span class="country">{}</span>""".format(self.country)) return items def get_short_html_detail(self): @@ -1977,9 +2159,7 @@ class Address(BaseHistorizedItem): items = self.get_short_html_items() if not items: items = [ - "<span class='no-address'>{}</span>".format( - _("No associated address") - ) + "<span class='no-address'>{}</span>".format(_("No associated address")) ] html += "".join(items) html += """</div>""" @@ -2041,25 +2221,24 @@ class Address(BaseHistorizedItem): return lbl def address_lbl(self): - lbl = '' - prefix = '' + lbl = "" + prefix = "" if self.alt_address_is_prefered: - prefix = 'alt_' - if getattr(self, prefix + 'address'): - lbl += getattr(self, prefix + 'address') - if getattr(self, prefix + 'address_complement'): + prefix = "alt_" + if getattr(self, prefix + "address"): + lbl += getattr(self, prefix + "address") + if getattr(self, prefix + "address_complement"): if lbl: lbl += "\n" - lbl += getattr(self, prefix + 'address_complement') - postal_code = getattr(self, prefix + 'postal_code') - town = getattr(self, prefix + 'town') + lbl += getattr(self, prefix + "address_complement") + postal_code = getattr(self, prefix + "postal_code") + town = getattr(self, prefix + "town") if postal_code or town: if lbl: lbl += "\n" lbl += "{}{}{}".format( - postal_code or '', - " " if postal_code and town else '', - town or '') + postal_code or "", " " if postal_code and town else "", town or "" + ) if self.phone: if lbl: lbl += "\n" @@ -2079,11 +2258,10 @@ class Merge(models.Model): merge_key = models.TextField(_("Merge key"), blank=True, null=True) merge_candidate = models.ManyToManyField("self", blank=True) merge_exclusion = models.ManyToManyField("self", blank=True) - archived = models.NullBooleanField(default=False, - blank=True, null=True) + archived = models.NullBooleanField(default=False, blank=True, null=True) # 1 for one word similarity, 2 for two word similarity, etc. MERGE_CLEMENCY = None - EMPTY_MERGE_KEY = '--' + EMPTY_MERGE_KEY = "--" MERGE_ATTRIBUTE = "name" class Meta: @@ -2093,7 +2271,7 @@ class Merge(models.Model): if self.archived: return merge_attr = getattr(self, self.MERGE_ATTRIBUTE) - self.merge_key = slugify(merge_attr if merge_attr else '') + self.merge_key = slugify(merge_attr if merge_attr else "") if not self.merge_key: self.merge_key = self.EMPTY_MERGE_KEY self.merge_key = self.merge_key @@ -2106,28 +2284,29 @@ class Merge(models.Model): self.save(merge_key_generated=True) if not self.pk or self.merge_key == self.EMPTY_MERGE_KEY: return - q = self.__class__.objects \ - .exclude(pk=self.pk) \ - .exclude(merge_exclusion=self) \ - .exclude(merge_candidate=self) \ + q = ( + self.__class__.objects.exclude(pk=self.pk) + .exclude(merge_exclusion=self) + .exclude(merge_candidate=self) .exclude(archived=True) + ) if not self.MERGE_CLEMENCY: q = q.filter(merge_key=self.merge_key) else: - subkeys_front = "-".join( - self.merge_key.split('-')[:self.MERGE_CLEMENCY]) - subkeys_back = "-".join( - self.merge_key.split('-')[-self.MERGE_CLEMENCY:]) - q = q.filter(Q(merge_key__istartswith=subkeys_front) | - Q(merge_key__iendswith=subkeys_back)) + subkeys_front = "-".join(self.merge_key.split("-")[: self.MERGE_CLEMENCY]) + subkeys_back = "-".join(self.merge_key.split("-")[-self.MERGE_CLEMENCY :]) + q = q.filter( + Q(merge_key__istartswith=subkeys_front) + | Q(merge_key__iendswith=subkeys_back) + ) for item in q.all(): self.merge_candidate.add(item) def save(self, *args, **kwargs): # prevent circular save merge_key_generated = False - if 'merge_key_generated' in kwargs: - merge_key_generated = kwargs.pop('merge_key_generated') + if "merge_key_generated" in kwargs: + merge_key_generated = kwargs.pop("merge_key_generated") self.generate_merge_key() item = super(Merge, self).save(*args, **kwargs) if not merge_key_generated: @@ -2142,26 +2321,22 @@ class Merge(models.Model): self.merge_exclusion.clear() def merge(self, item, keep_old=False, exclude_fields=None): - merge_model_objects(self, item, keep_old=keep_old, - exclude_fields=exclude_fields) + merge_model_objects( + self, item, keep_old=keep_old, exclude_fields=exclude_fields + ) self.generate_merge_candidate() - def __get_stats_cache_values(model_name, model_pk): StatsCache = apps.get_model("ishtar_common", "StatsCache") - q = StatsCache.objects.filter( - model=model_name, model_pk=model_pk - ) + q = StatsCache.objects.filter(model=model_name, model_pk=model_pk) nb = q.count() if nb >= 1: sc = q.all()[0] for extra in q.order_by("-id").all()[1:]: extra.delete() else: - sc = StatsCache.objects.create( - model=model_name, model_pk=model_pk - ) + sc = StatsCache.objects.create(model=model_name, model_pk=model_pk) values = sc.values if not values: values = {} @@ -2184,6 +2359,7 @@ def _update_stats(app, model, model_pk, funcname): sc.updated = datetime.datetime.now() sc.save() + def update_stats(statscache, item, funcname): if not settings.USE_BACKGROUND_TASK: current_values = statscache.values @@ -2213,19 +2389,17 @@ class DashboardFormItem: def last_stats_update(self): model_name = self._meta.app_label + "." + self._meta.model_name StatsCache = apps.get_model("ishtar_common", "StatsCache") - q = StatsCache.objects.filter( - model=model_name, model_pk=self.pk).order_by("-updated") + q = StatsCache.objects.filter(model=model_name, model_pk=self.pk).order_by( + "-updated" + ) if not q.count(): return return q.all()[0].updated - def _get_or_set_stats(self, funcname, update=False, - expected_type=None): + def _get_or_set_stats(self, funcname, update=False, expected_type=None): model_name = self._meta.app_label + "." + self._meta.model_name StatsCache = apps.get_model("ishtar_common", "StatsCache") - sc, __ = StatsCache.objects.get_or_create( - model=model_name, model_pk=self.pk - ) + sc, __ = StatsCache.objects.get_or_create(model=model_name, model_pk=self.pk) if not update: values = sc.values if funcname not in values: @@ -2243,62 +2417,62 @@ class DashboardFormItem: return values @classmethod - def get_periods(cls, slice='month', fltr={}, date_source='creation'): - date_var = date_source + '_date' - q = cls.objects.filter(**{date_var + '__isnull': False}) + def get_periods(cls, slice="month", fltr={}, date_source="creation"): + date_var = date_source + "_date" + q = cls.objects.filter(**{date_var + "__isnull": False}) if fltr: q = q.filter(**fltr) - if slice == 'year': - return [res[date_var].year for res in list(q.values(date_var) - .annotate( - Count("id")).order_by())] - elif slice == 'month': - return [(res[date_var].year, res[date_var].month) - for res in list(q.values(date_var) - .annotate(Count("id")).order_by())] + if slice == "year": + return [ + res[date_var].year + for res in list(q.values(date_var).annotate(Count("id")).order_by()) + ] + elif slice == "month": + return [ + (res[date_var].year, res[date_var].month) + for res in list(q.values(date_var).annotate(Count("id")).order_by()) + ] return [] @classmethod - def get_by_year(cls, year, fltr={}, date_source='creation'): - date_var = date_source + '_date' - q = cls.objects.filter(**{date_var + '__isnull': False}) + def get_by_year(cls, year, fltr={}, date_source="creation"): + date_var = date_source + "_date" + q = cls.objects.filter(**{date_var + "__isnull": False}) if fltr: q = q.filter(**fltr) - return q.filter( - **{date_var + '__year': year}).order_by('pk').distinct('pk') + return q.filter(**{date_var + "__year": year}).order_by("pk").distinct("pk") @classmethod - def get_by_month(cls, year, month, fltr={}, date_source='creation'): - date_var = date_source + '_date' - q = cls.objects.filter(**{date_var + '__isnull': False}) + def get_by_month(cls, year, month, fltr={}, date_source="creation"): + date_var = date_source + "_date" + q = cls.objects.filter(**{date_var + "__isnull": False}) if fltr: q = q.filter(**fltr) - q = q.filter( - **{date_var + '__year': year, date_var + '__month': month}) - return q.order_by('pk').distinct('pk') + q = q.filter(**{date_var + "__year": year, date_var + "__month": month}) + return q.order_by("pk").distinct("pk") @classmethod def get_total_number(cls, fltr=None): q = cls.objects if fltr: q = q.filter(**fltr) - return q.order_by('pk').distinct('pk').count() + return q.order_by("pk").distinct("pk").count() class DocumentItem: ALT_NAMES = { - 'documents__image__isnull': - SearchAltName( - pgettext_lazy("key for text search", "has-image"), - 'documents__image__isnull'), - 'documents__associated_url__isnull': - SearchAltName( - pgettext_lazy("key for text search", "has-url"), - 'documents__associated_url__isnull'), - 'documents__associated_file__isnull': - SearchAltName( - pgettext_lazy("key for text search", "has-attached-file"), - 'documents__associated_file__isnull'), + "documents__image__isnull": SearchAltName( + pgettext_lazy("key for text search", "has-image"), + "documents__image__isnull", + ), + "documents__associated_url__isnull": SearchAltName( + pgettext_lazy("key for text search", "has-url"), + "documents__associated_url__isnull", + ), + "documents__associated_file__isnull": SearchAltName( + pgettext_lazy("key for text search", "has-attached-file"), + "documents__associated_file__isnull", + ), } def public_representation(self): @@ -2313,29 +2487,35 @@ class DocumentItem: @property def images(self): - if not hasattr(self, 'documents'): + if not hasattr(self, "documents"): Document = apps.get_model("ishtar_common", "Document") return Document.objects.none() - return self.documents.filter( - image__isnull=False).exclude(image="").order_by("pk") + return ( + self.documents.filter(image__isnull=False).exclude(image="").order_by("pk") + ) @property def images_without_main_image(self): - if not hasattr(self, 'main_image') or not hasattr(self, 'documents'): + if not hasattr(self, "main_image") or not hasattr(self, "documents"): return self.images if not self.main_image: - return self.documents.filter( - image__isnull=False).exclude( - image="").order_by("pk") - return self.documents.filter( - image__isnull=False).exclude( - image="").exclude(pk=self.main_image.pk).order_by("pk") + return ( + self.documents.filter(image__isnull=False) + .exclude(image="") + .order_by("pk") + ) + return ( + self.documents.filter(image__isnull=False) + .exclude(image="") + .exclude(pk=self.main_image.pk) + .order_by("pk") + ) @property def pdf_attached(self): for document in self.documents.filter( - Q(associated_file__isnull=False) | - Q(source__associated_file__isnull=False)).all(): + Q(associated_file__isnull=False) | Q(source__associated_file__isnull=False) + ).all(): return document.pdf_attached def get_extra_actions(self, request): @@ -2348,22 +2528,21 @@ class DocumentItem: except AttributeError: actions = [] - if not hasattr(self, 'SLUG'): + if not hasattr(self, "SLUG"): return actions - can_add_doc = self.can_do(request, 'add_document') + can_add_doc = self.can_do(request, "add_document") if can_add_doc and ( - not hasattr(self, "is_locked") or - not self.is_locked(request.user)): + not hasattr(self, "is_locked") or not self.is_locked(request.user) + ): actions += [ ( - reverse("create-document") + "?{}={}".format( - self.SLUG, self.pk), + reverse("create-document") + "?{}={}".format(self.SLUG, self.pk), _("Add document/image"), "fa fa-plus", _("doc./image"), "", - False + False, ) ] return actions @@ -2378,27 +2557,27 @@ def clean_duplicate_association(document, related_item, action): return if class_name == "Find": for cr in document.context_records.filter( - base_finds__find__pk=related_item.pk).all(): + base_finds__find__pk=related_item.pk + ).all(): document.context_records.remove(cr) for ope in document.operations.filter( - context_record__base_finds__find__pk=related_item.pk).all(): + context_record__base_finds__find__pk=related_item.pk + ).all(): document.operations.remove(ope) return if class_name == "ContextRecord": - for ope in document.operations.filter( - context_record__pk=related_item.pk).all(): + for ope in document.operations.filter(context_record__pk=related_item.pk).all(): document.operations.remove(ope) - if document.finds.filter( - base_finds__context_record=related_item.pk).count(): + if document.finds.filter(base_finds__context_record=related_item.pk).count(): document.context_records.remove(related_item) return if class_name == "Operation": - if document.context_records.filter( - operation=related_item.pk).count(): + if document.context_records.filter(operation=related_item.pk).count(): document.operations.remove(related_item) return if document.finds.filter( - base_finds__context_record__operation=related_item.pk).count(): + base_finds__context_record__operation=related_item.pk + ).count(): document.operations.remove(related_item) return @@ -2422,12 +2601,10 @@ def document_attached_changed(sender, **kwargs): return for item in items: - clean_duplicate_association(instance, item, - kwargs.get("action", None)) + clean_duplicate_association(instance, item, kwargs.get("action", None)) for doc in item.documents.all(): doc.regenerate_all_ids() - q = item.documents.filter( - image__isnull=False).exclude(image='') + q = item.documents.filter(image__isnull=False).exclude(image="") if item.main_image: if q.filter(pk=item.main_image.pk).count(): return @@ -2438,7 +2615,7 @@ def document_attached_changed(sender, **kwargs): if not q.count(): return # by default get the lowest pk - item.main_image = q.order_by('pk').all()[0] + item.main_image = q.order_by("pk").all()[0] item.skip_history_when_saving = True item.save() @@ -2448,22 +2625,23 @@ class QuickAction: Quick action available from tables """ - def __init__(self, url, icon_class='', text='', target=None, rights=None, - module=None): + def __init__( + self, url, icon_class="", text="", target=None, rights=None, module=None + ): self.url = url self.icon_class = icon_class self.text = text self.rights = rights self.target = target self.module = module - assert self.target in ('one', 'many', None) + assert self.target in ("one", "many", None) def is_available(self, user, session=None, obj=None): if self.module and not getattr(get_current_profile(), self.module): return False if not self.rights: # no restriction return True - if not user or not hasattr(user, 'ishtaruser') or not user.ishtaruser: + if not user or not hasattr(user, "ishtaruser") or not user.ishtaruser: return False user = user.ishtaruser @@ -2491,8 +2669,16 @@ class QuickAction: class DynamicRequest: - def __init__(self, label, app_name, model_name, form_key, search_key, - type_query, search_query): + def __init__( + self, + label, + app_name, + model_name, + form_key, + search_key, + type_query, + search_query, + ): self.label = label self.form_key = form_key self.search_key = search_key @@ -2509,36 +2695,34 @@ class DynamicRequest: fields = {} for item in self.get_all_types().all(): fields[self.form_key + "-" + item.txt_idx] = forms.CharField( - label=str(self.label) + " " + str(item), - required=False + label=str(self.label) + " " + str(item), required=False ) return fields def get_extra_query(self, slug): - return { - self.type_query: slug - } + return {self.type_query: slug} def get_alt_names(self): alt_names = {} for item in self.get_all_types().all(): alt_names[self.form_key + "-" + item.txt_idx] = SearchAltName( - self.search_key + "-" + item.txt_idx, self.search_query, - self.get_extra_query(item.txt_idx), distinct_query=True + self.search_key + "-" + item.txt_idx, + self.search_query, + self.get_extra_query(item.txt_idx), + distinct_query=True, ) return alt_names class SpatialReferenceSystem(GeneralType): order = models.IntegerField(_("Order"), default=10) - auth_name = models.CharField( - _("Authority name"), default='EPSG', max_length=256) + auth_name = models.CharField(_("Authority name"), default="EPSG", max_length=256) srid = models.IntegerField(_("Authority SRID")) class Meta: verbose_name = _("Spatial reference system") verbose_name_plural = _("Spatial reference systems") - ordering = ('label',) + ordering = ("label",) @classmethod def get_documentation_string(cls): @@ -2557,37 +2741,46 @@ post_delete.connect(post_save_cache, sender=SpatialReferenceSystem) class GeoItem(models.Model): - GEO_SOURCE = ( - ('T', _("Town")), ('P', _("Precise")), ('M', _("Polygon")) - ) + GEO_SOURCE = (("T", _("Town")), ("P", _("Precise")), ("M", _("Polygon"))) # gis - x = models.FloatField(_('X'), blank=True, null=True) - y = models.FloatField(_('Y'), blank=True, null=True) - z = models.FloatField(_('Z'), blank=True, null=True) - estimated_error_x = models.FloatField(_('Estimated error for X'), - blank=True, null=True) - estimated_error_y = models.FloatField(_('Estimated error for Y'), - blank=True, null=True) - estimated_error_z = models.FloatField(_('Estimated error for Z'), - blank=True, null=True) + x = models.FloatField(_("X"), blank=True, null=True) + y = models.FloatField(_("Y"), blank=True, null=True) + z = models.FloatField(_("Z"), blank=True, null=True) + estimated_error_x = models.FloatField( + _("Estimated error for X"), blank=True, null=True + ) + estimated_error_y = models.FloatField( + _("Estimated error for Y"), blank=True, null=True + ) + estimated_error_z = models.FloatField( + _("Estimated error for Z"), blank=True, null=True + ) spatial_reference_system = models.ForeignKey( - SpatialReferenceSystem, verbose_name=_("Spatial Reference System"), - blank=True, null=True) + SpatialReferenceSystem, + verbose_name=_("Spatial Reference System"), + blank=True, + null=True, + ) point = models.PointField(_("Point"), blank=True, null=True, dim=3) point_2d = models.PointField(_("Point (2D)"), blank=True, null=True) point_source = models.CharField( - _("Point source"), choices=GEO_SOURCE, max_length=1, blank=True, - null=True) + _("Point source"), choices=GEO_SOURCE, max_length=1, blank=True, null=True + ) point_source_item = models.CharField( - _("Point source item"), max_length=100, blank=True, null=True) - multi_polygon = models.MultiPolygonField(_("Multi polygon"), blank=True, - null=True) + _("Point source item"), max_length=100, blank=True, null=True + ) + multi_polygon = models.MultiPolygonField(_("Multi polygon"), blank=True, null=True) multi_polygon_source = models.CharField( - _("Multi-polygon source"), choices=GEO_SOURCE, max_length=1, - blank=True, null=True) + _("Multi-polygon source"), + choices=GEO_SOURCE, + max_length=1, + blank=True, + null=True, + ) multi_polygon_source_item = models.CharField( - _("Multi polygon source item"), max_length=100, blank=True, null=True) + _("Multi polygon source item"), max_length=100, blank=True, null=True + ) GEO_LABEL = "" @@ -2621,13 +2814,18 @@ class GeoItem(models.Model): if not self.point_2d: return "" profile = get_current_profile() - if not profile.display_srs or not profile.display_srs.srid or ( + if ( + not profile.display_srs + or not profile.display_srs.srid + or ( profile.display_srs == self.spatial_reference_system - and self.x and self.y): + and self.x + and self.y + ) + ): x, y = self.x, self.y else: - point = self.point_2d.transform(profile.display_srs.srid, - clone=True) + point = self.point_2d.transform(profile.display_srs.srid, clone=True) x, y = point.x, point.y if rounded: return round(x, 5), round(y, 5) @@ -2641,40 +2839,38 @@ class GeoItem(models.Model): return profile.display_srs def get_precise_points(self): - if self.point_source == 'P' and self.point_2d: + if self.point_source == "P" and self.point_2d: return self.point_2d, self.point, self.point_source_item def get_precise_polygons(self): - if self.multi_polygon_source == 'P' and self.multi_polygon: + if self.multi_polygon_source == "P" and self.multi_polygon: return self.multi_polygon, self.multi_polygon_source_item def most_precise_geo(self): - if self.point_source == 'M': - return 'multi_polygon' + if self.point_source == "M": + return "multi_polygon" current_source = str(self.__class__._meta.verbose_name) - if self.multi_polygon_source_item == current_source \ - and (self.multi_polygon_source == "P" or - (self.point_source_item != current_source and - self.point_source != "P")): - return 'multi_polygon' - if self.point_source_item == current_source \ - and self.point_source == 'P': - return 'point' - if self.multi_polygon_source == 'P': - return 'multi_polygon' - if self.point_source == 'P': - return 'point' + if self.multi_polygon_source_item == current_source and ( + self.multi_polygon_source == "P" + or (self.point_source_item != current_source and self.point_source != "P") + ): + return "multi_polygon" + if self.point_source_item == current_source and self.point_source == "P": + return "point" + if self.multi_polygon_source == "P": + return "multi_polygon" + if self.point_source == "P": + return "point" if self.multi_polygon: - return 'multi_polygon' + return "multi_polygon" if self.point_2d: - return 'point' + return "point" def geo_point_source(self): if not self.point_source: return "" return "{} - {}".format( - dict(self.GEO_SOURCE)[self.point_source], - self.point_source_item + dict(self.GEO_SOURCE)[self.point_source], self.point_source_item ) def geo_polygon_source(self): @@ -2682,31 +2878,33 @@ class GeoItem(models.Model): return "" return "{} - {}".format( dict(self.GEO_SOURCE)[self.multi_polygon_source], - self.multi_polygon_source_item + self.multi_polygon_source_item, ) def _geojson_serialize(self, geom_attr): if not hasattr(self, geom_attr): return "" - cached_label_key = 'cached_label' + cached_label_key = "cached_label" if self.GEO_LABEL: cached_label_key = self.GEO_LABEL if getattr(self, "CACHED_LABELS", None): cached_label_key = self.CACHED_LABELS[-1] geojson = serialize( - 'geojson', + "geojson", self.__class__.objects.filter(pk=self.pk), - geometry_field=geom_attr, fields=(cached_label_key,)) + geometry_field=geom_attr, + fields=(cached_label_key,), + ) geojson_dct = json.loads(geojson) profile = get_current_profile() precision = profile.point_precision - features = geojson_dct.pop('features') + features = geojson_dct.pop("features") for idx in range(len(features)): feature = features[idx] - lbl = feature['properties'].pop(cached_label_key) - feature['properties']['name'] = lbl - feature['properties']['id'] = self.pk + lbl = feature["properties"].pop(cached_label_key) + feature["properties"]["name"] = lbl + feature["properties"]["id"] = self.pk if precision is not None: geom_type = feature["geometry"].get("type", None) if geom_type == "Point": @@ -2714,20 +2912,20 @@ class GeoItem(models.Model): round(coord, precision) for coord in feature["geometry"]["coordinates"] ] - geojson_dct['features'] = features - geojson_dct['link_template'] = simple_link_to_window(self).replace( - '999999', '<pk>' + geojson_dct["features"] = features + geojson_dct["link_template"] = simple_link_to_window(self).replace( + "999999", "<pk>" ) geojson = json.dumps(geojson_dct) return geojson @property def point_2d_geojson(self): - return self._geojson_serialize('point_2d') + return self._geojson_serialize("point_2d") @property def multi_polygon_geojson(self): - return self._geojson_serialize('multi_polygon') + return self._geojson_serialize("multi_polygon") class ImageContainerModel: @@ -2742,10 +2940,12 @@ class ImageContainerModel: class CompleteIdentifierItem(models.Model, ImageContainerModel): HAS_QR_CODE = True complete_identifier = models.TextField( - _("Complete identifier"), blank=True, default="") + _("Complete identifier"), blank=True, default="" + ) custom_index = models.IntegerField("Custom index", blank=True, null=True) - qrcode = models.ImageField(upload_to=get_image_path, blank=True, null=True, - max_length=255) + qrcode = models.ImageField( + upload_to=get_image_path, blank=True, null=True, max_length=255 + ) class Meta: abstract = True @@ -2773,16 +2973,20 @@ class CompleteIdentifierItem(models.Model, ImageContainerModel): tiny_url = TinyUrl() tiny_url.link = url tiny_url.save() - short_url = scheme + "://" + site.domain + reverse( - 'tiny-redirect', args=[tiny_url.get_short_id()]) + short_url = ( + scheme + + "://" + + site.domain + + reverse("tiny-redirect", args=[tiny_url.get_short_id()]) + ) qr = pyqrcode.create(short_url, version=settings.ISHTAR_QRCODE_VERSION) tmpdir_created = False if not tmpdir: tmpdir = tempfile.mkdtemp("-qrcode") tmpdir_created = True - filename = tmpdir + os.sep + 'qrcode.png' + filename = tmpdir + os.sep + "qrcode.png" qr.png(filename, scale=settings.ISHTAR_QRCODE_SCALE) - with open(filename, 'rb') as qrfile: + with open(filename, "rb") as qrfile: self.qrcode.save("qrcode.png", File(qrfile)) self.skip_history_when_saving = True self._no_move = True @@ -2794,13 +2998,12 @@ class CompleteIdentifierItem(models.Model, ImageContainerModel): SLUG = getattr(self, "SLUG", None) if not SLUG: return "" - complete_identifier = get_generated_id( - SLUG + "_complete_identifier", self) + complete_identifier = get_generated_id(SLUG + "_complete_identifier", self) if complete_identifier: return complete_identifier - cached_label_key = 'cached_label' - if getattr(self, 'GEO_LABEL', None): - cached_label_key = getattr(self, 'GEO_LABEL', None) + cached_label_key = "cached_label" + if getattr(self, "GEO_LABEL", None): + cached_label_key = getattr(self, "GEO_LABEL", None) if hasattr(self, "CACHED_COMPLETE_ID"): cached_label_key = self.CACHED_COMPLETE_ID if not cached_label_key: @@ -2829,8 +3032,7 @@ class CompleteIdentifierItem(models.Model, ImageContainerModel): return getattr(self, "get_index_" + key)() model = self.__class__ try: - self_keys = set( - list(model.objects.filter(pk=self.pk).values_list(*keys))) + self_keys = set(list(model.objects.filter(pk=self.pk).values_list(*keys))) except Exception: # bad settings - not managed here return if len(self_keys) != 1: # key is not distinct @@ -2846,12 +3048,12 @@ class CompleteIdentifierItem(models.Model, ImageContainerModel): for idx, key in enumerate(keys): q = q.filter(**{key: self_keys[idx]}) try: - r = q.aggregate(max_index=Max('custom_index')) + r = q.aggregate(max_index=Max("custom_index")) except Exception: # bad settings return - if not r['max_index']: + if not r["max_index"]: return 1 - return r['max_index'] + 1 + return r["max_index"] + 1 def save(self, *args, **kwargs): super(CompleteIdentifierItem, self).save(*args, **kwargs) @@ -2887,8 +3089,8 @@ class SearchVectorConfig: self.func = func def format(self, value): - if value == 'None': - value = '' + if value == "None": + value = "" if not self.func: return [value] return self.func(value) @@ -2898,11 +3100,12 @@ class ShortMenuItem: """ Item available in the short menu """ + UP_MODEL_QUERY = {} @classmethod def get_short_menu_class(cls, pk): - return '' + return "" @property def short_class_name(self): @@ -2914,6 +3117,7 @@ class MainItem(ShortMenuItem): Item with quick actions available from tables Extra actions are available from sheets """ + QUICK_ACTIONS = [] @classmethod @@ -2925,10 +3129,14 @@ class MainItem(ShortMenuItem): for action in cls.QUICK_ACTIONS: if not action.is_available(user, session=session, obj=obj): continue - qas.append([action.base_url, - mark_safe(action.text), - mark_safe(action.rendered_icon), - action.target or ""]) + qas.append( + [ + action.base_url, + mark_safe(action.text), + mark_safe(action.rendered_icon), + action.target or "", + ] + ) return qas @classmethod @@ -2947,21 +3155,21 @@ class MainItem(ShortMenuItem): self.save() def get_extra_actions(self, request): - if not hasattr(self, 'SLUG'): + if not hasattr(self, "SLUG"): return [] actions = [] if request.user.is_superuser and hasattr(self, "auto_external_id"): actions += [ ( - reverse("regenerate-external-id") + "?{}={}".format( - self.SLUG, self.pk), + reverse("regenerate-external-id") + + "?{}={}".format(self.SLUG, self.pk), _("Regenerate ID"), "fa fa-key", _("regen."), "btn-info", True, - 200 + 200, ) ] |