Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Besoins): Admin : Avoir le status de la mise en relation #1121

Merged
merged 3 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 44 additions & 11 deletions lemarche/tenders/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,44 +780,77 @@ class TenderSiaeSourceFilter(MultiChoice):
BUTTON_LABEL = "Appliquer"


class TenderSiaeStatusFilter(admin.SimpleListFilter):
title = "Status"
parameter_name = "status"

def lookups(self, request, model_admin):
return tender_constants.TENDER_SIAE_STATUS_CHOICES

def queryset(self, request, queryset):
value = self.value()
return queryset.filter(status_annotated=value)


@admin.register(TenderSiae, site=admin_site)
class TenderSiaeAdmin(admin.ModelAdmin):
list_display = ["created_at", "siae_with_link", "tender", "source"]
list_display = ["created_at", "siae_with_app_link", "tender_with_link", "source", "status"]
list_filter = [
("source", TenderSiaeSourceFilter),
TenderSiaeStatusFilter,
"survey_transactioned_answer",
]

readonly_fields = [field for field in TenderSiae.READONLY_FIELDS] + [
"tender",
"siae",
"siae_with_app_link",
"tender",
"tender_with_link",
"logs_display",
]

fieldsets = (
(
None,
{"fields": ("tender", "siae", "source", "found_with_ai")},
{"fields": ("siae", "siae_with_app_link", "tender_with_link", "source", "found_with_ai")},
),
("Mise en relation", {"fields": TenderSiae.FIELDS_RELATION}),
("Mise en relation", {"fields": (*TenderSiae.FIELDS_RELATION, "status")}),
("Transaction ?", {"fields": TenderSiae.FIELDS_SURVEY_TRANSACTIONED}),
("Stats", {"fields": ("logs_display",)}),
("Dates", {"fields": ("created_at", "updated_at")}),
)

def get_queryset(self, request):
qs = super().get_queryset(request)
qs = qs.with_prefetch_related()
qs = qs.with_status()
return qs

def has_add_permission(self, request):
return False

def has_change_permission(self, request, obj=None):
return False

def siae_with_link(self, tendersiae_list):
url = reverse("siae:detail", args=[tendersiae_list.siae.slug])
return format_html(
f'<a href="{url}" target="_blank">{tendersiae_list.siae.brand} ({tendersiae_list.siae.name})</a>'
)
def siae_with_app_link(self, tendersiae):
url = reverse("siae:detail", args=[tendersiae.siae.slug])
return format_html(f'<a href="{url}" target="_blank">{tendersiae.siae.brand} ({tendersiae.siae.name})</a>')

siae_with_app_link.short_description = "Structure (lien vers l'app)"
siae_with_app_link.admin_order_field = "siae"

def tender_with_link(self, tendersiae):
url = reverse("admin:tenders_tender_change", args=[tendersiae.tender.slug])
return format_html(f'<a href="{url}">{tendersiae.tender}</a>')

tender_with_link.short_description = "Besoin d'achat (lien vers l'admin)"
tender_with_link.admin_order_field = "tender"

def status(self, tendersiae):
return tendersiae.status

siae_with_link.short_description = "Structure"
siae_with_link.admin_order_field = "siae"
status.short_description = "Status"
status.admin_order_field = "status"

def logs_display(self, tender=None):
if tender:
Expand Down
18 changes: 18 additions & 0 deletions lemarche/tenders/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,21 @@
TENDER_SIAE_SOURCES_EXCEPT_IA = [
source[0] for source in TENDER_SIAE_SOURCE_CHOICES if source[0] != TENDER_SIAE_SOURCE_AI
]

TENDER_SIAE_STATUS_EMAIL_SEND_DATE = "EMAIL_SEND_DATE"
TENDER_SIAE_STATUS_EMAIL_SEND_DATE_DISPLAY = "Contactée"
TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE = "EMAIL_LINK_CLICK_DATE"
TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE_DISPLAY = "Cliquée"
TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE = "DETAIL_DISPLAY_DATE"
TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE_DISPLAY = "Vue"
TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE = "DETAIL_CONTACT_CLICK_DATE"
TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE_DISPLAY = "Intéressée"
TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE = "DETAIL_NOT_INTERESTED_CLICK_DATE"
TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE_DISPLAY = "Pas intéressée"
TENDER_SIAE_STATUS_CHOICES = (
(TENDER_SIAE_STATUS_EMAIL_SEND_DATE, TENDER_SIAE_STATUS_EMAIL_SEND_DATE_DISPLAY),
(TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE, TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE_DISPLAY),
(TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE, TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE_DISPLAY),
(TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE, TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE_DISPLAY),
(TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE, TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE_DISPLAY),
)
94 changes: 67 additions & 27 deletions lemarche/tenders/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -893,19 +893,50 @@ def set_sent(self):
self.save()


class TenderQuestion(models.Model):
text = models.TextField(verbose_name="Intitulé de la question", blank=False)

tender = models.ForeignKey(
"tenders.Tender", verbose_name="Besoin d'achat", related_name="questions", on_delete=models.CASCADE
)

created_at = models.DateTimeField(verbose_name="Date de création", default=timezone.now)
updated_at = models.DateTimeField(verbose_name="Date de modification", auto_now=True)

class Meta:
verbose_name = "Question de l'acheteur"
verbose_name_plural = "Questions de l'acheteur"

def __str__(self):
return self.text


class TenderSiaeQuerySet(models.QuerySet):
def email_click_reminder(self, gte_days_ago, lt_days_ago):
return (
self.filter(email_send_date__gte=gte_days_ago)
.filter(email_send_date__lt=lt_days_ago)
.filter(email_link_click_date__isnull=True)
.filter(detail_display_date__isnull=True)
.filter(detail_contact_click_date__isnull=True)
)
def with_prefetch_related(self):
return self.prefetch_related("tender", "siae")

def detail_contact_click_post_reminder(self, gte_days_ago, lt_days_ago):
return self.filter(detail_contact_click_date__gte=gte_days_ago).filter(
detail_contact_click_date__lt=lt_days_ago
def with_status(self):
return self.annotate(
status_annotated=Case(
When(
detail_not_interested_click_date__isnull=False,
then=Value(tender_constants.TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE),
),
When(
detail_contact_click_date__isnull=False,
then=Value(tender_constants.TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE),
),
When(
detail_display_date__isnull=False,
then=Value(tender_constants.TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE),
),
When(
email_link_click_date__isnull=False,
then=Value(tender_constants.TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE),
),
When(email_send_date__isnull=False, then=Value(tender_constants.TENDER_SIAE_STATUS_EMAIL_SEND_DATE)),
default=None,
)
)

def unread_stats(self, user):
Expand All @@ -924,23 +955,19 @@ def unread_stats(self, user):
.aggregate(**aggregates)
)

def email_click_reminder(self, gte_days_ago, lt_days_ago):
return (
self.filter(email_send_date__gte=gte_days_ago)
.filter(email_send_date__lt=lt_days_ago)
.filter(email_link_click_date__isnull=True)
.filter(detail_display_date__isnull=True)
.filter(detail_contact_click_date__isnull=True)
)

class TenderQuestion(models.Model):
text = models.TextField(verbose_name="Intitulé de la question", blank=False)

tender = models.ForeignKey(
"tenders.Tender", verbose_name="Besoin d'achat", related_name="questions", on_delete=models.CASCADE
)

created_at = models.DateTimeField(verbose_name="Date de création", default=timezone.now)
updated_at = models.DateTimeField(verbose_name="Date de modification", auto_now=True)

class Meta:
verbose_name = "Question de l'acheteur"
verbose_name_plural = "Questions de l'acheteur"

def __str__(self):
return self.text
def detail_contact_click_post_reminder(self, gte_days_ago, lt_days_ago):
return self.filter(detail_contact_click_date__gte=gte_days_ago).filter(
detail_contact_click_date__lt=lt_days_ago
)


class TenderSiae(models.Model):
Expand Down Expand Up @@ -1018,6 +1045,19 @@ class Meta:
verbose_name_plural = "Structures correspondantes au besoin"
ordering = ["-created_at"]

@property
def status(self):
if self.detail_not_interested_click_date:
return tender_constants.TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE_DISPLAY
if self.detail_contact_click_date:
return tender_constants.TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE_DISPLAY
if self.detail_display_date:
return tender_constants.TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE_DISPLAY
if self.email_link_click_date:
return tender_constants.TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE_DISPLAY
if self.email_send_date:
return tender_constants.TENDER_SIAE_STATUS_EMAIL_SEND_DATE_DISPLAY


class PartnerShareTenderQuerySet(models.QuerySet):
def is_active(self):
Expand Down
31 changes: 26 additions & 5 deletions lemarche/tenders/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ def test_tender_partner_is_active(self):
self.assertEqual(len(result_2), 4 + 1)


class TenderSiaeModelQuerysetTest(TestCase):
class TenderSiaeModelAndQuerysetTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.user_siae = UserFactory(kind=User.KIND_SIAE)
Expand All @@ -790,20 +790,20 @@ def setUpTestData(cls):
deadline_date=date_tomorrow,
kind=tender_constants.KIND_TENDER,
)
TenderSiae.objects.create(
cls.tendersiae_1 = TenderSiae.objects.create(
tender=cls.tender_with_siae_1, siae=siae_with_tender_3, email_send_date=date_last_week
)
TenderSiae.objects.create(
cls.tendersiae_2 = TenderSiae.objects.create(
tender=cls.tender_with_siae_1, siae=siae_with_tender_4, email_send_date=date_two_days_ago
)
TenderSiae.objects.create(
cls.tendersiae_3 = TenderSiae.objects.create(
tender=cls.tender_with_siae_1,
siae=siae_with_tender_5,
email_send_date=date_two_days_ago,
email_link_click_date=date_two_days_ago,
detail_contact_click_date=date_two_days_ago,
)
TenderSiae.objects.create(
cls.tendersiae_4 = TenderSiae.objects.create(
tender=cls.tender_with_siae_1,
siae=siae_with_tender_5,
detail_display_date=date_last_week,
Expand All @@ -813,6 +813,17 @@ def setUpTestData(cls):
siaes=[siae_with_tender_2, siae_with_tender_3], deadline_date=date_tomorrow
)

def test_with_status(self):
tendersiae_queryset = TenderSiae.objects.with_status()
self.assertEqual(
tendersiae_queryset.get(id=self.tendersiae_1.id).status_annotated,
tender_constants.TENDER_SIAE_STATUS_EMAIL_SEND_DATE,
)
self.assertEqual(
tendersiae_queryset.get(id=self.tendersiae_4.id).status_annotated,
tender_constants.TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE,
)

def test_email_click_reminder(self):
lt_days_ago = timezone.now() - timedelta(days=2)
gte_days_ago = timezone.now() - timedelta(days=2 + 1)
Expand All @@ -838,6 +849,16 @@ def test_unread_stats(self):
self.assertEqual(stats[f"unread_count_{tender_constants.KIND_QUOTE}_annotated"], 1)
self.assertEqual(stats[f"unread_count_{tender_constants.KIND_PROJECT}_annotated"], 0)

def test_status_property(self):
self.assertEqual(
TenderSiae.objects.get(id=self.tendersiae_1.id).status,
tender_constants.TENDER_SIAE_STATUS_EMAIL_SEND_DATE_DISPLAY,
)
self.assertEqual(
TenderSiae.objects.get(id=self.tendersiae_4.id).status,
tender_constants.TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE_DISPLAY,
)


class TenderAdminTest(TestCase):
def setUp(cls):
Expand Down
Loading