Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

hotfix beneficiary email #301

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f462476
Merge branch 'main' into orientation-query-expiration
ikarius May 15, 2024
32e73a6
orientation: ajout d'un endpoint pour rafraîchir la date d'expiration…
ikarius May 9, 2024
59a6862
e-mails: séparation de l'envoi des différents e-mails pour l'orientation
ikarius May 15, 2024
693aa53
modèle: ajout de la vérification de la validité du lien par hash
ikarius May 15, 2024
e248f4a
e-mails: utilisation du lien "magique" uniquement dans l'e-mail à la …
ikarius May 15, 2024
79ae269
typo: contact référent
ikarius May 20, 2024
1613843
Merge branch 'main' into orientation-query-expiration
ikarius May 21, 2024
6666626
revue: utilisation du hash complet
ikarius May 20, 2024
6b3dc98
log: ajout d'un log lors du rafraîchissement du lien de demande d'ori…
ikarius May 21, 2024
b9cdea5
fix: lien magique pour la structure
ikarius May 21, 2024
93cd247
rework: simplification de l'accés à la demande d'orientation avec hash
ikarius May 22, 2024
e50c106
Merge branch 'main' into orientation-query-expiration
ikarius May 22, 2024
cd0d482
fix: modification du hash
ikarius May 24, 2024
8f03731
permissions: restriction des accès aux point d'entrée de l'orientation
ikarius May 24, 2024
46100fa
tests: consolidation des tests suite aux changements de permissions
ikarius May 24, 2024
28a6f29
fix: cc sur l'envoi d'e-mail au bénéficiaire
ikarius May 27, 2024
4101afc
revue: simplification sur l'envoi d'e-mail au bénéficiaire
ikarius May 28, 2024
0b06a39
fix: l'e-mail envoyé au bénéficiaire lors de la création d'une orient…
ikarius May 29, 2024
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
2 changes: 1 addition & 1 deletion dora/orientations/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class OrientationAdmin(admin.ModelAdmin):
)
date_hierarchy = "creation_date"
ordering = ("-id",)
readonly_fields = ("query_id", "original_service_name")
readonly_fields = ("query_id", "query_expires_at", "original_service_name")
filter_horizontal = ("rejection_reasons",)
inlines = [SentContactEmailInline]

Expand Down
54 changes: 48 additions & 6 deletions dora/orientations/emails.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def beneficiaries_has_alternate_contact_methods(orientation):
)


def send_orientation_created_emails(orientation):
context = {
def _orientation_created_ctx(orientation) -> dict:
return {
"data": orientation,
"ContactPreference": ContactPreference,
"support_email": settings.SUPPORT_EMAIL,
Expand All @@ -40,7 +40,18 @@ def send_orientation_created_emails(orientation):
for a in orientation.beneficiary_attachments
],
}
# Structure porteuse


# e-mails envoyés lors de la création de l'orientation :
# pour les liens expirés pour la structure,
# il y a un besoin de renvoyer un e-mail du groupe séparément, d'où la séparation
# ça permettra de tester unitairement les e-mails par ailleurs...


def send_orientation_created_to_structure(orientation, context=None):
if not context:
context = _orientation_created_ctx(orientation)

send_mail(
f"{'[Envoyée - Structure porteuse] ' if debug else ''}Nouvelle demande d’orientation reçue",
orientation.get_contact_email(),
Expand All @@ -52,15 +63,25 @@ def send_orientation_created_emails(orientation):
tags=["orientation"],
reply_to=[orientation.prescriber.email],
)
# Prescripteur


def send_orientation_created_to_prescriber(orientation, context=None):
if not context:
context = _orientation_created_ctx(orientation)

send_mail(
f"{'[Envoyée - Prescripteur] ' if debug else ''}Votre demande a bien été transmise !",
orientation.prescriber.email,
mjml2html(render_to_string("orientation-created-prescriber.mjml", context)),
tags=["orientation"],
reply_to=[orientation.get_contact_email()],
)
# Référent


def send_orientation_created_to_referent(orientation, context=None):
if not context:
context = _orientation_created_ctx(orientation)

if (
orientation.referent_email
and orientation.referent_email != orientation.prescriber.email
Expand All @@ -72,7 +93,12 @@ def send_orientation_created_emails(orientation):
tags=["orientation"],
reply_to=[orientation.prescriber.email],
)
# Bénéficiaire


def send_orientation_created_to_beneficiary(orientation, context=None):
if not context:
context = _orientation_created_ctx(orientation)

if orientation.beneficiary_email:
send_mail(
f"{'[Envoyée - Bénéficiaire] ' if debug else ''}Une orientation a été effectuée en votre nom",
Expand All @@ -89,6 +115,22 @@ def send_orientation_created_emails(orientation):
)


def send_orientation_created_emails(orientation, cc=None):
context = _orientation_created_ctx(orientation)

# Structure porteuse
send_orientation_created_to_structure(orientation, context)

# Prescripteur
send_orientation_created_to_prescriber(orientation, context)

# Référent
send_orientation_created_to_referent(orientation, context)

# Bénéficiaire
send_orientation_created_to_beneficiary(orientation, context)


def send_orientation_accepted_emails(
orientation, prescriber_message, beneficiary_message
):
Expand Down
4 changes: 2 additions & 2 deletions dora/orientations/migrations/0006_sentcontactemail.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Migration(migrations.Migration):
choices=[
("BÉNÉFICIAIRE", "Bénéficiaire"),
("PRESCRIPTEUR", "Prescripteur"),
("RÉFÉRENT", "Réfeérent"),
("RÉFÉRENT", "Référent"),
],
max_length=20,
),
Expand All @@ -42,7 +42,7 @@ class Migration(migrations.Migration):
choices=[
("BÉNÉFICIAIRE", "Bénéficiaire"),
("PRESCRIPTEUR", "Prescripteur"),
("RÉFÉRENT", "Réfeérent"),
("RÉFÉRENT", "Référent"),
],
max_length=20,
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 4.2.12 on 2024-05-08 14:59

from django.db import migrations, models

import dora.orientations.models


class Migration(migrations.Migration):
dependencies = [
("orientations", "0008_orientation_di_contact_email_and_more"),
]

operations = [
migrations.AddField(
model_name="orientation",
name="query_expires_at",
field=models.DateTimeField(
default=dora.orientations.models._orientation_query_expiration_date,
verbose_name="expiration du lien de la demande",
),
),
migrations.AlterField(
model_name="orientation",
name="creation_date",
field=models.DateTimeField(
auto_now_add=True, verbose_name="date de création"
),
),
migrations.AlterField(
model_name="orientation",
name="processing_date",
field=models.DateTimeField(
blank=True, null=True, verbose_name="date de traitement"
),
),
migrations.AlterField(
model_name="orientation",
name="status",
field=models.CharField(
choices=[
("OUVERTE", "Ouverte / En cours de traitement"),
("VALIDÉE", "Validée"),
("REFUSÉE", "Refusée"),
],
default="OUVERTE",
max_length=10,
verbose_name="statut",
),
),
]
57 changes: 53 additions & 4 deletions dora/orientations/models.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import hashlib
import logging
import uuid

from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils import timezone

from dora.core.models import EnumModel
from dora.services.models import Service
from dora.structures.models import Structure

ORIENTATION_QUERY_LINK_TTL_DAY = 8

logger = logging.getLogger("dora.logs.core")


class ContactPreference(models.TextChoices):
PHONE = "TELEPHONE", "Téléphone"
Expand All @@ -27,6 +35,11 @@ class Meta:
verbose_name_plural = "Motifs de refus"


def _orientation_query_expiration_date():
# lu quelque part: les lambdas sont moyennement appréciées dans les migrations
return timezone.now() + relativedelta(days=ORIENTATION_QUERY_LINK_TTL_DAY)


class Orientation(models.Model):
id = models.BigAutoField(
auto_created=True,
Expand All @@ -37,6 +50,11 @@ class Orientation(models.Model):

query_id = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)

query_expires_at = models.DateTimeField(
default=_orientation_query_expiration_date,
verbose_name="expiration du lien de la demande",
)

# Infos bénéficiaires
requirements = ArrayField(
models.CharField(max_length=480),
Expand Down Expand Up @@ -140,12 +158,17 @@ class Orientation(models.Model):
RejectionReason, verbose_name="Motifs de refus de l'orientation", blank=True
)

creation_date = models.DateTimeField(auto_now_add=True, editable=False)
processing_date = models.DateTimeField(blank=True, null=True)
creation_date = models.DateTimeField(
auto_now_add=True, editable=False, verbose_name="date de création"
)
processing_date = models.DateTimeField(
blank=True, null=True, verbose_name="date de traitement"
)
status = models.CharField(
max_length=10,
choices=OrientationStatus.choices,
default=OrientationStatus.PENDING,
verbose_name="statut",
)
last_reminder_email_sent = models.DateTimeField(blank=True, null=True)

Expand All @@ -157,8 +180,15 @@ def save(self, *args, **kwargs):
def __str__(self):
return f"Orientation #{self.id}"

def get_query_id_hash(self) -> str:
return hashlib.sha256(
f"{self.query_id}{self.query_expires_at}{settings.SECRET_KEY}".encode(
"utf8"
)
).hexdigest()

def get_magic_link(self):
return self.get_frontend_url()
return f"{settings.FRONTEND_URL}/orientations?token={self.query_id}&h={self.get_query_id_hash()}"

def get_absolute_url(self):
return self.get_frontend_url()
Expand Down Expand Up @@ -241,11 +271,30 @@ def get_service_frontend_url(self):
else:
return ""

def refresh_query_expiration_date(self):
# on ne régénère le lien que si il est expiré
if self.query_expired:
self.query_expires_at = _orientation_query_expiration_date()
self.save()
logger.info(
"orientation:refresh_link",
{
"legal": True,
"reason": "lien expiré",
"queryId": str(self.query_id),
"ttlInDays": ORIENTATION_QUERY_LINK_TTL_DAY,
},
)

@property
def query_expired(self) -> bool:
return timezone.now() > self.query_expires_at


class ContactRecipient(models.TextChoices):
BENEFICIARY = "BÉNÉFICIAIRE", "Bénéficiaire"
PRESCRIBER = "PRESCRIPTEUR", "Prescripteur"
REFERENT = "RÉFÉRENT", "Réfeérent"
REFERENT = "RÉFÉRENT", "Référent"


class SentContactEmail(models.Model):
Expand Down
13 changes: 13 additions & 0 deletions dora/orientations/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import pytest
from model_bakery.random_gen import gen_email

from dora.core.test_utils import make_orientation

from ..models import Orientation


@pytest.fixture
def orientation() -> Orientation:
# l'e-mail bénéficiaire est optionel dans la génération,
# mais on veut vérifier l'envoi d'e-mail vers tous les destinataires
return make_orientation(beneficiary_email=gen_email)
Loading