mirror of
https://github.com/healthchecks/healthchecks.git
synced 2025-04-08 14:40:05 +00:00
Use separate counters for SMS and phone calls.
This commit is contained in:
parent
77ee8452c5
commit
8c13457037
11 changed files with 166 additions and 12 deletions
hc
accounts
api
payments
templates
|
@ -62,6 +62,9 @@ class TeamFieldset(Fieldset):
|
|||
"sms_limit",
|
||||
"sms_sent",
|
||||
"last_sms_date",
|
||||
"call_limit",
|
||||
"calls_sent",
|
||||
"last_call_date",
|
||||
)
|
||||
|
||||
|
||||
|
|
28
hc/accounts/migrations/0031_auto_20200803_1413.py
Normal file
28
hc/accounts/migrations/0031_auto_20200803_1413.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -37,6 +37,7 @@ class ProfileManager(models.Manager):
|
|||
# If not using payments, set high limits
|
||||
profile.check_limit = 500
|
||||
profile.sms_limit = 500
|
||||
profile.call_limit = 500
|
||||
profile.team_limit = 500
|
||||
|
||||
profile.save()
|
||||
|
@ -52,9 +53,15 @@ class Profile(models.Model):
|
|||
ping_log_limit = models.IntegerField(default=100)
|
||||
check_limit = models.IntegerField(default=20)
|
||||
token = models.CharField(max_length=128, blank=True)
|
||||
|
||||
last_sms_date = models.DateTimeField(null=True, blank=True)
|
||||
sms_limit = models.IntegerField(default=5)
|
||||
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)
|
||||
sort = models.CharField(max_length=20, default="created")
|
||||
deletion_notice_date = models.DateTimeField(null=True, blank=True)
|
||||
|
@ -229,6 +236,29 @@ class Profile(models.Model):
|
|||
self.save()
|
||||
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):
|
||||
from hc.api.models import Check
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ from django.contrib.auth.models import User
|
|||
from django.core import mail
|
||||
from django.test import TestCase
|
||||
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 django.conf import settings
|
||||
|
||||
|
@ -18,6 +18,11 @@ class SignupTestCase(TestCase):
|
|||
# An user should have been created
|
||||
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
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
subject = "Log in to %s" % settings.SITE_NAME
|
||||
|
@ -37,6 +42,17 @@ class SignupTestCase(TestCase):
|
|||
channel = Channel.objects.get()
|
||||
self.assertEqual(channel.project, project)
|
||||
|
||||
@override_settings(USE_PAYMENTS=False)
|
||||
def test_it_sets_high_limits(self):
|
||||
form = {"identity": "alice@example.org"}
|
||||
|
||||
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)
|
||||
def test_it_obeys_registration_open(self):
|
||||
form = {"identity": "dan@example.org"}
|
||||
|
|
18
hc/api/migrations/0074_auto_20200803_1411.py
Normal file
18
hc/api/migrations/0074_auto_20200803_1411.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -756,6 +756,9 @@ class NotifyTestCase(BaseTestCase):
|
|||
|
||||
@patch("hc.api.transports.requests.request")
|
||||
def test_call(self, mock_post):
|
||||
self.profile.call_limit = 1
|
||||
self.profile.save()
|
||||
|
||||
value = {"label": "foo", "value": "+1234567890"}
|
||||
self._setup_data("call", json.dumps(value))
|
||||
self.check.last_ping = now() - td(hours=2)
|
||||
|
@ -772,8 +775,8 @@ class NotifyTestCase(BaseTestCase):
|
|||
@patch("hc.api.transports.requests.request")
|
||||
def test_call_limit(self, mock_post):
|
||||
# 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()
|
||||
|
||||
definition = {"value": "+1234567890"}
|
||||
|
@ -792,6 +795,19 @@ class NotifyTestCase(BaseTestCase):
|
|||
self.assertEqual(email.to[0], "alice@example.org")
|
||||
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")
|
||||
@override_settings(APPRISE_ENABLED=True)
|
||||
def test_apprise_enabled(self, mock_apprise):
|
||||
|
|
|
@ -486,7 +486,7 @@ class Call(HttpTransport):
|
|||
|
||||
def notify(self, check):
|
||||
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")
|
||||
return "Monthly phone call limit exceeded"
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
|
||||
self.profile.sms_limit = 0
|
||||
self.profile.sms_sent = 1
|
||||
self.profile.call_limit = 0
|
||||
self.profile.calls_sent = 1
|
||||
self.profile.save()
|
||||
|
||||
r = self.run_update()
|
||||
|
@ -41,6 +43,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
self.assertEqual(self.profile.team_limit, 9)
|
||||
self.assertEqual(self.profile.sms_limit, 50)
|
||||
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
|
||||
# because there was no previous subscription
|
||||
|
@ -54,6 +58,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
|
||||
self.profile.sms_limit = 0
|
||||
self.profile.sms_sent = 1
|
||||
self.profile.call_limit = 0
|
||||
self.profile.calls_sent = 1
|
||||
self.profile.save()
|
||||
|
||||
r = self.run_update("S5")
|
||||
|
@ -72,6 +78,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
self.assertEqual(self.profile.team_limit, 2)
|
||||
self.assertEqual(self.profile.sms_limit, 5)
|
||||
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
|
||||
assert not mock.Subscription.cancel.called
|
||||
|
@ -82,6 +90,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
|
||||
self.profile.sms_limit = 0
|
||||
self.profile.sms_sent = 1
|
||||
self.profile.call_limit = 0
|
||||
self.profile.calls_sent = 1
|
||||
self.profile.save()
|
||||
|
||||
r = self.run_update("Y192")
|
||||
|
@ -100,6 +110,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
self.assertEqual(self.profile.team_limit, 9)
|
||||
self.assertEqual(self.profile.sms_limit, 50)
|
||||
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
|
||||
assert not mock.Subscription.cancel.called
|
||||
|
@ -110,6 +122,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
|
||||
self.profile.sms_limit = 0
|
||||
self.profile.sms_sent = 1
|
||||
self.profile.call_limit = 0
|
||||
self.profile.calls_sent = 1
|
||||
self.profile.save()
|
||||
|
||||
r = self.run_update("P80")
|
||||
|
@ -128,6 +142,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
self.assertEqual(self.profile.team_limit, 500)
|
||||
self.assertEqual(self.profile.sms_limit, 500)
|
||||
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
|
||||
assert not mock.Subscription.cancel.called
|
||||
|
@ -144,6 +160,8 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
|
||||
self.profile.sms_limit = 1
|
||||
self.profile.sms_sent = 1
|
||||
self.profile.call_limit = 1
|
||||
self.profile.calls_sent = 1
|
||||
self.profile.save()
|
||||
|
||||
r = self.run_update("")
|
||||
|
@ -162,6 +180,7 @@ class UpdateSubscriptionTestCase(BaseTestCase):
|
|||
self.assertEqual(self.profile.check_limit, 20)
|
||||
self.assertEqual(self.profile.team_limit, 2)
|
||||
self.assertEqual(self.profile.sms_limit, 5)
|
||||
self.assertEqual(self.profile.call_limit, 0)
|
||||
|
||||
self.assertTrue(mock.Subscription.cancel.called)
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ def update(request):
|
|||
profile.check_limit = 20
|
||||
profile.team_limit = 2
|
||||
profile.sms_limit = 5
|
||||
profile.call_limit = 0
|
||||
profile.save()
|
||||
|
||||
if plan_id == "":
|
||||
|
@ -135,6 +136,8 @@ def update(request):
|
|||
profile.ping_log_limit = 1000
|
||||
profile.sms_limit = 5
|
||||
profile.sms_sent = 0
|
||||
profile.call_limit = 5
|
||||
profile.calls_sent = 0
|
||||
profile.save()
|
||||
elif plan_id in ("P20", "Y192"):
|
||||
profile.check_limit = 100
|
||||
|
@ -142,6 +145,8 @@ def update(request):
|
|||
profile.ping_log_limit = 1000
|
||||
profile.sms_limit = 50
|
||||
profile.sms_sent = 0
|
||||
profile.call_limit = 20
|
||||
profile.calls_sent = 0
|
||||
profile.save()
|
||||
elif plan_id in ("P80", "Y768"):
|
||||
profile.check_limit = 1000
|
||||
|
@ -149,6 +154,8 @@ def update(request):
|
|||
profile.ping_log_limit = 1000
|
||||
profile.sms_limit = 500
|
||||
profile.sms_sent = 0
|
||||
profile.call_limit = 100
|
||||
profile.calls_sent = 0
|
||||
profile.save()
|
||||
|
||||
request.session["set_plan_status"] = "success"
|
||||
|
|
|
@ -124,9 +124,12 @@
|
|||
{% else %}
|
||||
Never
|
||||
{% 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>
|
||||
{% endif %}
|
||||
{% if ch.kind == "call" %}
|
||||
<p>Used {{ profile.calls_sent_this_month }} of {{ profile.call_limit }} phone calls this month.</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="actions">
|
||||
{% if ch.kind == "webhook" %}
|
||||
|
|
|
@ -87,9 +87,10 @@
|
|||
</li>
|
||||
<li class="list-group-item">API access</li>
|
||||
<li class="list-group-item">
|
||||
<span data-help="sms-help">5 SMS, WhatsApp and call credits</span>
|
||||
<span data-help="sms-help">5 SMS & WhatsApp credits</span>
|
||||
</li>
|
||||
<li class="list-group-item"> </li>
|
||||
<li class="list-group-item"> </li>
|
||||
</ul>
|
||||
|
||||
{% if not request.user.is_authenticated %}
|
||||
|
@ -120,7 +121,10 @@
|
|||
</li>
|
||||
<li class="list-group-item">API access</li>
|
||||
<li class="list-group-item">
|
||||
<span data-help="sms-help">5 SMS, WhatsApp and call credits</span>
|
||||
<span data-help="sms-help">5 SMS & WhatsApp credits</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span data-help="phone-call-help">5 phone call credits</span>
|
||||
</li>
|
||||
<li class="list-group-item">Email support</li>
|
||||
</ul>
|
||||
|
@ -156,7 +160,10 @@
|
|||
</li>
|
||||
<li class="list-group-item">API access</li>
|
||||
<li class="list-group-item">
|
||||
<span data-help="sms-help">50 SMS, WhatsApp and call credits</span>
|
||||
<span data-help="sms-help">50 SMS & WhatsApp credits</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span data-help="phone-call-help">20 phone call credits</span>
|
||||
</li>
|
||||
<li class="list-group-item">Email support</li>
|
||||
</ul>
|
||||
|
@ -192,7 +199,10 @@
|
|||
</li>
|
||||
<li class="list-group-item">API access</li>
|
||||
<li class="list-group-item">
|
||||
<span data-help="sms-help">500 SMS, WhatsApp and call credits</span>
|
||||
<span data-help="sms-help">500 SMS & WhatsApp credits</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span data-help="phone-call-help">100 phone call credits</span>
|
||||
</li>
|
||||
<li class="list-group-item">Priority email support</li>
|
||||
</ul>
|
||||
|
@ -303,10 +313,14 @@
|
|||
</div>
|
||||
|
||||
<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, WhatsApp and phone
|
||||
call notifications. </p>
|
||||
<p>The limit is applied to the combined number of sent SMS and WhatsApp
|
||||
notifications.</p>
|
||||
</div>
|
||||
|
||||
<div id="phone-call-help" class="hidden">
|
||||
<p>The maximum number of phone call notifications per month.</p>
|
||||
</div>
|
||||
|
||||
{% if not request.user.is_authenticated %}
|
||||
|
|
Loading…
Add table
Reference in a new issue