diff --git a/lemarche/favorites/tests.py b/lemarche/favorites/tests.py index fb8fa3bd6..ca011dcff 100644 --- a/lemarche/favorites/tests.py +++ b/lemarche/favorites/tests.py @@ -50,17 +50,9 @@ def test_with_siae_stats(self): self.assertEqual(favorite_list_queryset.get(id=favorite_list.id).siae_count_annotated, 0) self.assertEqual(favorite_list_queryset.get(id=favorite_list_with_siaes.id).siae_count_annotated, 2) - def test_siae_annotate_with_user_favorite_list_count(self): + def test_siae_with_in_user_favorite_list_stats(self): FavoriteListFactory(user=self.user, siaes=[self.siae_1]) FavoriteListFactory(user=self.user, siaes=[self.siae_1]) - siae_queryset = Siae.objects.annotate_with_user_favorite_list_count(self.user) - self.assertEqual(siae_queryset.get(id=self.siae_1.id).in_user_favorite_list_count, 2) - self.assertEqual(siae_queryset.get(id=self.siae_2.id).in_user_favorite_list_count, 0) - - def test_siae_annotate_with_user_favorite_list_ids(self): - favorite_list_1 = FavoriteListFactory(user=self.user, siaes=[self.siae_1]) - FavoriteListFactory(user=self.user, siaes=[self.siae_1]) - siae_queryset = Siae.objects.annotate_with_user_favorite_list_ids(self.user) - self.assertEqual(len(siae_queryset.get(id=self.siae_1.id).in_user_favorite_list_ids), 2) - self.assertEqual(len(siae_queryset.get(id=self.siae_2.id).in_user_favorite_list_ids), 0) - self.assertEqual(siae_queryset.get(id=self.siae_1.id).in_user_favorite_list_ids[0], favorite_list_1.id) + siae_queryset = Siae.objects.with_in_user_favorite_list_stats(self.user) + self.assertEqual(siae_queryset.get(id=self.siae_1.id).in_user_favorite_list_count_annotated, 2) + self.assertEqual(siae_queryset.get(id=self.siae_2.id).in_user_favorite_list_count_annotated, 0) diff --git a/lemarche/siaes/admin.py b/lemarche/siaes/admin.py index 9c8673d88..076a76fd2 100644 --- a/lemarche/siaes/admin.py +++ b/lemarche/siaes/admin.py @@ -141,9 +141,9 @@ class SiaeAdmin(FieldsetsInlineMixin, gis_admin.OSMGeoAdmin): "label_count_with_link", "client_reference_count_with_link", "image_count_with_link", - "tender_email_send_count_with_link", - "tender_detail_display_count_with_link", - "tender_detail_contact_click_count_with_link", + "tender_email_send_count_annotated_with_link", + "tender_detail_display_count_annotated_with_link", + "tender_detail_contact_click_count_annotated_with_link", "created_at", ] list_filter = [ @@ -175,11 +175,11 @@ class SiaeAdmin(FieldsetsInlineMixin, gis_admin.OSMGeoAdmin): "coords_display", "logo_url", "logo_url_display", - "tender_count_with_link", - "tender_email_send_count_with_link", - "tender_email_link_click_count_with_link", - "tender_detail_display_count_with_link", - "tender_detail_contact_click_count_with_link", + "tender_count_annotated_with_link", + "tender_email_send_count_annotated_with_link", + "tender_email_link_click_count_annotated_with_link", + "tender_detail_display_count_annotated_with_link", + "tender_detail_contact_click_count_annotated_with_link", "logs_display", # "import_raw_object", "import_raw_object_display", @@ -295,11 +295,11 @@ class SiaeAdmin(FieldsetsInlineMixin, gis_admin.OSMGeoAdmin): "Besoins des acheteurs", { "fields": ( - "tender_count_with_link", - "tender_email_send_count_with_link", - "tender_email_link_click_count_with_link", - "tender_detail_display_count_with_link", - "tender_detail_contact_click_count_with_link", + "tender_count_annotated_with_link", + "tender_email_send_count_annotated_with_link", + "tender_email_link_click_count_annotated_with_link", + "tender_detail_display_count_annotated_with_link", + "tender_detail_contact_click_count_annotated_with_link", ) }, ), @@ -531,52 +531,54 @@ def import_raw_object_display(self, instance: Siae = None): import_raw_object_display.short_description = Siae._meta.get_field("import_raw_object").verbose_name - def tender_count_with_link(self, siae): + def tender_count_annotated_with_link(self, siae): url = reverse("admin:tenders_tender_changelist") + f"?siaes__in={siae.id}" - return format_html(f'{getattr(siae, "tender_count", 0)}') + return format_html(f'{getattr(siae, "tender_count_annotated", 0)}') - tender_count_with_link.short_description = "Besoins concernés" - tender_count_with_link.admin_order_field = "tender_count" + tender_count_annotated_with_link.short_description = "Besoins concernés" + tender_count_annotated_with_link.admin_order_field = "tender_count_annotated" - def tender_email_send_count_with_link(self, siae): + def tender_email_send_count_annotated_with_link(self, siae): url = ( reverse("admin:tenders_tender_changelist") + f"?siaes__in={siae.id}&tendersiae__email_send_date__isnull=False" ) - return format_html(f'{getattr(siae, "tender_email_send_count", 0)}') + return format_html(f'{getattr(siae, "tender_email_send_count_annotated", 0)}') - tender_email_send_count_with_link.short_description = "Besoins reçus" - tender_email_send_count_with_link.admin_order_field = "tender_email_send_count" + tender_email_send_count_annotated_with_link.short_description = "Besoins reçus" + tender_email_send_count_annotated_with_link.admin_order_field = "tender_email_send_count_annotated" - def tender_email_link_click_count_with_link(self, siae): + def tender_email_link_click_count_annotated_with_link(self, siae): url = ( reverse("admin:tenders_tender_changelist") + f"?siaes__in={siae.id}&tendersiae__email_link_click_date__isnull=False" ) - return format_html(f'{getattr(siae, "tender_email_link_click_count", 0)}') + return format_html(f'{getattr(siae, "tender_email_link_click_count_annotated", 0)}') - tender_email_link_click_count_with_link.short_description = "Besoins cliqués" - tender_email_link_click_count_with_link.admin_order_field = "tender_email_link_click_count" + tender_email_link_click_count_annotated_with_link.short_description = "Besoins cliqués" + tender_email_link_click_count_annotated_with_link.admin_order_field = "tender_email_link_click_count_annotated" - def tender_detail_display_count_with_link(self, siae): + def tender_detail_display_count_annotated_with_link(self, siae): url = ( reverse("admin:tenders_tender_changelist") + f"?siaes__in={siae.id}&tendersiae__detail_display_date__isnull=False" ) - return format_html(f'{getattr(siae, "tender_detail_display_count", 0)}') + return format_html(f'{getattr(siae, "tender_detail_display_count_annotated", 0)}') - tender_detail_display_count_with_link.short_description = "Besoins vues" - tender_detail_display_count_with_link.admin_order_field = "tender_detail_display_count" + tender_detail_display_count_annotated_with_link.short_description = "Besoins vues" + tender_detail_display_count_annotated_with_link.admin_order_field = "tender_detail_display_count_annotated" - def tender_detail_contact_click_count_with_link(self, siae): + def tender_detail_contact_click_count_annotated_with_link(self, siae): url = ( reverse("admin:tenders_tender_changelist") + f"?siaes__in={siae.id}&tendersiae__detail_contact_click_date__isnull=False" ) - return format_html(f'{getattr(siae, "tender_detail_contact_click_count", 0)}') + return format_html(f'{getattr(siae, "tender_detail_contact_click_count_annotated", 0)}') - tender_detail_contact_click_count_with_link.short_description = "Besoins intéressés" - tender_detail_contact_click_count_with_link.admin_order_field = "tender_detail_contact_click_count" + tender_detail_contact_click_count_annotated_with_link.short_description = "Besoins intéressés" + tender_detail_contact_click_count_annotated_with_link.admin_order_field = ( + "tender_detail_contact_click_count_annotated" + ) def logs_display(self, siae=None): if siae: diff --git a/lemarche/siaes/factories.py b/lemarche/siaes/factories.py index f77bd586e..9f4dad352 100644 --- a/lemarche/siaes/factories.py +++ b/lemarche/siaes/factories.py @@ -4,7 +4,7 @@ from factory.django import DjangoModelFactory from lemarche.siaes import constants as siae_constants -from lemarche.siaes.models import Siae, SiaeClientReference, SiaeGroup, SiaeLabelOld, SiaeOffer +from lemarche.siaes.models import Siae, SiaeClientReference, SiaeGroup, SiaeImage, SiaeLabelOld, SiaeOffer class SiaeGroupFactory(DjangoModelFactory): @@ -75,3 +75,10 @@ class Meta: model = SiaeLabelOld name = factory.Faker("name", locale="fr_FR") + + +class SiaeImageFactory(DjangoModelFactory): + class Meta: + model = SiaeImage + + name = factory.Faker("name", locale="fr_FR") diff --git a/lemarche/siaes/models.py b/lemarche/siaes/models.py index 06e0ea87e..da4b63fc7 100644 --- a/lemarche/siaes/models.py +++ b/lemarche/siaes/models.py @@ -5,7 +5,6 @@ from django.contrib.gis.db import models as gis_models from django.contrib.gis.db.models.functions import Distance from django.contrib.gis.measure import D -from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.search import TrigramSimilarity # SearchVector from django.db import IntegrityError, models, transaction from django.db.models import BooleanField, Case, CharField, Count, F, IntegerField, PositiveIntegerField, Q, Sum, When @@ -312,20 +311,12 @@ def with_country_geo_range(self): def exclude_country_geo_range(self): return self.exclude(Q(geo_range=siae_constants.GEO_RANGE_COUNTRY)) - def annotate_with_user_favorite_list_count(self, user): + def with_in_user_favorite_list_stats(self, user): """ Enrich each Siae with the number of occurences in the user's favorite lists """ return self.prefetch_related("favorite_lists").annotate( - in_user_favorite_list_count=Count("favorite_lists", filter=Q(favorite_lists__user=user)) - ) - - def annotate_with_user_favorite_list_ids(self, user): - """ - Enrich each Siae with the list of occurences in the user's favorite lists - """ - return self.prefetch_related("favorite_lists").annotate( - in_user_favorite_list_ids=ArrayAgg("favorite_lists__pk", filter=Q(favorite_lists__user=user)) + in_user_favorite_list_count_annotated=Count("favorite_lists", filter=Q(favorite_lists__user=user)) ) def has_contact_email(self): @@ -371,23 +362,23 @@ def with_tender_stats(self): Enrich each Siae with stats on their linked Tender """ return self.annotate( - tender_count=Count("tenders", distinct=True), - tender_email_send_count=Sum( + tender_count_annotated=Count("tenders", distinct=True), + tender_email_send_count_annotated=Sum( Case(When(tendersiae__email_send_date__isnull=False, then=1), default=0, output_field=IntegerField()) ), - tender_email_link_click_count=Sum( + tender_email_link_click_count_annotated=Sum( Case( When(tendersiae__email_link_click_date__isnull=False, then=1), default=0, output_field=IntegerField(), ) ), - tender_detail_display_count=Sum( + tender_detail_display_count_annotated=Sum( Case( When(tendersiae__detail_display_date__isnull=False, then=1), default=0, output_field=IntegerField() ) ), - tender_detail_contact_click_count=Sum( + tender_detail_contact_click_count_annotated=Sum( Case( When(tendersiae__detail_contact_click_date__isnull=False, then=1), default=0, @@ -396,16 +387,16 @@ def with_tender_stats(self): ), ) - def annotate_with_brand_or_name(self, with_order_by=False): + def with_brand_or_name(self, with_order_by=False): """ We usually want to display the brand by default See Siae.name_display() """ qs = self.annotate( - brand_or_name=Case(When(brand="", then=F("name")), default=F("brand"), output_field=CharField()) + brand_or_name_annotated=Case(When(brand="", then=F("name")), default=F("brand"), output_field=CharField()) ) if with_order_by: - qs = qs.order_by("brand_or_name") + qs = qs.order_by("brand_or_name_annotated") return qs def with_content_filled_stats(self): @@ -417,12 +408,12 @@ def with_content_filled_stats(self): - Level 4 (full): user_count + sector_count + description + logo + client_reference_count + image_count + label_count # noqa """ return self.annotate( - content_filled_basic=Case( + content_filled_basic_annotated=Case( When(Q(user_count__gte=1) & Q(sector_count__gte=1) & ~Q(description=""), then=True), default=False, output_field=BooleanField(), ), - content_filled_full=Case( + content_filled_full_annotated=Case( When( Q(user_count__gte=1) & Q(sector_count__gte=1) @@ -441,24 +432,23 @@ def with_content_filled_stats(self): def content_not_filled(self): return self.filter(Q(sector_count=0) | Q(contact_email="")) - def with_employees_count(self): + def with_employees_stats(self): """ Enrich each Siae with count of employees Annotate first to use the field "c2_etp_count" if employees_insertion_count is null Next, the sum of an integer and a null value is null, so we check if fields are not null before make sum """ return self.annotate( - employees_insertion_count_with_c2_etp=Case( + employees_insertion_count_with_c2_etp_annotated=Case( When(employees_insertion_count=None, then=Round(F("c2_etp_count"))), default=F("employees_insertion_count"), output_field=PositiveIntegerField(), - ) - ).annotate( - employees_count=Case( - When(employees_insertion_count_with_c2_etp=None, then=F("employees_permanent_count")), - When(employees_permanent_count=None, then=F("employees_insertion_count_with_c2_etp")), - default=F("employees_insertion_count_with_c2_etp") + F("employees_permanent_count"), - ) + ), + employees_count_annotated=Case( + When(employees_insertion_count_with_c2_etp_annotated=None, then=F("employees_permanent_count")), + When(employees_permanent_count=None, then=F("employees_insertion_count_with_c2_etp_annotated")), + default=F("employees_insertion_count_with_c2_etp_annotated") + F("employees_permanent_count"), + ), ) diff --git a/lemarche/siaes/tests.py b/lemarche/siaes/tests.py index e80de8ec1..96bf79554 100644 --- a/lemarche/siaes/tests.py +++ b/lemarche/siaes/tests.py @@ -6,7 +6,14 @@ from lemarche.perimeters.models import Perimeter from lemarche.sectors.factories import SectorFactory from lemarche.siaes import constants as siae_constants, utils as siae_utils -from lemarche.siaes.factories import SiaeFactory, SiaeGroupFactory, SiaeLabelOldFactory, SiaeOfferFactory +from lemarche.siaes.factories import ( + SiaeClientReferenceFactory, + SiaeFactory, + SiaeGroupFactory, + SiaeImageFactory, + SiaeLabelOldFactory, + SiaeOfferFactory, +) from lemarche.siaes.models import Siae, SiaeGroup, SiaeLabel, SiaeUser from lemarche.users.factories import UserFactory @@ -345,32 +352,43 @@ def test_has_user_queryset(self): self.assertEqual(Siae.objects.count(), 2) self.assertEqual(Siae.objects.has_user().count(), 1) - # def test_annotate_with_user_favorite_list_ids(self): + # def test_with_in_user_favorite_list_stats(self): # see favorites > tests.py # def test_with_tender_stats(self): # see tenders > tests.py > TenderModelQuerysetStatsTest - def test_annotate_with_brand_or_name(self): + def test_with_brand_or_name(self): siae_1 = SiaeFactory(name="ZZZ", brand="ABC") siae_2 = SiaeFactory(name="Test", brand="") - siae_queryset = Siae.objects.annotate_with_brand_or_name() - self.assertEqual(siae_queryset.get(id=siae_1.id).brand_or_name, siae_1.brand) - self.assertEqual(siae_queryset.get(id=siae_2.id).brand_or_name, siae_2.name) + siae_queryset = Siae.objects.with_brand_or_name() + self.assertEqual(siae_queryset.get(id=siae_1.id).brand_or_name_annotated, siae_1.brand) + self.assertEqual(siae_queryset.get(id=siae_2.id).brand_or_name_annotated, siae_2.name) self.assertEqual(siae_queryset.first(), siae_2) # default order is by "name" - siae_queryset_with_order_by = Siae.objects.annotate_with_brand_or_name().order_by("brand_or_name") + siae_queryset_with_order_by = Siae.objects.with_brand_or_name().order_by("brand_or_name_annotated") self.assertEqual(siae_queryset_with_order_by.first(), siae_1) - siae_queryset_with_order_by_parameter = Siae.objects.annotate_with_brand_or_name(with_order_by=True) + siae_queryset_with_order_by_parameter = Siae.objects.with_brand_or_name(with_order_by=True) self.assertEqual(siae_queryset_with_order_by_parameter.first(), siae_1) def test_with_content_filled_stats(self): siae_empty = SiaeFactory(name="Empty") - siae_filled_basic = SiaeFactory(name="Filled basic", user_count=1, sector_count=2, description="desc") + siae_filled_basic = SiaeFactory(name="Filled basic", user_count=1, sector_count=1, description="desc") + siae_filled_full = SiaeFactory( + name="Filled full", user_count=1, sector_count=1, description="desc", logo_url="https://logo.png" + ) + SiaeClientReferenceFactory(siae=siae_filled_full) + SiaeImageFactory(siae=siae_filled_full) + SiaeLabelOldFactory(siae=siae_filled_full) + siae_filled_full.save() siae_queryset = Siae.objects.with_content_filled_stats() - self.assertEqual(siae_queryset.get(id=siae_empty.id).content_filled_basic, False) - self.assertEqual(siae_queryset.get(id=siae_filled_basic.id).content_filled_basic, True) - - def test_with_employees_count(self): + self.assertEqual(siae_queryset.get(id=siae_empty.id).content_filled_basic_annotated, False) + self.assertEqual(siae_queryset.get(id=siae_empty.id).content_filled_full_annotated, False) + self.assertEqual(siae_queryset.get(id=siae_filled_basic.id).content_filled_basic_annotated, True) + self.assertEqual(siae_queryset.get(id=siae_filled_basic.id).content_filled_full_annotated, False) + self.assertEqual(siae_queryset.get(id=siae_filled_full.id).content_filled_basic_annotated, True) + self.assertEqual(siae_queryset.get(id=siae_filled_full.id).content_filled_full_annotated, True) + + def test_with_employees_stats(self): siae_1 = SiaeFactory() siae_2 = SiaeFactory(employees_insertion_count=10) siae_3 = SiaeFactory(c2_etp_count=19.5) @@ -380,30 +398,30 @@ def test_with_employees_count(self): siae_7 = SiaeFactory(c2_etp_count=2550, employees_permanent_count=1500) siae_8 = SiaeFactory(employees_insertion_count=125, c2_etp_count=158, employees_permanent_count=88) - siae_queryset = Siae.objects.with_employees_count() - self.assertEqual(siae_queryset.get(id=siae_1.id).employees_insertion_count_with_c2_etp, None) - self.assertEqual(siae_queryset.get(id=siae_1.id).employees_count, None) + siae_queryset = Siae.objects.with_employees_stats() + self.assertEqual(siae_queryset.get(id=siae_1.id).employees_insertion_count_with_c2_etp_annotated, None) + self.assertEqual(siae_queryset.get(id=siae_1.id).employees_count_annotated, None) - self.assertEqual(siae_queryset.get(id=siae_2.id).employees_insertion_count_with_c2_etp, 10) - self.assertEqual(siae_queryset.get(id=siae_2.id).employees_count, 10) + self.assertEqual(siae_queryset.get(id=siae_2.id).employees_insertion_count_with_c2_etp_annotated, 10) + self.assertEqual(siae_queryset.get(id=siae_2.id).employees_count_annotated, 10) - self.assertEqual(siae_queryset.get(id=siae_3.id).employees_insertion_count_with_c2_etp, 20) - self.assertEqual(siae_queryset.get(id=siae_3.id).employees_count, 20) + self.assertEqual(siae_queryset.get(id=siae_3.id).employees_insertion_count_with_c2_etp_annotated, 20) + self.assertEqual(siae_queryset.get(id=siae_3.id).employees_count_annotated, 20) - self.assertEqual(siae_queryset.get(id=siae_4.id).employees_insertion_count_with_c2_etp, None) - self.assertEqual(siae_queryset.get(id=siae_4.id).employees_count, 155) + self.assertEqual(siae_queryset.get(id=siae_4.id).employees_insertion_count_with_c2_etp_annotated, None) + self.assertEqual(siae_queryset.get(id=siae_4.id).employees_count_annotated, 155) - self.assertEqual(siae_queryset.get(id=siae_5.id).employees_insertion_count_with_c2_etp, 22) - self.assertEqual(siae_queryset.get(id=siae_5.id).employees_count, 22) + self.assertEqual(siae_queryset.get(id=siae_5.id).employees_insertion_count_with_c2_etp_annotated, 22) + self.assertEqual(siae_queryset.get(id=siae_5.id).employees_count_annotated, 22) - self.assertEqual(siae_queryset.get(id=siae_6.id).employees_insertion_count_with_c2_etp, 280) - self.assertEqual(siae_queryset.get(id=siae_6.id).employees_count, 280 + 105) + self.assertEqual(siae_queryset.get(id=siae_6.id).employees_insertion_count_with_c2_etp_annotated, 280) + self.assertEqual(siae_queryset.get(id=siae_6.id).employees_count_annotated, 280 + 105) - self.assertEqual(siae_queryset.get(id=siae_7.id).employees_insertion_count_with_c2_etp, 2550) - self.assertEqual(siae_queryset.get(id=siae_7.id).employees_count, 2550 + 1500) + self.assertEqual(siae_queryset.get(id=siae_7.id).employees_insertion_count_with_c2_etp_annotated, 2550) + self.assertEqual(siae_queryset.get(id=siae_7.id).employees_count_annotated, 2550 + 1500) - self.assertEqual(siae_queryset.get(id=siae_8.id).employees_insertion_count_with_c2_etp, 125) - self.assertEqual(siae_queryset.get(id=siae_8.id).employees_count, 125 + 88) + self.assertEqual(siae_queryset.get(id=siae_8.id).employees_insertion_count_with_c2_etp_annotated, 125) + self.assertEqual(siae_queryset.get(id=siae_8.id).employees_count_annotated, 125 + 88) class SiaeModelPerimeterQuerysetTest(TestCase): diff --git a/lemarche/templates/dashboard/siae_edit_info.html b/lemarche/templates/dashboard/siae_edit_info.html index 5b002fd1a..7ac058579 100644 --- a/lemarche/templates/dashboard/siae_edit_info.html +++ b/lemarche/templates/dashboard/siae_edit_info.html @@ -41,7 +41,7 @@ - {% if last_3_siae_content_filled_full %} + {% if last_3_siae_content_filled_full_annotated %}
@@ -50,8 +50,8 @@
Inspirez-vous des fiches commerciales des prestataires inclusifs - {{ last_3_siae_content_filled_full.0.name_display }} - et {{ last_3_siae_content_filled_full.1.name_display }}. + {{ last_3_siae_content_filled_full_annotated.0.name_display }} + et {{ last_3_siae_content_filled_full_annotated.1.name_display }}.
Une fiche commerciale bien complétée c'est davantage de chances d'être sollicité par des clients. diff --git a/lemarche/templates/networks/dashboard_network_siae_list.html b/lemarche/templates/networks/dashboard_network_siae_list.html index 688269835..db0a61e32 100644 --- a/lemarche/templates/networks/dashboard_network_siae_list.html +++ b/lemarche/templates/networks/dashboard_network_siae_list.html @@ -82,25 +82,25 @@