Skip to content

Commit

Permalink
linting
Browse files Browse the repository at this point in the history
  • Loading branch information
biagiodistefano committed Feb 2, 2024
1 parent 72e08a2 commit 2fa3836
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 48 deletions.
41 changes: 13 additions & 28 deletions src/api/messages.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import json
import logging
from datetime import timedelta
from pathlib import Path

from dateutil.relativedelta import relativedelta
from django.contrib.sites.models import Site
from django.shortcuts import reverse
from twilio.rest import Client as TwilioClient
Expand All @@ -13,40 +11,27 @@
logger = logging.getLogger("twilio_whatsapp")


def load_templates():
template_dir = Path(__file__).parent / "templates" / "api"
templates = {}
delta_dict = {
"Invitation": (relativedelta(months=1, hours=1), None),
"2-week reminder": (relativedelta(weeks=2, hours=1), timedelta(days=1)),
"1-week reminder": (relativedelta(weeks=1, hours=1), timedelta(days=1)),
"Final reminder": (relativedelta(days=2, hours=1), timedelta(days=1)),
}
for file_path in template_dir.glob("*.txt"):
with file_path.open("r") as file:
content = file.read().strip()
# Use the file stem (without extension) as the key and content as the value
file_stem = file_path.stem
templates[file_stem] = (content, *delta_dict.get(file_stem))
return templates


TEMPLATES = load_templates()


def send_whatsapp_message(to: str, body: str) -> MessageInstance | None:
def send_whatsapp_message(
to: str, body: str, content_sid: None | str = None, variables: dict | None = None
) -> MessageInstance | None:
if settings.DEBUG and to not in settings.DEBUG_NUMBERS_ALLOWED:
return None
variables = variables or {}
account_sid = settings.TWILIO_ACCOUNT_SID
auth_token = settings.TWILIO_AUTH_TOKEN
from_whatsapp_number = settings.TWILIO_FROM_WHATSAPP_NUMBER
client = TwilioClient(account_sid, auth_token)
callback_path = reverse("slr-api:twilio_status_callback")
callback_url = (settings.NGROK_URL or f"https://{Site.objects.get_current().domain}") + callback_path
message = client.messages.create(
kwargs = dict(
body=body,
from_=f"whatsapp:{from_whatsapp_number}",
from_=settings.TWILIO_SENDER_SID,
content_variables=json.dumps(variables),
to=f"whatsapp:{to}",
status_callback=callback_url,
)
if content_sid is not None:
kwargs["content_sid"] = content_sid
message = client.messages.create(
**kwargs,
)
return message
19 changes: 19 additions & 0 deletions src/api/migrations/0040_message_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.7 on 2024-02-02 12:59

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('api', '0039_alter_messagetemplate_language_and_more'),
]

operations = [
migrations.AddField(
model_name='message',
name='template',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.messagetemplate'),
),
]
15 changes: 8 additions & 7 deletions src/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,14 +403,14 @@ def to_twilio_json(self) -> dict:
},
}

def cleaned_text(self) -> str:
def cleaned_text(self, variables: dict | None = None) -> str:
text = self.text
placeholder_counter = 1
keyword_occurrences = {}

while True:
found = False
for keyword in self.variables.keys():
for keyword in (variables or self.variables).keys():
if f"{{{keyword}}}" in text:
found = True
text = text.replace(f"{{{keyword}}}", f"{{{{{placeholder_counter}}}}}", 1)
Expand All @@ -421,19 +421,19 @@ def cleaned_text(self) -> str:

return text

def cleaned_variables(self) -> dict[str, str]:
keyword_occurrences = self._generate_keyword_occurrences()
new_variables = {str(k): self.variables[v] for k, v in keyword_occurrences.items()}
def cleaned_variables(self, variables: dict | None = None) -> dict[str, str]:
keyword_occurrences = self._generate_keyword_occurrences(variables)
new_variables = {str(k): (variables or self.variables)[v] for k, v in keyword_occurrences.items()}
return new_variables

def _generate_keyword_occurrences(self) -> dict[int, str]:
def _generate_keyword_occurrences(self, variables: dict | None = None) -> dict[int, str]:
text = self.text # Assuming self.body contains the template text
placeholder_counter = 1
keyword_occurrences = {}

while True:
found = False
for keyword in self.variables.keys():
for keyword in (variables or self.variables).keys():
if f"{{{keyword}}}" in text:
found = True
text = text.replace(f"{{{keyword}}}", "", 1)
Expand Down Expand Up @@ -472,6 +472,7 @@ def update_status(self) -> requests.Response:

class Message(MessageBase):
party = models.ForeignKey(Party, on_delete=models.CASCADE)
template = models.ForeignKey(MessageTemplate, on_delete=models.SET_NULL, null=True, blank=True)
text = MarkdownField(rendered_field="text_rendered", validator=VALIDATOR_STANDARD, default="", blank=True)
text_rendered = RenderedMarkdownField()
due_at = models.DateTimeField(db_index=True, null=True, blank=True)
Expand Down
1 change: 1 addition & 0 deletions src/api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
TWILIO_ACCOUNT_SID = getattr(settings, "TWILIO_ACCOUNT_SID", None)
TWILIO_AUTH_TOKEN = getattr(settings, "TWILIO_AUTH_TOKEN", None)
TWILIO_FROM_WHATSAPP_NUMBER = getattr(settings, "TWILIO_FROM_WHATSAPP_NUMBER", None)
TWILIO_SENDER_SID = getattr(settings, "TWILIO_SENDER_SID", None)
DEBUG_NUMBERS_ALLOWED = getattr(settings, "DEBUG_NUMBERS_ALLOWED", [])
NGROK_URL = getattr(settings, "NGROK_URL", None)
MY_PHONE_NUMBER = getattr(settings, "MY_PHONE_NUMBER", None)
24 changes: 14 additions & 10 deletions src/api/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,20 @@ def create_invite(sender, instance: models.Party, created: bool, **kwargs):
@receiver(post_save, sender=models.Party)
def create_party_default_messages(sender, instance: models.Party, created: bool, **kwargs):
for message_template in models.MessageTemplate.objects.filter(is_default_party_message=True):
msg_instance, created = models.Message.objects.get_or_create(party=instance, title=message_template.title)
if not created:
continue
msg_instance.text = message_template.text
if message_template.send_delta is not None:
msg_instance.due_at = instance.date_and_time - message_template.send_delta
msg_instance.send_threshold = message_template.send_threshold
msg_instance.draft = message_template.draft
msg_instance.autosend = message_template.autosend
msg_instance.save()
models.Message.objects.get_or_create(party=instance, template=message_template)


@receiver(post_save, sender=models.Message)
def autofill_message_from_template(sender, instance: models.Message, created: bool, **kwargs):
if created and instance.template:
instance.title = instance.template.title
instance.text = instance.template.text
instance.send_threshold = instance.template.send_threshold
instance.draft = instance.template.draft
instance.autosend = instance.template.autosend
if instance.template.send_delta is not None:
instance.due_at = instance.party.date_and_time - instance.template.send_delta
instance.save()


@receiver(post_save, sender=models.Person)
Expand Down
14 changes: 11 additions & 3 deletions src/api/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def send_due_messages(
models.Message.objects.filter(party=party, due_at__lte=current_time, draft=False, autosend=True)
.exclude(Q(send_threshold__isnull=False) & Q(due_at__lt=current_time - F("send_threshold")))
.order_by("due_at")
.prefetch_related("template")
)
if filter_messages:
messages = messages.filter(pk__in=[msg.pk for msg in filter_messages])
Expand All @@ -52,9 +53,14 @@ def send_due_messages(
print(f"[DRY] {log_msg}")
continue
try:
variables = dict(name=person.first_name, party=str(party), url=personal_url)
twilio_message = send_whatsapp_message(
to=person.phone_number,
body=message.text.format(name=person.first_name, party=str(party), url=personal_url),
body=message.template.cleaned_text(variables=variables)
if message.template
else message.text.format(**variables), # noqa: E501
content_sid=message.template.sid if message.template else None,
variables=message.template.cleaned_variables(variables) if message.template else None,
)
except Exception as e:
logger.error(f"Error sending message to {person}: {e}")
Expand Down Expand Up @@ -92,8 +98,10 @@ def _get_recipients(


@shared_task
def send_whatsapp_message(to: str, body: str) -> MessageInstance | None:
return _send_whatsapp_message(to, body)
def send_whatsapp_message(
to: str, body: str, content_sid: None | str = None, variables: dict | None = None
) -> MessageInstance | None:
return _send_whatsapp_message(to, body, content_sid, variables)


@shared_task
Expand Down
1 change: 1 addition & 0 deletions src/sinsloveandrainbows/settings/twilio.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
TWILIO_ACCOUNT_SID = config("TWILIO_ACCOUNT_SID", default=None)
TWILIO_AUTH_TOKEN = config("TWILIO_AUTH_TOKEN", default=None)
TWILIO_FROM_WHATSAPP_NUMBER = config("TWILIO_FROM_WHATSAPP_NUMBER", default=None)
TWILIO_SENDER_SID = config("TWILIO_SENDER_SID", default=None)

DEBUG_NUMBERS_ALLOWED = config("DEBUG_NUMBERS_ALLOWED", cast=lambda v: [s.strip() for s in v.split(",")], default="")
MY_PHONE_NUMBER = config("MY_PHONE_NUMBER", default=None)
Expand Down

0 comments on commit 2fa3836

Please sign in to comment.