Browse Source

Use separate counters for SMS and phone calls.

pull/409/head
Pēteris Caune 4 years ago
parent
commit
8c13457037
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
11 changed files with 166 additions and 12 deletions
  1. +3
    -0
      hc/accounts/admin.py
  2. +28
    -0
      hc/accounts/migrations/0031_auto_20200803_1413.py
  3. +30
    -0
      hc/accounts/models.py
  4. +17
    -1
      hc/accounts/tests/test_signup.py
  5. +18
    -0
      hc/api/migrations/0074_auto_20200803_1411.py
  6. +18
    -2
      hc/api/tests/test_notify.py
  7. +1
    -1
      hc/api/transports.py
  8. +19
    -0
      hc/payments/tests/test_update_subscription.py
  9. +7
    -0
      hc/payments/views.py
  10. +4
    -1
      templates/front/channels.html
  11. +21
    -7
      templates/payments/pricing.html

+ 3
- 0
hc/accounts/admin.py View File

@ -62,6 +62,9 @@ class TeamFieldset(Fieldset):
"sms_limit", "sms_limit",
"sms_sent", "sms_sent",
"last_sms_date", "last_sms_date",
"call_limit",
"calls_sent",
"last_call_date",
) )


+ 28
- 0
hc/accounts/migrations/0031_auto_20200803_1413.py View File

@ -0,0 +1,28 @@
# Generated by Django 3.0.8 on 2020-08-03 14:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0030_member_transfer_request_date'),
]
operations = [
migrations.AddField(
model_name='profile',
name='call_limit',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='profile',
name='calls_sent',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='profile',
name='last_call_date',
field=models.DateTimeField(blank=True, null=True),
),
]

+ 30
- 0
hc/accounts/models.py View File

@ -37,6 +37,7 @@ class ProfileManager(models.Manager):
# If not using payments, set high limits # If not using payments, set high limits
profile.check_limit = 500 profile.check_limit = 500
profile.sms_limit = 500 profile.sms_limit = 500
profile.call_limit = 500
profile.team_limit = 500 profile.team_limit = 500
profile.save() profile.save()
@ -52,9 +53,15 @@ class Profile(models.Model):
ping_log_limit = models.IntegerField(default=100) ping_log_limit = models.IntegerField(default=100)
check_limit = models.IntegerField(default=20) check_limit = models.IntegerField(default=20)
token = models.CharField(max_length=128, blank=True) token = models.CharField(max_length=128, blank=True)
last_sms_date = models.DateTimeField(null=True, blank=True) last_sms_date = models.DateTimeField(null=True, blank=True)
sms_limit = models.IntegerField(default=5) sms_limit = models.IntegerField(default=5)
sms_sent = models.IntegerField(default=0) sms_sent = models.IntegerField(default=0)
last_call_date = models.DateTimeField(null=True, blank=True)
call_limit = models.IntegerField(default=0)
calls_sent = models.IntegerField(default=0)
team_limit = models.IntegerField(default=2) team_limit = models.IntegerField(default=2)
sort = models.CharField(max_length=20, default="created") sort = models.CharField(max_length=20, default="created")
deletion_notice_date = models.DateTimeField(null=True, blank=True) deletion_notice_date = models.DateTimeField(null=True, blank=True)
@ -229,6 +236,29 @@ class Profile(models.Model):
self.save() self.save()
return True return True
def calls_sent_this_month(self):
# IF last_call_date was never set, we have not made any phone calls yet.
if not self.last_call_date:
return 0
# If last sent date is not from this month, we've made 0 calls this month.
if month(timezone.now()) > month(self.last_call_date):
return 0
return self.calls_sent
def authorize_call(self):
""" If monthly limit not exceeded, increase counter and return True """
sent_this_month = self.calls_sent_this_month()
if sent_this_month >= self.call_limit:
return False
self.calls_sent = sent_this_month + 1
self.last_call_date = timezone.now()
self.save()
return True
def num_checks_used(self): def num_checks_used(self):
from hc.api.models import Check from hc.api.models import Check


+ 17
- 1
hc/accounts/tests/test_signup.py View File

@ -2,7 +2,7 @@ from django.contrib.auth.models import User
from django.core import mail from django.core import mail
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from hc.accounts.models import Project
from hc.accounts.models import Profile, Project
from hc.api.models import Channel, Check from hc.api.models import Channel, Check
from django.conf import settings from django.conf import settings
@ -18,6 +18,11 @@ class SignupTestCase(TestCase):
# An user should have been created # An user should have been created
user = User.objects.get() user = User.objects.get()
# A profile should have been created
profile = Profile.objects.get()
self.assertEqual(profile.sms_limit, 5)
self.assertEqual(profile.call_limit, 0)
# And email sent # And email sent
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
subject = "Log in to %s" % settings.SITE_NAME subject = "Log in to %s" % settings.SITE_NAME
@ -37,6 +42,17 @@ class SignupTestCase(TestCase):
channel = Channel.objects.get() channel = Channel.objects.get()
self.assertEqual(channel.project, project) self.assertEqual(channel.project, project)
@override_settings(USE_PAYMENTS=False)
def test_it_sets_high_limits(self):
form = {"identity": "[email protected]"}
self.client.post("/accounts/signup/", form)
# A profile should have been created
profile = Profile.objects.get()
self.assertEqual(profile.sms_limit, 500)
self.assertEqual(profile.call_limit, 500)
@override_settings(REGISTRATION_OPEN=False) @override_settings(REGISTRATION_OPEN=False)
def test_it_obeys_registration_open(self): def test_it_obeys_registration_open(self):
form = {"identity": "[email protected]"} form = {"identity": "[email protected]"}


+ 18
- 0
hc/api/migrations/0074_auto_20200803_1411.py View File

@ -0,0 +1,18 @@
# Generated by Django 3.0.8 on 2020-08-03 14:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0073_auto_20200721_1000'),
]
operations = [
migrations.AlterField(
model_name='channel',
name='kind',
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('pagertree', 'PagerTree'), ('pagerteam', 'Pager Team'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord'), ('telegram', 'Telegram'), ('sms', 'SMS'), ('zendesk', 'Zendesk'), ('trello', 'Trello'), ('matrix', 'Matrix'), ('whatsapp', 'WhatsApp'), ('apprise', 'Apprise'), ('mattermost', 'Mattermost'), ('msteams', 'Microsoft Teams'), ('shell', 'Shell Command'), ('zulip', 'Zulip'), ('spike', 'Spike'), ('call', 'Phone Call')], max_length=20),
),
]

+ 18
- 2
hc/api/tests/test_notify.py View File

@ -756,6 +756,9 @@ class NotifyTestCase(BaseTestCase):
@patch("hc.api.transports.requests.request") @patch("hc.api.transports.requests.request")
def test_call(self, mock_post): def test_call(self, mock_post):
self.profile.call_limit = 1
self.profile.save()
value = {"label": "foo", "value": "+1234567890"} value = {"label": "foo", "value": "+1234567890"}
self._setup_data("call", json.dumps(value)) self._setup_data("call", json.dumps(value))
self.check.last_ping = now() - td(hours=2) self.check.last_ping = now() - td(hours=2)
@ -772,8 +775,8 @@ class NotifyTestCase(BaseTestCase):
@patch("hc.api.transports.requests.request") @patch("hc.api.transports.requests.request")
def test_call_limit(self, mock_post): def test_call_limit(self, mock_post):
# At limit already: # At limit already:
self.profile.last_sms_date = now()
self.profile.sms_sent = 50
self.profile.last_call_date = now()
self.profile.calls_sent = 50
self.profile.save() self.profile.save()
definition = {"value": "+1234567890"} definition = {"value": "+1234567890"}
@ -792,6 +795,19 @@ class NotifyTestCase(BaseTestCase):
self.assertEqual(email.to[0], "[email protected]") self.assertEqual(email.to[0], "[email protected]")
self.assertEqual(email.subject, "Monthly Phone Call Limit Reached") self.assertEqual(email.subject, "Monthly Phone Call Limit Reached")
@patch("hc.api.transports.requests.request")
def test_call_limit_reset(self, mock_post):
# At limit, but also into a new month
self.profile.calls_sent = 50
self.profile.last_call_date = now() - td(days=100)
self.profile.save()
self._setup_data("sms", "+1234567890")
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
self.assertTrue(mock_post.called)
@patch("apprise.Apprise") @patch("apprise.Apprise")
@override_settings(APPRISE_ENABLED=True) @override_settings(APPRISE_ENABLED=True)
def test_apprise_enabled(self, mock_apprise): def test_apprise_enabled(self, mock_apprise):


+ 1
- 1
hc/api/transports.py View File

@ -486,7 +486,7 @@ class Call(HttpTransport):
def notify(self, check): def notify(self, check):
profile = Profile.objects.for_user(self.channel.project.owner) profile = Profile.objects.for_user(self.channel.project.owner)
if not profile.authorize_sms():
if not profile.authorize_call():
profile.send_sms_limit_notice("phone call") profile.send_sms_limit_notice("phone call")
return "Monthly phone call limit exceeded" return "Monthly phone call limit exceeded"


+ 19
- 0
hc/payments/tests/test_update_subscription.py View File

@ -22,6 +22,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.profile.sms_limit = 0 self.profile.sms_limit = 0
self.profile.sms_sent = 1 self.profile.sms_sent = 1
self.profile.call_limit = 0
self.profile.calls_sent = 1
self.profile.save() self.profile.save()
r = self.run_update() r = self.run_update()
@ -41,6 +43,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.assertEqual(self.profile.team_limit, 9) self.assertEqual(self.profile.team_limit, 9)
self.assertEqual(self.profile.sms_limit, 50) self.assertEqual(self.profile.sms_limit, 50)
self.assertEqual(self.profile.sms_sent, 0) self.assertEqual(self.profile.sms_sent, 0)
self.assertEqual(self.profile.call_limit, 20)
self.assertEqual(self.profile.calls_sent, 0)
# braintree.Subscription.cancel should have not been called # braintree.Subscription.cancel should have not been called
# because there was no previous subscription # because there was no previous subscription
@ -54,6 +58,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.profile.sms_limit = 0 self.profile.sms_limit = 0
self.profile.sms_sent = 1 self.profile.sms_sent = 1
self.profile.call_limit = 0
self.profile.calls_sent = 1
self.profile.save() self.profile.save()
r = self.run_update("S5") r = self.run_update("S5")
@ -72,6 +78,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.assertEqual(self.profile.team_limit, 2) self.assertEqual(self.profile.team_limit, 2)
self.assertEqual(self.profile.sms_limit, 5) self.assertEqual(self.profile.sms_limit, 5)
self.assertEqual(self.profile.sms_sent, 0) self.assertEqual(self.profile.sms_sent, 0)
self.assertEqual(self.profile.call_limit, 5)
self.assertEqual(self.profile.calls_sent, 0)
# braintree.Subscription.cancel should have not been called # braintree.Subscription.cancel should have not been called
assert not mock.Subscription.cancel.called assert not mock.Subscription.cancel.called
@ -82,6 +90,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.profile.sms_limit = 0 self.profile.sms_limit = 0
self.profile.sms_sent = 1 self.profile.sms_sent = 1
self.profile.call_limit = 0
self.profile.calls_sent = 1
self.profile.save() self.profile.save()
r = self.run_update("Y192") r = self.run_update("Y192")
@ -100,6 +110,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.assertEqual(self.profile.team_limit, 9) self.assertEqual(self.profile.team_limit, 9)
self.assertEqual(self.profile.sms_limit, 50) self.assertEqual(self.profile.sms_limit, 50)
self.assertEqual(self.profile.sms_sent, 0) self.assertEqual(self.profile.sms_sent, 0)
self.assertEqual(self.profile.call_limit, 20)
self.assertEqual(self.profile.calls_sent, 0)
# braintree.Subscription.cancel should have not been called # braintree.Subscription.cancel should have not been called
assert not mock.Subscription.cancel.called assert not mock.Subscription.cancel.called
@ -110,6 +122,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.profile.sms_limit = 0 self.profile.sms_limit = 0
self.profile.sms_sent = 1 self.profile.sms_sent = 1
self.profile.call_limit = 0
self.profile.calls_sent = 1
self.profile.save() self.profile.save()
r = self.run_update("P80") r = self.run_update("P80")
@ -128,6 +142,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.assertEqual(self.profile.team_limit, 500) self.assertEqual(self.profile.team_limit, 500)
self.assertEqual(self.profile.sms_limit, 500) self.assertEqual(self.profile.sms_limit, 500)
self.assertEqual(self.profile.sms_sent, 0) self.assertEqual(self.profile.sms_sent, 0)
self.assertEqual(self.profile.call_limit, 100)
self.assertEqual(self.profile.calls_sent, 0)
# braintree.Subscription.cancel should have not been called # braintree.Subscription.cancel should have not been called
assert not mock.Subscription.cancel.called assert not mock.Subscription.cancel.called
@ -144,6 +160,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.profile.sms_limit = 1 self.profile.sms_limit = 1
self.profile.sms_sent = 1 self.profile.sms_sent = 1
self.profile.call_limit = 1
self.profile.calls_sent = 1
self.profile.save() self.profile.save()
r = self.run_update("") r = self.run_update("")
@ -162,6 +180,7 @@ class UpdateSubscriptionTestCase(BaseTestCase):
self.assertEqual(self.profile.check_limit, 20) self.assertEqual(self.profile.check_limit, 20)
self.assertEqual(self.profile.team_limit, 2) self.assertEqual(self.profile.team_limit, 2)
self.assertEqual(self.profile.sms_limit, 5) self.assertEqual(self.profile.sms_limit, 5)
self.assertEqual(self.profile.call_limit, 0)
self.assertTrue(mock.Subscription.cancel.called) self.assertTrue(mock.Subscription.cancel.called)


+ 7
- 0
hc/payments/views.py View File

@ -117,6 +117,7 @@ def update(request):
profile.check_limit = 20 profile.check_limit = 20
profile.team_limit = 2 profile.team_limit = 2
profile.sms_limit = 5 profile.sms_limit = 5
profile.call_limit = 0
profile.save() profile.save()
if plan_id == "": if plan_id == "":
@ -135,6 +136,8 @@ def update(request):
profile.ping_log_limit = 1000 profile.ping_log_limit = 1000
profile.sms_limit = 5 profile.sms_limit = 5
profile.sms_sent = 0 profile.sms_sent = 0
profile.call_limit = 5
profile.calls_sent = 0
profile.save() profile.save()
elif plan_id in ("P20", "Y192"): elif plan_id in ("P20", "Y192"):
profile.check_limit = 100 profile.check_limit = 100
@ -142,6 +145,8 @@ def update(request):
profile.ping_log_limit = 1000 profile.ping_log_limit = 1000
profile.sms_limit = 50 profile.sms_limit = 50
profile.sms_sent = 0 profile.sms_sent = 0
profile.call_limit = 20
profile.calls_sent = 0
profile.save() profile.save()
elif plan_id in ("P80", "Y768"): elif plan_id in ("P80", "Y768"):
profile.check_limit = 1000 profile.check_limit = 1000
@ -149,6 +154,8 @@ def update(request):
profile.ping_log_limit = 1000 profile.ping_log_limit = 1000
profile.sms_limit = 500 profile.sms_limit = 500
profile.sms_sent = 0 profile.sms_sent = 0
profile.call_limit = 100
profile.calls_sent = 0
profile.save() profile.save()
request.session["set_plan_status"] = "success" request.session["set_plan_status"] = "success"


+ 4
- 1
templates/front/channels.html View File

@ -124,9 +124,12 @@
{% else %} {% else %}
Never Never
{% endif %} {% endif %}
{% if ch.kind == "sms" or ch.kind == "whatsapp" or ch.kind == "call" %}
{% if ch.kind == "sms" or ch.kind == "whatsapp" %}
<p>Used {{ profile.sms_sent_this_month }} of {{ profile.sms_limit }} sends this month.</p> <p>Used {{ profile.sms_sent_this_month }} of {{ profile.sms_limit }} sends this month.</p>
{% endif %} {% endif %}
{% if ch.kind == "call" %}
<p>Used {{ profile.calls_sent_this_month }} of {{ profile.call_limit }} phone calls this month.</p>
{% endif %}
</td> </td>
<td class="actions"> <td class="actions">
{% if ch.kind == "webhook" %} {% if ch.kind == "webhook" %}


+ 21
- 7
templates/payments/pricing.html View File

@ -87,9 +87,10 @@
</li> </li>
<li class="list-group-item">API access</li> <li class="list-group-item">API access</li>
<li class="list-group-item"> <li class="list-group-item">
<span data-help="sms-help">5 SMS, WhatsApp and call credits</span>
<span data-help="sms-help">5 SMS &amp; WhatsApp credits</span>
</li> </li>
<li class="list-group-item">&nbsp;</li> <li class="list-group-item">&nbsp;</li>
<li class="list-group-item">&nbsp;</li>
</ul> </ul>
{% if not request.user.is_authenticated %} {% if not request.user.is_authenticated %}
@ -120,7 +121,10 @@
</li> </li>
<li class="list-group-item">API access</li> <li class="list-group-item">API access</li>
<li class="list-group-item"> <li class="list-group-item">
<span data-help="sms-help">5 SMS, WhatsApp and call credits</span>
<span data-help="sms-help">5 SMS &amp; WhatsApp credits</span>
</li>
<li class="list-group-item">
<span data-help="phone-call-help">5 phone call credits</span>
</li> </li>
<li class="list-group-item">Email support</li> <li class="list-group-item">Email support</li>
</ul> </ul>
@ -156,7 +160,10 @@
</li> </li>
<li class="list-group-item">API access</li> <li class="list-group-item">API access</li>
<li class="list-group-item"> <li class="list-group-item">
<span data-help="sms-help">50 SMS, WhatsApp and call credits</span>
<span data-help="sms-help">50 SMS &amp; WhatsApp credits</span>
</li>
<li class="list-group-item">
<span data-help="phone-call-help">20 phone call credits</span>
</li> </li>
<li class="list-group-item">Email support</li> <li class="list-group-item">Email support</li>
</ul> </ul>
@ -192,7 +199,10 @@
</li> </li>
<li class="list-group-item">API access</li> <li class="list-group-item">API access</li>
<li class="list-group-item"> <li class="list-group-item">
<span data-help="sms-help">500 SMS, WhatsApp and call credits</span>
<span data-help="sms-help">500 SMS &amp; WhatsApp credits</span>
</li>
<li class="list-group-item">
<span data-help="phone-call-help">100 phone call credits</span>
</li> </li>
<li class="list-group-item">Priority email support</li> <li class="list-group-item">Priority email support</li>
</ul> </ul>
@ -303,10 +313,14 @@
</div> </div>
<div id="sms-help" class="hidden"> <div id="sms-help" class="hidden">
<p>The maximum number of SMS, WhatsApp and phone call notifications per month.</p>
<p>The maximum number of SMS and WhatsApp notifications per month.</p>
<p>The limit is applied to the combined number of sent SMS and WhatsApp
notifications.</p>
</div>
<p>The limit is applied to the combined number of sent SMS, WhatsApp and phone
call notifications. </p>
<div id="phone-call-help" class="hidden">
<p>The maximum number of phone call notifications per month.</p>
</div> </div>
{% if not request.user.is_authenticated %} {% if not request.user.is_authenticated %}


Loading…
Cancel
Save