Projet

Général

Profil

0001-ovh-send-email-credit-alerts-42921.patch

Valentin Deniaud, 12 octobre 2020 16:29

Télécharger (13,6 ko)

Voir les différences:

Subject: [PATCH] ovh: send email credit alerts (#42921)

 .../ovh/migrations/0011_auto_20201008_1731.py | 31 ++++++++
 passerelle/apps/ovh/models.py                 | 53 ++++++++++---
 .../ovh/templates/ovh/credit_alert_body.html  | 22 ++++++
 .../ovh/templates/ovh/credit_alert_body.txt   | 17 ++++
 .../templates/ovh/credit_alert_subject.txt    |  6 ++
 passerelle/settings.py                        |  2 +
 passerelle/templates/emails/body_base.html    | 15 ++++
 passerelle/templates/emails/body_base.txt     |  1 +
 passerelle/templates/emails/button-link.html  |  1 +
 passerelle/templates/emails/subject.txt       |  1 +
 tests/test_sms.py                             | 79 +++++++++++++++++++
 11 files changed, 217 insertions(+), 11 deletions(-)
 create mode 100644 passerelle/apps/ovh/migrations/0011_auto_20201008_1731.py
 create mode 100644 passerelle/apps/ovh/templates/ovh/credit_alert_body.html
 create mode 100644 passerelle/apps/ovh/templates/ovh/credit_alert_body.txt
 create mode 100644 passerelle/apps/ovh/templates/ovh/credit_alert_subject.txt
 create mode 100644 passerelle/templates/emails/body_base.html
 create mode 100644 passerelle/templates/emails/body_base.txt
 create mode 100644 passerelle/templates/emails/button-link.html
 create mode 100644 passerelle/templates/emails/subject.txt
passerelle/apps/ovh/migrations/0011_auto_20201008_1731.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-10-08 15:31
3
from __future__ import unicode_literals
4

  
5
import django.contrib.postgres.fields
6
from django.db import migrations, models
7

  
8

  
9
class Migration(migrations.Migration):
10

  
11
    dependencies = [
12
        ('ovh', '0010_auto_20201008_1126'),
13
    ]
14

  
15
    operations = [
16
        migrations.AddField(
17
            model_name='ovhsmsgateway',
18
            name='alert_emails',
19
            field=django.contrib.postgres.fields.ArrayField(base_field=models.EmailField(blank=True, max_length=254), blank=True, null=True, size=None, verbose_name='Email addresses to send credit alerts to'),
20
        ),
21
        migrations.AddField(
22
            model_name='ovhsmsgateway',
23
            name='credit_alert_timestamp',
24
            field=models.DateTimeField(null=True),
25
        ),
26
        migrations.AlterField(
27
            model_name='ovhsmsgateway',
28
            name='credit_threshold_alert',
29
            field=models.PositiveIntegerField(default=500, verbose_name='Credit alert threshold'),
30
        ),
31
    ]
passerelle/apps/ovh/models.py
2 2
import json
3 3
import requests
4 4
import time
5
from datetime import timedelta
5 6
from urllib.parse import urljoin
6 7

  
8
from django.contrib.postgres.fields import ArrayField
9
from django.conf import settings
10
from django.core.mail import send_mail
7 11
from django.db import models
12
from django.template.loader import render_to_string
13
from django.utils import timezone
8 14
from django.utils.encoding import force_text
9 15
from django.utils.translation import ugettext_lazy as _
10 16

  
......
69 75
    msg_class = models.IntegerField(choices=MESSAGES_CLASSES, default=1,
70 76
                                    verbose_name=_('Message class'))
71 77
    credit_threshold_alert = models.PositiveIntegerField(verbose_name=_('Credit alert threshold'),
72
                                                         default=100)
78
                                                         default=500)
73 79
    credit_left = models.PositiveIntegerField(verbose_name=_('Credit left'), default=0, editable=False)
80
    alert_emails = ArrayField(
81
        models.EmailField(blank=True),
82
        blank=True,
83
        null=True,
84
        verbose_name=_('Email addresses to send credit alerts to'),
85
    )
86
    credit_alert_timestamp = models.DateTimeField(null=True)
74 87

  
75 88
    TEST_DEFAULTS = {
76 89
        'create_kwargs': {
......
174 187
        if self.credit_left < 0:
175 188
            self.credit_left = 0
176 189
        self.save(update_credit=False)
177
        if self.credit_left < self.credit_threshold_alert:
178
            ret['warning'] = 'credit level too low for %s: %s (threshold %s)' % (
179
                self.slug,
180
                self.credit_left,
181
                self.credit_threshold_alert,
182
            )
190
        self.send_credit_alert_if_needed()
183 191
        ret['credit_left'] = self.credit_left
184 192
        ret['ovh_result'] = result
185 193
        ret['sms_ids'] = result.get('ids', [])
......
187 195
        return ret
188 196

  
189 197
    def update_credit_left(self):
190
        if not self.uses_new_api:
191
            return
192 198
        result = self.request('get', endpoint='')
193 199
        self.credit_left = result['creditsLeft']
194 200
        self.save(update_credit=False)
195 201

  
202
    def send_credit_alert_if_needed(self):
203
        if self.credit_left >= self.credit_threshold_alert:
204
            return
205
        if self.credit_alert_timestamp and self.credit_alert_timestamp > timezone.now() - timedelta(days=1):
206
            return  # alerts are sent daily
207
        ctx = {
208
            'connector': self,
209
            'connector_url': urljoin(settings.SITE_BASE_URL, self.get_absolute_url()),
210
        }
211
        subject = render_to_string('ovh/credit_alert_subject.txt', ctx).strip()
212
        body = render_to_string('ovh/credit_alert_body.txt', ctx)
213
        html_body = render_to_string('ovh/credit_alert_body.html', ctx)
214
        send_mail(
215
            _('OVH SMS alert: only %s credits left') % self.credit_left,
216
            body,
217
            settings.DEFAULT_FROM_EMAIL,
218
            self.alert_emails,
219
            html_message=html_body,
220
        )
221
        self.credit_alert_timestamp = timezone.now()
222
        self.save()
223
        self.logger.warning('credit is too low, alerts were sent to %s', self.alert_emails)
224

  
196 225
    def hourly(self):
197 226
        super().hourly()
198
        self.update_credit_left()
227
        if self.uses_new_api:
228
            self.update_credit_left()
229
            self.send_credit_alert_if_needed()
199 230

  
200 231
    def save(self, *args, update_credit=True, **kwargs):
201 232
        super().save(*args, **kwargs)
202
        if update_credit:
233
        if update_credit and self.uses_new_api:
203 234
            self.add_job('update_credit_left')
204 235

  
205 236
    def send_msg_legacy(self, text, sender, destinations, **kwargs):
passerelle/apps/ovh/templates/ovh/credit_alert_body.html
1
{% extends "emails/body_base.html" %}
2
{% load i18n %}
3

  
4
{% block content %}
5
<p>{% trans "Hi," %}</p>
6

  
7
<p>
8
{% blocktrans trimmed with name=connector.title credit_left=connector.credit_left %}
9
There are only {{ credit_left }} credits left for connector {{ name }}.
10
{% endblocktrans %}
11
</p>
12

  
13
<p>
14
{% blocktrans trimmed with account=connector.account %}
15
Please add more credit as soon as possible for OVH account {{ account }}.
16
{% endblocktrans %}
17
</p>
18

  
19
{% with _("View connector page") as button_label %}
20
{% include "emails/button-link.html" with url=connector_url label=button_label %}
21
{% endwith %}
22
{% endblock %}
passerelle/apps/ovh/templates/ovh/credit_alert_body.txt
1
{% extends "emails/body_base.txt" %}
2
{% load i18n %}
3

  
4
{% block content %}{% autoescape off %}{% trans "Hi," %}
5

  
6
{% blocktrans trimmed with name=connector.title credit_left=connector.credit_left %}
7
There are only {{ credit_left }} credits left for connector {{ name }}.
8
{% endblocktrans %}
9

  
10
{% blocktrans trimmed with account=connector.account %}
11
Please add more credit as soon as possible for OVH account {{ account }}.
12
{% endblocktrans %}
13

  
14
{% trans "View connector page:" %} {{ connector_url }}
15

  
16
{% endautoescape %}
17
{% endblock %}
passerelle/apps/ovh/templates/ovh/credit_alert_subject.txt
1
{% extends "emails/subject.txt" %}
2
{% load i18n %}
3

  
4
{% block email-subject %}{% autoescape off %}{% blocktrans trimmed with credit_left=connector.credit_left %}
5
OVH SMS alert: only {{ credit_left }} credits left
6
{% endblocktrans %}{% endautoescape %}{% endblock %}
passerelle/settings.py
210 210
# Passerelle can receive big requests (for example base64 encoded files)
211 211
DATA_UPLOAD_MAX_MEMORY_SIZE = 100*1024*1024
212 212

  
213
SITE_BASE_URL = 'http://localhost'
214

  
213 215
# List of passerelle.utils.Request response Content-Type to log
214 216
LOGGED_CONTENT_TYPES_MESSAGES = (
215 217
    r'text/', r'application/(json|xml)'
passerelle/templates/emails/body_base.html
1
<!DOCTYPE html>
2
<html>
3
<head>
4
  <meta charset="utf-8">
5
</head>
6
<body>
7
  <div style="max-width: 60ex;">
8
    <div class="content">
9
      {% block content %}
10
      {{ content }}
11
      {% endblock %}
12
    </div>
13
  </div>
14
</body>
15
</html>
passerelle/templates/emails/body_base.txt
1
{% block content %}{{ content }}{% endblock %}
passerelle/templates/emails/button-link.html
1
<a href="{{ url }}">{{ label }}</a>
passerelle/templates/emails/subject.txt
1
{% block email-subject %}{% endblock %}
tests/test_sms.py
259 259
    with utils.mock_url(ovh_url, resp, 200) as mocked:
260 260
        connector.hourly()
261 261
    assert connector.credit_left == 456
262

  
263

  
264
def test_ovh_alert_emails(app, freezer, mailoutbox):
265
    connector = OVHSMSGateway.objects.create(
266
        slug='test-ovh', title='Test OVH', account='sms-test42',
267
        application_key='RHrTdU2oTsrVC0pu',
268
        application_secret='CLjtS69tTcPgCKxedeoZlgMSoQGSiXMa',
269
        consumer_key='iF0zi0MJrbjNcI3hvuvwkhNk8skrigxz',
270
        credit_threshold_alert=100,
271
        credit_left=102,
272
        alert_emails=['test@entrouvert.org'],
273
    )
274
    api = ApiUser.objects.create(username='apiuser')
275
    obj_type = ContentType.objects.get_for_model(connector)
276
    AccessRight.objects.create(codename='can_send_messages', apiuser=api, resource_type=obj_type,
277
                               resource_pk=connector.pk)
278

  
279
    payload = {
280
        'message': 'hello',
281
        'from': '+33699999999',
282
        'to': ['+33688888888', '+33677777777'],
283
    }
284

  
285
    # register job
286
    freezer.move_to('2019-01-01 00:00:00')
287
    path = '/%s/%s/send/' % (connector.get_connector_slug(), connector.slug)
288
    app.post_json(path, params=payload)
289

  
290
    # perform job
291
    freezer.move_to('2019-01-01 01:00:03')
292
    resp = {
293
        'totalCreditsRemoved': 1,
294
    }
295
    base_url = connector.API_URL % {'serviceName': 'sms-test42'}
296
    url = base_url + 'jobs/'
297
    with utils.mock_url(url, resp, 200) as mocked:
298
        connector.jobs()
299

  
300
    assert connector.credit_left == 101
301
    assert len(mailoutbox) == 0
302

  
303
    # register job
304
    app.post_json(path, params=payload)
305

  
306
    # perform job
307
    freezer.move_to('2019-01-01 01:00:06')
308
    resp = {
309
        'totalCreditsRemoved': 2,
310
    }
311
    with utils.mock_url(url, resp, 200) as mocked:
312
        connector.jobs()
313

  
314
    mail = mailoutbox[0]
315
    assert mail.recipients() == ['test@entrouvert.org']
316
    assert mail.subject == 'OVH SMS alert: only 99 credits left'
317
    for body in (mail.body, mail.alternatives[0][0]):
318
        assert connector.account in body
319
        assert connector.title in body
320
        assert 'http://localhost/ovh/test-ovh/' in body
321
    mailoutbox.clear()
322

  
323
    # alert should not be send with every SMS
324
    app.post_json(path, params=payload)
325
    freezer.move_to('2019-01-01 01:00:09')
326
    with utils.mock_url(url, resp, 200) as mocked:
327
        connector.jobs()
328
    assert len(mailoutbox) == 0
329

  
330
    # alert is sent again daily
331
    resp = {'creditsLeft': 99}
332
    ovh_url = connector.API_URL % {'serviceName': 'sms-test42'}
333
    with utils.mock_url(ovh_url, resp, 200) as mocked:
334
        connector.hourly()
335
    assert len(mailoutbox) == 0
336

  
337
    freezer.move_to('2019-01-02 01:00:07')
338
    with utils.mock_url(ovh_url, resp, 200) as mocked:
339
        connector.hourly()
340
    assert len(mailoutbox) == 1
262
-