Skip to content

Commit

Permalink
SiaeGroup: update annotated count
Browse files Browse the repository at this point in the history
  • Loading branch information
raphodn committed Oct 20, 2023
1 parent 8ccd821 commit 7d15141
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 33 deletions.
17 changes: 8 additions & 9 deletions lemarche/siaes/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from django.contrib.contenttypes.admin import GenericTabularInline
from django.contrib.gis import admin as gis_admin
from django.db import models
from django.db.models import Count
from django.urls import reverse
from django.utils.html import format_html, mark_safe
from fieldsets_with_inlines import FieldsetsInlineMixin
Expand Down Expand Up @@ -717,14 +716,14 @@ def image_url_display(self, siae_image):

@admin.register(SiaeGroup, site=admin_site)
class SiaeGroupAdmin(admin.ModelAdmin):
list_display = ["id", "name", "nb_siaes", "created_at"]
list_display = ["id", "name", "siae_live_count_annotated_with_link", "created_at"]
search_fields = ["id", "name"]
search_help_text = "Cherche sur les champs : ID, Nom"

prepopulated_fields = {"slug": ("name",)}
autocomplete_fields = ["sectors"]
readonly_fields = [f"{field}_last_updated" for field in SiaeGroup.TRACK_UPDATE_FIELDS] + [
"nb_siaes",
"siae_live_count_annotated_with_link",
"logo_url_display",
"created_at",
"updated_at",
Expand All @@ -745,7 +744,7 @@ class SiaeGroupAdmin(admin.ModelAdmin):
"year_constitution",
"siae_count",
"siae_count_last_updated",
"nb_siaes",
"siae_live_count_annotated_with_link",
"employees_insertion_count",
"employees_insertion_count_last_updated",
"employees_permanent_count",
Expand Down Expand Up @@ -782,15 +781,15 @@ class SiaeGroupAdmin(admin.ModelAdmin):

def get_queryset(self, request):
qs = super().get_queryset(request)
qs = qs.annotate(siae_count_live=Count("siaes", distinct=True))
qs = qs.with_siae_stats()
return qs

def nb_siaes(self, siae_group):
def siae_live_count_annotated_with_link(self, siae_group):
url = reverse("admin:siaes_siae_changelist") + f"?groups__in={siae_group.id}"
return format_html(f'<a href="{url}">{siae_group.siae_count_live}</a>')
return format_html(f'<a href="{url}">{siae_group.siae_live_count_annotated}</a>')

nb_siaes.short_description = "Nombre de structures (live)"
nb_siaes.admin_order_field = "siae_count_live"
siae_live_count_annotated_with_link.short_description = "Nombre de structures (live)"
siae_live_count_annotated_with_link.admin_order_field = "siae_live_count_annotated"

def logo_url_display(self, siae_group):
if siae_group.logo_url:
Expand Down
5 changes: 5 additions & 0 deletions lemarche/siaes/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ class Meta:
name = factory.Faker("company", locale="fr_FR")
# slug auto-generated

@factory.post_generation
def siaes(self, create, extracted, **kwargs):
if extracted:
self.siaes.add(*extracted)


class SiaeFactory(DjangoModelFactory):
class Meta:
Expand Down
14 changes: 11 additions & 3 deletions lemarche/siaes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ def get_city_filter(perimeter, with_country=False):
return filters


class SiaeGroupQuerySet(models.QuerySet):
def with_siae_stats(self):
return self.annotate(siae_live_count_annotated=Count("siaes", distinct=True))


class SiaeGroup(models.Model):
TRACK_UPDATE_FIELDS = [
# set last_updated fields
Expand Down Expand Up @@ -119,6 +124,8 @@ class SiaeGroup(models.Model):
created_at = models.DateTimeField("Date de création", default=timezone.now)
updated_at = models.DateTimeField("Date de modification", auto_now=True)

objects = models.Manager.from_queryset(SiaeGroupQuerySet)()

class Meta:
verbose_name = "Groupement"
verbose_name_plural = "Groupements"
Expand Down Expand Up @@ -1124,9 +1131,10 @@ def siae_networks_changed(sender, instance, action, **kwargs):

@receiver(m2m_changed, sender=Siae.groups.through)
def siae_groups_changed(sender, instance, action, **kwargs):
if action in ("post_add", "post_remove", "post_clear"):
instance.group_count = instance.groups.count()
instance.save()
if isinstance(instance, Siae):
if action in ("post_add", "post_remove", "post_clear"):
instance.group_count = instance.groups.count()
instance.save()


class SiaeUser(models.Model):
Expand Down
14 changes: 14 additions & 0 deletions lemarche/siaes/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ def test_update_last_updated_fields(self):
self.assertNotEqual(siae_group.employees_insertion_count_last_updated, employees_insertion_count_last_updated)


class SiaeGroupQuerySetTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.siae_1 = SiaeFactory()
cls.siae_2 = SiaeFactory(is_active=False)
cls.siae_group = SiaeGroupFactory()
cls.siae_group_with_siaes = SiaeGroupFactory(siaes=[cls.siae_1, cls.siae_2])

def test_with_siae_stats(self):
siae_group_queryset = SiaeGroup.objects.with_siae_stats()
self.assertEqual(siae_group_queryset.get(id=self.siae_group.id).siae_live_count_annotated, 0)
self.assertEqual(siae_group_queryset.get(id=self.siae_group_with_siaes.id).siae_live_count_annotated, 1)


class SiaeModelTest(TestCase):
def setUp(self):
pass
Expand Down
2 changes: 1 addition & 1 deletion lemarche/templates/tenders/_card_list_item.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div>
<time class="fs-sm d-block" aria-label="Date de clôture">
<span>{{ tender.deadline_date|default:"" }}</span>
{% if tender.deadline_date_is_outdated %}
{% if tender.deadline_date_is_outdated_annotated %}
<span class="badge badge-xs badge-base badge-pill badge-pilotage float-right">Clôturé</span>
{% endif %}
{% if tender.status == tender.STATUS_DRAFT %}
Expand Down
2 changes: 1 addition & 1 deletion lemarche/templates/tenders/_list_item_buyer.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<div class="col-md-8" style="border-right:1px solid;">
<p class="mb-1">
Date de clôture : {{ tender.deadline_date|default:"" }}
{% if tender.deadline_date_is_outdated %}
{% if tender.deadline_date_is_outdated_annotated %}
<span class="badge badge-sm badge-base badge-pill badge-pilotage">Clôturé</span>
{% endif %}
{% if tender.status == tender.STATUS_DRAFT %}
Expand Down
2 changes: 1 addition & 1 deletion lemarche/templates/tenders/_list_item_network.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<div class="col-md-12">
<p class="mb-1">
Date de clôture : {{ tender.deadline_date|default:"" }}
{% if tender.deadline_date_is_outdated %}
{% if tender.deadline_date_is_outdated_annotated %}
<span class="badge badge-sm badge-base badge-pill badge-pilotage">Clôturé</span>
{% endif %}
<span class="float-right badge badge-base badge-pill badge-emploi">
Expand Down
2 changes: 1 addition & 1 deletion lemarche/templates/tenders/_list_item_siae.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<div class="col-md-12">
<p class="mb-1">
Date de clôture : {{ tender.deadline_date|default:"" }}
{% if tender.deadline_date_is_outdated %}
{% if tender.deadline_date_is_outdated_annotated %}
<span class="badge badge-sm badge-base badge-pill badge-pilotage">Clôturé</span>
{% endif %}
{% if not tender.tendersiae_set.first.detail_display_date %}
Expand Down
6 changes: 4 additions & 2 deletions lemarche/tenders/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,14 @@ def filter_with_siaes(self, siaes):

def with_deadline_date_is_outdated(self, limit_date=datetime.today()):
return self.annotate(
deadline_date_is_outdated=ExpressionWrapper(Q(deadline_date__lt=limit_date), output_field=BooleanField())
deadline_date_is_outdated_annotated=ExpressionWrapper(
Q(deadline_date__lt=limit_date), output_field=BooleanField()
)
)

def order_by_deadline_date(self, limit_date=datetime.today()):
return self.with_deadline_date_is_outdated(limit_date=limit_date).order_by(
"deadline_date_is_outdated", "deadline_date", "-updated_at"
"deadline_date_is_outdated_annotated", "deadline_date", "-updated_at"
)

def with_question_stats(self):
Expand Down
30 changes: 15 additions & 15 deletions lemarche/tenders/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,24 @@ def setUpTestData(cls):
# coords=Point(5.8862, 45.1106),
)

def test_by_user_queryset(self):
def test_by_user(self):
user = UserFactory()
TenderFactory(author=user)
TenderFactory()
self.assertEqual(Tender.objects.by_user(user).count(), 1)

def test_validated_queryset(self):
def test_validated(self):
TenderFactory(validated_at=timezone.now())
TenderFactory(validated_at=None)
self.assertEqual(Tender.objects.validated().count(), 1)

def test_is_live_queryset(self):
def test_is_live(self):
TenderFactory(deadline_date=timezone.now() + timedelta(days=1))
TenderFactory(deadline_date=timezone.now() - timedelta(days=1))
# TenderFactory(deadline_date=None) # cannot be None
self.assertEqual(Tender.objects.is_live().count(), 1)

def test_has_amount_queryset(self):
def test_has_amount(self):
TenderFactory()
TenderFactory(amount=tender_constants.AMOUNT_RANGE_0_1)
TenderFactory(amount_exact=1000)
Expand All @@ -193,7 +193,7 @@ def test_has_amount_queryset(self):
self.assertEqual(Tender.objects.has_amount().count(), 3)
self.assertEqual(Tender.objects.filter(amount__isnull=True, amount_exact__isnull=True).count(), 1)

def test_in_sectors_queryset(self):
def test_in_sectors(self):
sector_1 = SectorFactory(name="Un secteur")
sector_2 = SectorFactory(name="Un deuxieme secteur")
sector_3 = SectorFactory(name="Autre")
Expand All @@ -205,7 +205,7 @@ def test_in_sectors_queryset(self):
self.assertEqual(Tender.objects.in_sectors([sector_1, sector_3]).count(), 1)
self.assertEqual(Tender.objects.in_sectors([sector_3]).count(), 0)

def test_in_perimeters_queryset(self):
def test_in_perimeters(self):
# create the Perimeters
auvergne_rhone_alpes_perimeter = PerimeterFactory(
name="Auvergne-Rhône-Alpes", kind=Perimeter.KIND_REGION, insee_code="R84"
Expand Down Expand Up @@ -248,7 +248,7 @@ def test_default_order(self):
self.assertEqual(tender_queryset.count(), 3)
self.assertEqual(tender_queryset.first().id, self.tender_3.id)

def test_order_by_deadline_date_queryset(self):
def test_order_by_deadline_date(self):
tender_queryset = Tender.objects.order_by_deadline_date()
self.assertEqual(tender_queryset.count(), 3)
self.assertEqual(tender_queryset.first().id, self.tender_2.id)
Expand Down Expand Up @@ -306,13 +306,13 @@ def setUpTestData(cls):
TenderQuestionFactory(tender=cls.tender_with_siae_1)
TenderQuestionFactory(tender=cls.tender_with_siae_1)

def test_filter_with_siaes_queryset(self):
def test_filter_with_siaes(self):
self.tender_with_siae_2.validated_at = None
self.tender_with_siae_2.save()
# tender_with_siae_2 is not validated
self.assertEqual(Tender.objects.filter_with_siaes(self.user_siae.siaes.all()).count(), 1)

def test_with_siae_stats_queryset(self):
def test_with_siae_stats(self):
self.assertEqual(Tender.objects.count(), 2 + 1)
tender_with_siae_1 = Tender.objects.with_siae_stats().filter(id=self.tender_with_siae_1.id).first()
self.assertEqual(tender_with_siae_1.siaes.count(), 6)
Expand Down Expand Up @@ -340,7 +340,7 @@ def test_with_siae_stats_queryset(self):
self.assertEqual(tender_without_siae.siae_detail_contact_click_count, 0)
self.assertEqual(tender_without_siae.siae_detail_contact_click_since_last_seen_date_count, 0)

def test_siae_with_tender_stats_queryset(self):
def test_siae_with_tender_stats(self):
self.assertEqual(Siae.objects.count(), 6 + 1)
siae_with_tender_1 = Siae.objects.with_tender_stats().filter(id=self.siae_with_tender_1.id).first()
# self.assertEqual(siae_with_tender_1.tenders.count(), 2)
Expand All @@ -361,14 +361,14 @@ def test_with_question_stats(self):
self.assertEqual(tender_with_siae_1.questions.count(), 2)
self.assertEqual(tender_with_siae_1.question_count, 2)

def test_with_deadline_date_is_outdated_queryset(self):
def test_with_deadline_date_is_outdated(self):
TenderFactory(deadline_date=date_last_week)
tender_queryset = Tender.objects.with_deadline_date_is_outdated()
tender_3 = tender_queryset.first() # order by -created_at
self.assertTrue(tender_3.deadline_date_is_outdated)
self.assertTrue(tender_3.deadline_date_is_outdated_annotated)
tender_with_siae_1 = tender_queryset.last()
self.assertEqual(tender_with_siae_1.id, self.tender_with_siae_1.id)
self.assertFalse(tender_with_siae_1.deadline_date_is_outdated)
self.assertFalse(tender_with_siae_1.deadline_date_is_outdated_annotated)

# doesn't work when chaining these 2 querysets: adds duplicates...
# def test_chain_querysets(self):
Expand Down Expand Up @@ -551,15 +551,15 @@ def setUpTestData(cls):
siaes=[siae_with_tender_2, siae_with_tender_3], deadline_date=date_tomorrow
)

def test_email_click_reminder_queryset(self):
def test_email_click_reminder(self):
lt_days_ago = timezone.now() - timedelta(days=2)
gte_days_ago = timezone.now() - timedelta(days=2 + 1)
self.assertEqual(TenderSiae.objects.count(), 2 + 2 + 4)
self.assertEqual(
TenderSiae.objects.email_click_reminder(lt_days_ago=lt_days_ago, gte_days_ago=gte_days_ago).count(), 1
)

def test_detail_contact_click_post_reminder_queryset(self):
def test_detail_contact_click_post_reminder(self):
lt_days_ago = timezone.now() - timedelta(days=2)
gte_days_ago = timezone.now() - timedelta(days=2 + 1)
self.assertEqual(TenderSiae.objects.count(), 2 + 2 + 4)
Expand Down

0 comments on commit 7d15141

Please sign in to comment.