summaryrefslogtreecommitdiff
path: root/ishtar_common/models_common.py
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2023-04-18 17:21:38 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2023-04-18 17:21:38 +0200
commita589b3ef96c9adf4e408713201ffe7d269e4f78f (patch)
tree5ad635c7fba5f5d1d1a40a5c5066321c870edfac /ishtar_common/models_common.py
parent2b9862d29073e31cc89e807fd355a691b0d932dd (diff)
downloadIshtar-a589b3ef96c9adf4e408713201ffe7d269e4f78f.tar.bz2
Ishtar-a589b3ef96c9adf4e408713201ffe7d269e4f78f.zip
Document -> Town/Area: models, admin, forms
Diffstat (limited to 'ishtar_common/models_common.py')
-rw-r--r--ishtar_common/models_common.py359
1 files changed, 189 insertions, 170 deletions
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py
index 1e6da2b7d..8942d57c2 100644
--- a/ishtar_common/models_common.py
+++ b/ishtar_common/models_common.py
@@ -1978,6 +1978,175 @@ class OwnPerms(object):
return q
+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",
+ ),
+ }
+
+ def documents_list(self) -> list:
+ Document = apps.get_model("ishtar_common", "Document")
+ return self.get_associated_main_item_list("documents", Document)
+
+ def public_representation(self):
+ images = []
+ if getattr(self, "main_image", None):
+ images.append(self.main_image.public_representation())
+ images += [
+ image.public_representation()
+ for image in self.images_without_main_image.all()
+ ]
+ return {"images": images}
+
+ @property
+ def images(self):
+ 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")
+ )
+
+ @property
+ def images_number(self):
+ return self.images.count()
+
+ @property
+ def images_without_main_image(self):
+ 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")
+ )
+
+ @property
+ def pdf_attached(self):
+ for document in self.documents.filter(
+ Q(associated_file__isnull=False) | Q(source__associated_file__isnull=False)
+ ).all():
+ return document.pdf_attached
+
+ def get_extra_actions(self, request):
+ """
+ For sheet template: return "Add document / image" action
+ """
+ # url, base_text, icon, extra_text, extra css class, is a quick action
+ try:
+ actions = super(DocumentItem, self).get_extra_actions(request)
+ except AttributeError:
+ actions = []
+
+ if not hasattr(self, "SLUG"):
+ return actions
+
+ 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)
+ ):
+ actions += [
+ (
+ reverse("create-document") + "?{}={}".format(self.SLUG, self.pk),
+ _("Add document/image"),
+ "fa fa-plus",
+ _("doc./image"),
+ "",
+ False,
+ )
+ ]
+ return actions
+
+
+def clean_duplicate_association(document, related_item, action):
+ profile = get_current_profile()
+ if not profile.clean_redundant_document_association or action != "post_add":
+ return
+ class_name = related_item.__class__.__name__
+ if class_name not in ("Find", "ContextRecord", "Operation"):
+ return
+ if class_name == "Find":
+ for cr in document.context_records.filter(
+ 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():
+ document.operations.remove(ope)
+ return
+ if class_name == "ContextRecord":
+ 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():
+ document.context_records.remove(related_item)
+ return
+ if class_name == "Operation":
+ 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():
+ document.operations.remove(related_item)
+ return
+
+
+def document_attached_changed(sender, **kwargs):
+ # associate a default main image
+ instance = kwargs.get("instance", None)
+ model = kwargs.get("model", None)
+ pk_set = kwargs.get("pk_set", None)
+ if not instance or not model:
+ return
+
+ if hasattr(instance, "documents"):
+ items = [instance]
+ else:
+ if not pk_set:
+ return
+ try:
+ items = [model.objects.get(pk=pk) for pk in pk_set]
+ except model.DoesNotExist:
+ return
+
+ for item in items:
+ 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="")
+ if item.main_image:
+ if q.filter(pk=item.main_image.pk).count():
+ return
+ # the association has disappear not the main image anymore
+ item.main_image = None
+ item.skip_history_when_saving = True
+ item.save()
+ if not q.count():
+ return
+ # by default get the lowest pk
+ item.main_image = q.order_by("pk").all()[0]
+ item.skip_history_when_saving = True
+ item.save()
+
+
class NumberManager(models.Manager):
def get_by_natural_key(self, number):
return self.get(number=number)
@@ -2927,7 +3096,8 @@ class TownManager(models.Manager):
return self.get(numero_insee=numero_insee, year=year)
-class Town(GeographicItem, Imported, models.Model):
+class Town(GeographicItem, Imported, DocumentItem, models.Model):
+ SLUG = "town"
name = models.CharField(_("Name"), max_length=100)
surface = models.IntegerField(_("Surface (m2)"), blank=True, null=True)
center = models.PointField(
@@ -2956,6 +3126,17 @@ class Town(GeographicItem, Imported, models.Model):
cached_label = models.CharField(
_("Cached name"), max_length=500, null=True, blank=True, db_index=True
)
+ documents = models.ManyToManyField(
+ "Document", related_name="towns", verbose_name=_("Documents"), blank=True
+ )
+ main_image = models.ForeignKey(
+ "Document",
+ related_name="main_image_towns",
+ on_delete=models.SET_NULL,
+ verbose_name=_("Main image"),
+ blank=True,
+ null=True,
+ )
objects = TownManager()
class Meta:
@@ -3085,6 +3266,12 @@ class Town(GeographicItem, Imported, models.Model):
if self.numero_insee != old_num:
return True
+ def _get_base_image_path(self):
+ if self.numero_insee and len(self.numero_insee) == 5:
+ prefix = self.numero_insee[:2]
+ return f"{self.SLUG}/{prefix}"
+ return self.SLUG
+
def _generate_cached_label(self):
cached_label = self.name
if settings.COUNTRY == "fr" and self.numero_insee:
@@ -3112,6 +3299,7 @@ def post_save_town(sender, **kwargs):
post_save.connect(post_save_town, sender=Town)
m2m_changed.connect(geodata_attached_changed, sender=Town.geodata.through)
+m2m_changed.connect(document_attached_changed, sender=Town.documents.through)
def town_child_changed(sender, **kwargs):
@@ -3563,175 +3751,6 @@ class DashboardFormItem:
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",
- ),
- }
-
- def documents_list(self) -> list:
- Document = apps.get_model("ishtar_common", "Document")
- return self.get_associated_main_item_list("documents", Document)
-
- def public_representation(self):
- images = []
- if getattr(self, "main_image", None):
- images.append(self.main_image.public_representation())
- images += [
- image.public_representation()
- for image in self.images_without_main_image.all()
- ]
- return {"images": images}
-
- @property
- def images(self):
- 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")
- )
-
- @property
- def images_number(self):
- return self.images.count()
-
- @property
- def images_without_main_image(self):
- 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")
- )
-
- @property
- def pdf_attached(self):
- for document in self.documents.filter(
- Q(associated_file__isnull=False) | Q(source__associated_file__isnull=False)
- ).all():
- return document.pdf_attached
-
- def get_extra_actions(self, request):
- """
- For sheet template: return "Add document / image" action
- """
- # url, base_text, icon, extra_text, extra css class, is a quick action
- try:
- actions = super(DocumentItem, self).get_extra_actions(request)
- except AttributeError:
- actions = []
-
- if not hasattr(self, "SLUG"):
- return actions
-
- 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)
- ):
- actions += [
- (
- reverse("create-document") + "?{}={}".format(self.SLUG, self.pk),
- _("Add document/image"),
- "fa fa-plus",
- _("doc./image"),
- "",
- False,
- )
- ]
- return actions
-
-
-def clean_duplicate_association(document, related_item, action):
- profile = get_current_profile()
- if not profile.clean_redundant_document_association or action != "post_add":
- return
- class_name = related_item.__class__.__name__
- if class_name not in ("Find", "ContextRecord", "Operation"):
- return
- if class_name == "Find":
- for cr in document.context_records.filter(
- 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():
- document.operations.remove(ope)
- return
- if class_name == "ContextRecord":
- 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():
- document.context_records.remove(related_item)
- return
- if class_name == "Operation":
- 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():
- document.operations.remove(related_item)
- return
-
-
-def document_attached_changed(sender, **kwargs):
- # associate a default main image
- instance = kwargs.get("instance", None)
- model = kwargs.get("model", None)
- pk_set = kwargs.get("pk_set", None)
- if not instance or not model:
- return
-
- if hasattr(instance, "documents"):
- items = [instance]
- else:
- if not pk_set:
- return
- try:
- items = [model.objects.get(pk=pk) for pk in pk_set]
- except model.DoesNotExist:
- return
-
- for item in items:
- 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="")
- if item.main_image:
- if q.filter(pk=item.main_image.pk).count():
- return
- # the association has disappear not the main image anymore
- item.main_image = None
- item.skip_history_when_saving = True
- item.save()
- if not q.count():
- return
- # by default get the lowest pk
- item.main_image = q.order_by("pk").all()[0]
- item.skip_history_when_saving = True
- item.save()
-
-
class QuickAction:
"""
Quick action available from tables