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

[Dépôt de besoin] Envoyer en asynchrone les besoins validés #998

Merged
merged 4 commits into from
Dec 6, 2023
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
5 changes: 3 additions & 2 deletions clevercloud/cron.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
"30 8 * * * $ROOT/clevercloud/tenders_send_author_transactioned_question_emails.sh",
"0 9 * * * $ROOT/clevercloud/tenders_send_siae_contacted_reminder_emails.sh",
"10 9 * * * $ROOT/clevercloud/tenders_send_siae_interested_reminder_emails.sh",
"20 9 * * * $ROOT/clevercloud/tenders_send_author_incremental.sh"
]
"20 9 * * * $ROOT/clevercloud/tenders_send_author_incremental.sh",
"*/5 9-17 * * 1-5 $ROOT/clevercloud/tenders_send_validated.sh"
]
22 changes: 22 additions & 0 deletions clevercloud/tenders_send_validated.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash -l

# Send Tenders who are validated but not sent

# Do not run if this env var is not set:
if [[ -z "$CRON_TENDER_SEND_VALIDATED_ENABLED" ]]; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J'adore cette possibilité de débrayer !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seul hic, c'est qu'il n'y a pas d'option intermédiaire (désactiver le cron mais permettre l'envoi manuel 🤔 )

echo "CRON_TENDER_SEND_VALIDATED_ENABLED not set. Exiting..."
exit 0
fi

# About clever cloud cronjobs:
# https://www.clever-cloud.com/doc/tools/crons/

if [[ "$INSTANCE_NUMBER" != "0" ]]; then
echo "Instance number is ${INSTANCE_NUMBER}. Stop here."
exit 0
fi

# $APP_HOME is set by default by clever cloud.
cd $APP_HOME

django-admin send_validated_tenders
7 changes: 4 additions & 3 deletions lemarche/templates/tenders/admin_change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@
{% if original.validated_at %}
<i>Validé le {{ original.validated_at }}.&nbsp;</i>
{% if original.sent_at %}
<i>Envoyé le {{ original.sent_at }}.</i><br>
<button type="submit" name="_restart_tender" value="1" class="button">Renvoyer aux structures</button>
<i>Envoyé le {{ original.sent_at }}.</i>
<button type="submit" class="button" name="_restart_tender" value="1">Renvoyer aux structures</button>
{% endif %}
{% else %}
<button type="submit" name="_validate_tender" value="1" class="button">Valider (sauvegarder) et envoyer aux structures</button>
<button type="submit" class="button" name="_validate_tender" value="1">Valider (sauvegarder) et envoyer aux structures</button>
<i>L'envoi des besoins 'validés' se fait toutes les 5 minutes, du Lundi au Vendredi, entre 9h et 17h</i>
{% endif %}
</div>
{% endif %}
Expand Down
40 changes: 4 additions & 36 deletions lemarche/tenders/admin.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from ckeditor.widgets import CKEditorWidget
from django import forms
from django.conf import settings
from django.contrib import admin
from django.contrib.contenttypes.admin import GenericTabularInline
from django.db import models
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils import timezone
from django.utils.html import format_html
from django_admin_filters import MultiChoice
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
Expand All @@ -18,13 +16,8 @@
from lemarche.tenders.forms import TenderAdminForm
from lemarche.tenders.models import PartnerShareTender, Tender, TenderQuestion, TenderStepsData
from lemarche.utils.admin.admin_site import admin_site
from lemarche.utils.apis import api_hubspot
from lemarche.utils.fields import ChoiceArrayField, pretty_print_readonly_jsonfield
from lemarche.www.tenders.tasks import (
send_confirmation_published_email_to_author,
send_tender_emails_to_partners,
send_tender_emails_to_siaes,
)
from lemarche.www.tenders.tasks import restart_send_tender_task


class KindFilter(MultiChoice):
Expand Down Expand Up @@ -95,29 +88,6 @@ class TenderQuestionInline(admin.TabularInline):
extra = 0


def validate_and_send_tender_task(tender: Tender):
# 1) validate the tender
tender.set_validated_and_sent(with_save=True)
# 2) find the matching Siaes? done in Tender post_save signal
send_confirmation_published_email_to_author(tender, nb_matched_siaes=tender.siaes.count())
# 3) send the tender to all matching Siaes & Partners
send_tender_emails_to_siaes(tender)
send_tender_emails_to_partners(tender)


def restart_send_tender_task(tender: Tender):
# 1) log the tender send restart
log_item = {
"action": "restart_send",
"date": timezone.now().isoformat(),
}
tender.logs.append(log_item)
tender.save()
# 2) send the tender to all matching Siaes & Partners
send_tender_emails_to_siaes(tender)
send_tender_emails_to_partners(tender)


@admin.register(Tender, site=admin_site)
class TenderAdmin(FieldsetsInlineMixin, admin.ModelAdmin):
list_display = [
Expand Down Expand Up @@ -425,7 +395,7 @@ def siae_count_annotated_with_link(self, tender):
url = reverse("admin:siaes_siae_changelist") + f"?tenders__in={tender.id}"
return format_html(f'<a href="{url}">{getattr(tender, "siae_count_annotated", 0)}</a>')

siae_count_annotated_with_link.short_description = "Structures concernées"
siae_count_annotated_with_link.short_description = "S. concernées"
siae_count_annotated_with_link.admin_order_field = "siae_count_annotated"

def siae_email_send_count_annotated_with_link(self, tender):
Expand Down Expand Up @@ -494,10 +464,8 @@ def response_change(self, request, obj: Tender):
Catch submit of custom admin button to Validate or Resend Tender
"""
if request.POST.get("_validate_tender"):
validate_and_send_tender_task(tender=obj)
self.message_user(request, "Ce dépôt de besoin a été validé et envoyé aux structures")
if settings.BITOUBI_ENV == "prod":
api_hubspot.create_deal_from_tender(tender=obj)
obj.set_validated()
self.message_user(request, "Ce dépôt de besoin a été validé. Il sera envoyé en temps voulu :)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄

return HttpResponseRedirect(".")
elif request.POST.get("_restart_tender"):
restart_send_tender_task(tender=obj)
Expand Down
23 changes: 23 additions & 0 deletions lemarche/tenders/management/commands/send_validated_tenders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from django.core.management.base import BaseCommand

from lemarche.tenders.models import Tender
from lemarche.www.tenders.tasks import send_validated_tender


class Command(BaseCommand):
"""
Command to send validated tenders

Note: run via a CRON
"*/5 9-17 * * 1-5" = Every 5 minutes from 9am through 5pm, Monday through Friday
https://cron.help/#*/5_9-17_*_*_1-5

Usage: python manage.py send_validated_tenders
"""

def handle(self, *args, **options):
validated_tenders_to_send = Tender.objects.validated_but_not_sent()
if validated_tenders_to_send.count():
self.stdout.write(f"Found {validated_tenders_to_send.count()} validated tender(s) to send")
for tender in validated_tenders_to_send:
send_validated_tender(tender)
20 changes: 13 additions & 7 deletions lemarche/tenders/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def by_user(self, user):
def validated(self):
return self.filter(validated_at__isnull=False)

def validated_but_not_sent(self):
return self.filter(validated_at__isnull=False).filter(sent_at__isnull=True)

def sent(self):
return self.filter(sent_at__isnull=False)

Expand Down Expand Up @@ -644,36 +647,39 @@ def is_pending_validation(self) -> bool:

@property
def is_validated(self) -> bool:
return self.validated_at and self.status == tender_constants.STATUS_VALIDATED
return bool(self.validated_at) and self.status == tender_constants.STATUS_VALIDATED

@property
def is_pending_validation_or_validated(self) -> bool:
return self.is_pending_validation or self.is_validated

@property
def is_sent(self) -> bool:
return self.sent_at and self.status == tender_constants.STATUS_SENT
return bool(self.sent_at) and self.status == tender_constants.STATUS_SENT

@property
def is_validated_or_sent(self) -> bool:
return self.is_validated or self.is_sent

def set_validated_and_sent(self, with_save=True):
def set_validated(self):
self.validated_at = timezone.now()
self.sent_at = timezone.now()
self.status = tender_constants.STATUS_SENT
self.status = tender_constants.STATUS_VALIDATED
log_item = {
"action": "validate",
"date": self.validated_at.isoformat(),
}
self.logs.append(log_item)
self.save()

def set_sent(self):
self.sent_at = timezone.now()
self.status = tender_constants.STATUS_SENT
log_item = {
"action": "send",
"date": self.sent_at.isoformat(),
}
self.logs.append(log_item)
if with_save:
self.save()
self.save()


class TenderSiaeQuerySet(models.QuerySet):
Expand Down
28 changes: 27 additions & 1 deletion lemarche/www/tenders/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,38 @@

from lemarche.siaes.models import Siae
from lemarche.tenders.models import PartnerShareTender, Tender, TenderSiae
from lemarche.utils.apis import api_mailjet, api_slack
from lemarche.utils.apis import api_hubspot, api_mailjet, api_slack
from lemarche.utils.data import date_to_string
from lemarche.utils.emails import send_mail_async, whitelist_recipient_list
from lemarche.utils.urls import get_admin_url_object, get_domain_url, get_share_url_object


def send_validated_tender(tender: Tender):
# find the matching Siaes? done in Tender post_save signal
# notify author
send_confirmation_published_email_to_author(tender, nb_matched_siaes=tender.siaes.count())
# send the tender to all matching Siaes & Partners
send_tender_emails_to_siaes(tender)
send_tender_emails_to_partners(tender)
# log
tender.set_sent()
if settings.BITOUBI_ENV == "prod":
api_hubspot.create_deal_from_tender(tender=tender)


def restart_send_tender_task(tender: Tender):
# send the tender to all matching Siaes & Partners
send_tender_emails_to_siaes(tender)
send_tender_emails_to_partners(tender)
# log
log_item = {
"action": "restart_send",
"date": timezone.now().isoformat(),
}
tender.logs.append(log_item)
tender.save()


# @task()
def send_tender_emails_to_siaes(tender: Tender):
"""
Expand Down