sms: factorize OVH and SMSFactor connectors (#81143)

This commit is contained in:
Corentin Sechet 2023-09-18 17:29:17 +02:00
parent 62e452c31e
commit aed4c44107
10 changed files with 78 additions and 151 deletions

View File

@ -2,25 +2,19 @@ import hashlib
import json
import random
import time
from datetime import timedelta
from urllib.parse import urljoin
import requests
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.core.mail import send_mail
from django.db import models
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from passerelle.base.models import SkipJob
from passerelle.sms.models import SMSResource
from passerelle.sms.models import TrackCreditSMSResource
from passerelle.utils.jsonresponse import APIError
class OVHSMSGateway(SMSResource):
class OVHSMSGateway(TrackCreditSMSResource):
documentation_url = (
'https://doc-publik.entrouvert.com/admin-fonctionnel/les-tutos/configuration-envoi-sms/'
)
@ -83,17 +77,6 @@ class OVHSMSGateway(SMSResource):
),
)
msg_class = models.IntegerField(choices=MESSAGES_CLASSES, default=1, verbose_name=_('Message class'))
credit_threshold_alert = models.PositiveIntegerField(
verbose_name=_('Credit alert threshold'), default=500
)
credit_left = models.PositiveIntegerField(verbose_name=_('Credit left'), default=0, editable=False)
alert_emails = ArrayField(
models.EmailField(blank=True),
blank=True,
null=True,
verbose_name=_('Email addresses list to send credit alerts to, separated by comma'),
)
credit_alert_timestamp = models.DateTimeField(null=True, editable=False)
TEST_DEFAULTS = {
'create_kwargs': {
@ -191,39 +174,15 @@ class OVHSMSGateway(SMSResource):
self.save(update_credit=False)
return credits_removed
def check_status(self):
if self.uses_new_api:
super().check_status()
def update_credit_left(self):
result = self.request('get', endpoint='')
self.credit_left = result['creditsLeft']
self.save(update_credit=False)
def send_credit_alert_if_needed(self):
if self.credit_left >= self.credit_threshold_alert:
return
if self.credit_alert_timestamp and self.credit_alert_timestamp > timezone.now() - timedelta(days=1):
return # alerts are sent daily
ctx = {
'connector': self,
'connector_url': urljoin(settings.SITE_BASE_URL, self.get_absolute_url()),
}
subject = render_to_string('ovh/credit_alert_subject.txt', ctx).strip()
body = render_to_string('ovh/credit_alert_body.txt', ctx)
html_body = render_to_string('ovh/credit_alert_body.html', ctx)
send_mail(
subject,
body,
settings.DEFAULT_FROM_EMAIL,
self.alert_emails,
html_message=html_body,
)
self.credit_alert_timestamp = timezone.now()
self.save()
self.logger.warning('credit is too low, alerts were sent to %s', self.alert_emails)
def check_status(self):
if self.uses_new_api:
self.update_credit_left()
self.send_credit_alert_if_needed()
def save(self, *args, update_credit=True, **kwargs):
super().save(*args, **kwargs)
if update_credit and self.uses_new_api:

View File

@ -1,22 +0,0 @@
{% extends "emails/body_base.html" %}
{% load i18n %}
{% block content %}
<p>{% trans "Hi," %}</p>
<p>
{% blocktrans trimmed with name=connector.title credit_left=connector.credit_left %}
There are only {{ credit_left }} credits left for connector {{ name }}.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed with account=connector.account %}
Please add more credit as soon as possible for OVH account {{ account }}.
{% endblocktrans %}
</p>
{% with _("View connector page") as button_label %}
{% include "emails/button-link.html" with url=connector_url label=button_label %}
{% endwith %}
{% endblock %}

View File

@ -1,17 +0,0 @@
{% extends "emails/body_base.txt" %}
{% load i18n %}
{% block content %}{% autoescape off %}{% trans "Hi," %}
{% blocktrans trimmed with name=connector.title credit_left=connector.credit_left %}
There are only {{ credit_left }} credits left for connector {{ name }}.
{% endblocktrans %}
{% blocktrans trimmed with account=connector.account %}
Please add more credit as soon as possible for OVH account {{ account }}.
{% endblocktrans %}
{% trans "View connector page:" %} {{ connector_url }}
{% endautoescape %}
{% endblock %}

View File

@ -13,36 +13,19 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import logging
import urllib.parse
import requests
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.core.mail import send_mail
from django.db import models
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from passerelle.sms.models import SMSResource
from passerelle.sms.models import TrackCreditSMSResource
from passerelle.utils.jsonresponse import APIError
class SMSFactorSMSGateway(SMSResource):
class SMSFactorSMSGateway(TrackCreditSMSResource):
auth_token = models.CharField(verbose_name=_('Auth Token'), max_length=255)
credit_threshold_alert = models.PositiveIntegerField(
verbose_name=_('Credit alert threshold'), default=500
)
credit_left = models.PositiveIntegerField(verbose_name=_('Credit left'), default=0, editable=False)
alert_emails = ArrayField(
models.EmailField(blank=True),
blank=True,
null=True,
verbose_name=_('Email addresses list to send credit alerts to, separated by comma'),
)
credit_alert_timestamp = models.DateTimeField(null=True, editable=False)
# unecessary field
allow_premium_rate = None
@ -173,36 +156,6 @@ class SMSFactorSMSGateway(SMSResource):
try:
# SMS Factor returns this as a string, for an unknown reason
self.credit_left = int(result['credits'])
self.save(update_fields=['credit_left'])
except KeyError:
self.logger.warning('Cannot retrieve credits for sms-factor connector: %s', result)
else:
self.save(update_fields=['credit_left'])
def send_credit_alert_if_needed(self):
if self.credit_left >= self.credit_threshold_alert:
return
if self.credit_alert_timestamp and self.credit_alert_timestamp > timezone.now() - datetime.timedelta(
days=1
):
return # alerts are sent daily
ctx = {
'connector': self,
'connector_url': urllib.parse.urljoin(settings.SITE_BASE_URL, self.get_absolute_url()),
}
subject = render_to_string('smsfactor/credit_alert_subject.txt', ctx).strip()
body = render_to_string('smsfactor/credit_alert_body.txt', ctx)
html_body = render_to_string('smsfactor/credit_alert_body.html', ctx)
send_mail(
subject,
body,
settings.DEFAULT_FROM_EMAIL,
self.alert_emails,
html_message=html_body,
)
self.credit_alert_timestamp = timezone.now()
self.save()
self.logger.warning('credit is too low, alerts were sent to %s', self.alert_emails)
def check_status(self):
self.update_credit_left()
self.send_credit_alert_if_needed()

View File

@ -1,6 +0,0 @@
{% extends "emails/subject.txt" %}
{% load i18n %}
{% block email-subject %}{% autoescape off %}{% blocktrans trimmed with credit_left=connector.credit_left %}
SMS Factor alert: only {{ credit_left }} credits left
{% endblocktrans %}{% endautoescape %}{% endblock %}

View File

@ -13,13 +13,19 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import logging
import re
import urllib.parse
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.core.mail import send_mail
from django.core.validators import RegexValidator
from django.db import models
from django.template.loader import render_to_string
from django.urls import re_path, reverse
from django.utils import timezone
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy as _
@ -256,3 +262,52 @@ class SMSLog(models.Model):
def __str__(self):
return '%s %s %s' % (self.timestamp, self.appname, self.slug)
class TrackCreditSMSResource(SMSResource):
credit_threshold_alert = models.PositiveIntegerField(
verbose_name=_('Credit alert threshold'), default=500
)
credit_left = models.PositiveIntegerField(verbose_name=_('Credit left'), default=0, editable=False)
alert_emails = ArrayField(
models.EmailField(blank=True),
blank=True,
null=True,
verbose_name=_('Email addresses list to send credit alerts to, separated by comma'),
)
credit_alert_timestamp = models.DateTimeField(null=True, editable=False)
def update_credit_left(self):
raise NotImplementedError()
def send_credit_alert_if_needed(self):
if self.credit_left >= self.credit_threshold_alert:
return
if self.credit_alert_timestamp and self.credit_alert_timestamp > timezone.now() - datetime.timedelta(
days=1
):
return # alerts are sent daily
ctx = {
'connector': self,
'connector_url': urllib.parse.urljoin(settings.SITE_BASE_URL, self.get_absolute_url()),
}
subject = render_to_string('sms/credit_alert_subject.txt', ctx).strip()
body = render_to_string('sms/credit_alert_body.txt', ctx)
html_body = render_to_string('sms/credit_alert_body.html', ctx)
send_mail(
subject,
body,
settings.DEFAULT_FROM_EMAIL,
self.alert_emails,
html_message=html_body,
)
self.credit_alert_timestamp = timezone.now()
self.save()
self.logger.warning('credit is too low, alerts were sent to %s', self.alert_emails)
def check_status(self):
self.update_credit_left()
self.send_credit_alert_if_needed()
class Meta:
abstract = True

View File

@ -12,7 +12,7 @@
<p>
{% blocktrans trimmed with account=connector.account %}
Please add more credit as soon as possible for your SMS Factor account.
Please add more credit as soon as possible for your account.
{% endblocktrans %}
</p>
@ -20,3 +20,4 @@
{% include "emails/button-link.html" with url=connector_url label=button_label %}
{% endwith %}
{% endblock %}

View File

@ -7,11 +7,12 @@
There are only {{ credit_left }} credits left for connector {{ name }}.
{% endblocktrans %}
{% blocktrans trimmed with account=connector.account %}
Please add more credit as soon as possible for your SMS Factor account.
{% blocktrans trimmed %}
Please add more credit as soon as possible for your account.
{% endblocktrans %}
{% trans "View connector page:" %} {{ connector_url }}
{% endautoescape %}
{% endblock %}

View File

@ -2,5 +2,5 @@
{% load i18n %}
{% block email-subject %}{% autoescape off %}{% blocktrans trimmed with credit_left=connector.credit_left %}
OVH SMS alert: only {{ credit_left }} credits left
SMS alert: only {{ credit_left }} credits left
{% endblocktrans %}{% endautoescape %}{% endblock %}

View File

@ -28,14 +28,18 @@ from passerelle.apps.ovh.models import OVHSMSGateway
from passerelle.apps.sfr_dmc.models import SfrDmcGateway
from passerelle.apps.smsfactor.models import SMSFactorSMSGateway
from passerelle.base.models import AccessRight, ApiUser, Job, ResourceLog
from passerelle.sms.models import SMSLog, SMSResource
from passerelle.sms.models import SMSLog, SMSResource, TrackCreditSMSResource
from passerelle.utils.jsonresponse import APIError
from tests.test_manager import login
from tests.utils import FakedResponse
pytestmark = pytest.mark.django_db
klasses = SMSResource.__subclasses__()
klasses = [
klass
for klass in SMSResource.__subclasses__() + TrackCreditSMSResource.__subclasses__()
if not klass._meta.abstract
]
def test_clean_numbers():
@ -526,9 +530,8 @@ def test_ovh_alert_emails(app, freezer, mailoutbox):
mail = mailoutbox[0]
assert mail.recipients() == ['test@entrouvert.org']
assert mail.subject == 'OVH SMS alert: only 99 credits left'
assert mail.subject == 'SMS alert: only 99 credits left'
for body in (mail.body, mail.alternatives[0][0]):
assert connector.account in body
assert connector.title in body
assert 'http://localhost/ovh/test-ovh/' in body
mailoutbox.clear()
@ -810,7 +813,7 @@ def test_sms_factor_alert_emails(app, freezer, mailoutbox):
mail = mailoutbox[0]
assert mail.recipients() == ['test@entrouvert.org']
assert mail.subject == 'SMS Factor alert: only 99 credits left'
assert mail.subject == 'SMS alert: only 99 credits left'
for body in (mail.body, mail.alternatives[0][0]):
assert 'SMS Factor' in body
assert connector.title in body