forked from GithubBackups/healthchecks
Pricing page tweaks. Limit free accounts to 20 checks per account.
This commit is contained in:
parent
09e5129bbe
commit
859a9ed735
20
hc/accounts/migrations/0007_profile_check_limit.py
Normal file
20
hc/accounts/migrations/0007_profile_check_limit.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2017-05-07 13:04
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0006_profile_current_team'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='profile',
|
||||||
|
name='check_limit',
|
||||||
|
field=models.IntegerField(default=20),
|
||||||
|
),
|
||||||
|
]
|
@ -18,6 +18,10 @@ class ProfileManager(models.Manager):
|
|||||||
profile = self.filter(user=user).first()
|
profile = self.filter(user=user).first()
|
||||||
if profile is None:
|
if profile is None:
|
||||||
profile = Profile(user=user, team_access_allowed=user.is_superuser)
|
profile = Profile(user=user, team_access_allowed=user.is_superuser)
|
||||||
|
if not settings.USE_PAYMENTS:
|
||||||
|
# If not using payments, set a high check_limit
|
||||||
|
profile.check_limit = 500
|
||||||
|
|
||||||
profile.save()
|
profile.save()
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
@ -30,6 +34,7 @@ class Profile(models.Model):
|
|||||||
next_report_date = models.DateTimeField(null=True, blank=True)
|
next_report_date = models.DateTimeField(null=True, blank=True)
|
||||||
reports_allowed = models.BooleanField(default=True)
|
reports_allowed = models.BooleanField(default=True)
|
||||||
ping_log_limit = models.IntegerField(default=100)
|
ping_log_limit = models.IntegerField(default=100)
|
||||||
|
check_limit = models.IntegerField(default=20)
|
||||||
token = models.CharField(max_length=128, blank=True)
|
token = models.CharField(max_length=128, blank=True)
|
||||||
api_key = models.CharField(max_length=128, blank=True)
|
api_key = models.CharField(max_length=128, blank=True)
|
||||||
current_team = models.ForeignKey("self", null=True)
|
current_team = models.ForeignKey("self", null=True)
|
||||||
|
21
hc/api/migrations/0029_auto_20170507_1251.py
Normal file
21
hc/api/migrations/0029_auto_20170507_1251.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2017-05-07 12:51
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0028_auto_20170305_1907'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='notification',
|
||||||
|
name='code',
|
||||||
|
field=models.UUIDField(default=uuid.uuid4, editable=False, null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -179,3 +179,10 @@ class CreateCheckTestCase(BaseTestCase):
|
|||||||
|
|
||||||
doc = r.json()
|
doc = r.json()
|
||||||
self.assertEqual(doc["timeout"], 86400)
|
self.assertEqual(doc["timeout"], 86400)
|
||||||
|
|
||||||
|
def test_it_obeys_check_limit(self):
|
||||||
|
self.profile.check_limit = 0
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
r = self.post({"api_key": "abc"})
|
||||||
|
self.assertEqual(r.status_code, 403)
|
||||||
|
@ -111,6 +111,10 @@ def checks(request):
|
|||||||
created = False
|
created = False
|
||||||
check = _lookup(request.user, request.json)
|
check = _lookup(request.user, request.json)
|
||||||
if check is None:
|
if check is None:
|
||||||
|
num_checks = Check.objects.filter(user=request.user).count()
|
||||||
|
if num_checks >= request.user.profile.check_limit:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
check = Check(user=request.user)
|
check = Check(user=request.user)
|
||||||
created = True
|
created = True
|
||||||
|
|
||||||
|
@ -25,3 +25,12 @@ class AddCheckTestCase(BaseTestCase):
|
|||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 405)
|
self.assertEqual(r.status_code, 405)
|
||||||
|
|
||||||
|
def test_it_obeys_check_limit(self):
|
||||||
|
self.profile.check_limit = 0
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
url = "/checks/add/"
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.post(url)
|
||||||
|
self.assertEqual(r.status_code, 400)
|
||||||
|
@ -58,3 +58,11 @@ class MyChecksTestCase(BaseTestCase):
|
|||||||
|
|
||||||
# Mobile
|
# Mobile
|
||||||
self.assertContains(r, "label-warning")
|
self.assertContains(r, "label-warning")
|
||||||
|
|
||||||
|
def test_it_hides_add_check_button(self):
|
||||||
|
self.profile.check_limit = 0
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get("/checks/")
|
||||||
|
self.assertContains(r, "Check limit reached", status_code=200)
|
||||||
|
@ -54,6 +54,8 @@ def my_checks(request):
|
|||||||
elif check.in_grace_period():
|
elif check.in_grace_period():
|
||||||
grace_tags.add(tag)
|
grace_tags.add(tag)
|
||||||
|
|
||||||
|
can_add_more = len(checks) < request.team.check_limit
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
"page": "checks",
|
"page": "checks",
|
||||||
"checks": checks,
|
"checks": checks,
|
||||||
@ -62,7 +64,8 @@ def my_checks(request):
|
|||||||
"down_tags": down_tags,
|
"down_tags": down_tags,
|
||||||
"grace_tags": grace_tags,
|
"grace_tags": grace_tags,
|
||||||
"ping_endpoint": settings.PING_ENDPOINT,
|
"ping_endpoint": settings.PING_ENDPOINT,
|
||||||
"timezones": all_timezones
|
"timezones": all_timezones,
|
||||||
|
"can_add_more": can_add_more
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "front/my_checks.html", ctx)
|
return render(request, "front/my_checks.html", ctx)
|
||||||
@ -135,6 +138,10 @@ def about(request):
|
|||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
def add_check(request):
|
def add_check(request):
|
||||||
|
num_checks = Check.objects.filter(user=request.team.user).count()
|
||||||
|
if num_checks >= request.team.check_limit:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
check = Check(user=request.team.user)
|
check = Check(user=request.team.user)
|
||||||
check.save()
|
check.save()
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from mock import patch
|
from mock import patch
|
||||||
|
|
||||||
|
from hc.accounts.models import Profile
|
||||||
from hc.payments.models import Subscription
|
from hc.payments.models import Subscription
|
||||||
from hc.test import BaseTestCase
|
from hc.test import BaseTestCase
|
||||||
|
|
||||||
@ -13,6 +14,10 @@ class CancelPlanTestCase(BaseTestCase):
|
|||||||
self.sub.plan_id = "P5"
|
self.sub.plan_id = "P5"
|
||||||
self.sub.save()
|
self.sub.save()
|
||||||
|
|
||||||
|
self.profile.ping_log_limit = 1000
|
||||||
|
self.profile.check_limit = 500
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
@patch("hc.payments.models.braintree")
|
@patch("hc.payments.models.braintree")
|
||||||
def test_it_works(self, mock_braintree):
|
def test_it_works(self, mock_braintree):
|
||||||
|
|
||||||
@ -23,3 +28,9 @@ class CancelPlanTestCase(BaseTestCase):
|
|||||||
self.sub.refresh_from_db()
|
self.sub.refresh_from_db()
|
||||||
self.assertEqual(self.sub.subscription_id, "")
|
self.assertEqual(self.sub.subscription_id, "")
|
||||||
self.assertEqual(self.sub.plan_id, "")
|
self.assertEqual(self.sub.plan_id, "")
|
||||||
|
|
||||||
|
# User's profile should have standard limits
|
||||||
|
profile = Profile.objects.get(user=self.alice)
|
||||||
|
self.assertEqual(profile.ping_log_limit, 100)
|
||||||
|
self.assertEqual(profile.check_limit, 20)
|
||||||
|
self.assertFalse(profile.team_access_allowed)
|
||||||
|
@ -38,9 +38,11 @@ class CreatePlanTestCase(BaseTestCase):
|
|||||||
self.assertEqual(sub.subscription_id, "t-sub-id")
|
self.assertEqual(sub.subscription_id, "t-sub-id")
|
||||||
self.assertEqual(sub.plan_id, "P5")
|
self.assertEqual(sub.plan_id, "P5")
|
||||||
|
|
||||||
# User's profile should have a higher ping log limit:
|
# User's profile should have a higher limits
|
||||||
profile = Profile.objects.get(user=self.alice)
|
profile = Profile.objects.get(user=self.alice)
|
||||||
self.assertEqual(profile.ping_log_limit, 1000)
|
self.assertEqual(profile.ping_log_limit, 1000)
|
||||||
|
self.assertEqual(profile.check_limit, 500)
|
||||||
|
self.assertTrue(profile.team_access_allowed)
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -108,10 +108,12 @@ def create_plan(request):
|
|||||||
profile = request.user.profile
|
profile = request.user.profile
|
||||||
if plan_id == "P5":
|
if plan_id == "P5":
|
||||||
profile.ping_log_limit = 1000
|
profile.ping_log_limit = 1000
|
||||||
|
profile.check_limit = 500
|
||||||
profile.team_access_allowed = True
|
profile.team_access_allowed = True
|
||||||
profile.save()
|
profile.save()
|
||||||
elif plan_id == "P75":
|
elif plan_id == "P75":
|
||||||
profile.ping_log_limit = 1000
|
profile.ping_log_limit = 1000
|
||||||
|
profile.check_limit = 500
|
||||||
profile.team_access_allowed = True
|
profile.team_access_allowed = True
|
||||||
profile.save()
|
profile.save()
|
||||||
|
|
||||||
@ -157,6 +159,14 @@ def update_payment_method(request):
|
|||||||
def cancel_plan(request):
|
def cancel_plan(request):
|
||||||
sub = Subscription.objects.get(user=request.user)
|
sub = Subscription.objects.get(user=request.user)
|
||||||
sub.cancel()
|
sub.cancel()
|
||||||
|
|
||||||
|
# Revert to default limits--
|
||||||
|
profile = request.user.profile
|
||||||
|
profile.ping_log_limit = 100
|
||||||
|
profile.check_limit = 20
|
||||||
|
profile.team_access_allowed = False
|
||||||
|
profile.save()
|
||||||
|
|
||||||
return redirect("hc-pricing")
|
return redirect("hc-pricing")
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,6 +191,11 @@ To create a "cron" check, specify the "schedule" and "tz" parameters.
|
|||||||
<td>Returned if the <code>unique</code> parameter was used and an
|
<td>Returned if the <code>unique</code> parameter was used and an
|
||||||
existing check was matched.</td>
|
existing check was matched.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>403 Forbidden</th>
|
||||||
|
<td>Returned if the account's check limit has been reached.
|
||||||
|
For free accounts, the limit is 20 checks per account.</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3 class="api-section">Example Request</h3>
|
<h3 class="api-section">Example Request</h3>
|
||||||
|
@ -42,10 +42,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
{% if can_add_more %}
|
||||||
<form method="post" action="{% url 'hc-add-check' %}" class="text-center">
|
<form method="post" action="{% url 'hc-add-check' %}" class="text-center">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="submit" class="btn btn-primary btn-lg" value="Add Check">
|
<input type="submit" class="btn btn-primary btn-lg" value="Add Check">
|
||||||
</form>
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<strong>Check limit reached.</strong>
|
||||||
|
To add more checks, please
|
||||||
|
<a href="{% url 'hc-pricing' %}">upgrade your account!</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -67,11 +67,10 @@
|
|||||||
<p>free</p>
|
<p>free</p>
|
||||||
</div>
|
</div>
|
||||||
<ul class="list-group text-center">
|
<ul class="list-group text-center">
|
||||||
<li class="list-group-item"><i class="fa fa-check"></i> Personal or Commercial use</li>
|
<li class="list-group-item">Single User Access</li>
|
||||||
<li class="list-group-item"><i class="fa fa-check"></i> Unlimited Checks</li>
|
<li class="list-group-item"><i class="fa fa-check"></i> 20 Checks</li>
|
||||||
<li class="list-group-item"><i class="fa fa-check"></i> Unlimited Alerts</li>
|
|
||||||
<li class="list-group-item">100 log entries per check</li>
|
<li class="list-group-item">100 log entries per check</li>
|
||||||
<li class="list-group-item">One User</li>
|
<li class="list-group-item"><i class="fa fa-check"></i> Personal or Commercial use</li>
|
||||||
<li class="list-group-item"> </li>
|
<li class="list-group-item"> </li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
@ -102,11 +101,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="list-group text-center">
|
<ul class="list-group text-center">
|
||||||
<li class="list-group-item">Personal or Commercial use</li>
|
|
||||||
<li class="list-group-item">Unlimited Checks</li>
|
|
||||||
<li class="list-group-item">Unlimited Alerts</li>
|
|
||||||
<li class="list-group-item">1000 log entries per check</li>
|
|
||||||
<li class="list-group-item">Team Access</li>
|
<li class="list-group-item">Team Access</li>
|
||||||
|
<li class="list-group-item">Unlimited Checks</li>
|
||||||
|
<li class="list-group-item">1000 log entries per check</li>
|
||||||
|
<li class="list-group-item">Personal or Commercial use</li>
|
||||||
<li class="list-group-item">Email Support</li>
|
<li class="list-group-item">Email Support</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
@ -144,11 +142,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="list-group text-center">
|
<ul class="list-group text-center">
|
||||||
<li class="list-group-item">Personal or Commercial use</li>
|
|
||||||
<li class="list-group-item">Unlimited Checks</li>
|
|
||||||
<li class="list-group-item">Unlimited Alerts</li>
|
|
||||||
<li class="list-group-item">1000 log entries per check</li>
|
|
||||||
<li class="list-group-item">Team Access</li>
|
<li class="list-group-item">Team Access</li>
|
||||||
|
<li class="list-group-item">Unlimited Checks</li>
|
||||||
|
<li class="list-group-item">1000 log entries per check</li>
|
||||||
|
<li class="list-group-item">Personal or Commercial use</li>
|
||||||
<li class="list-group-item">Priority Email Support</li>
|
<li class="list-group-item">Priority Email Support</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
@ -213,7 +210,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<h1>Premium Features</h1>
|
<h1>Premium Features</h1>
|
||||||
<h2>What is the "log entries / check" number?</h2>
|
<h2>What is the "log entries per check" number?</h2>
|
||||||
<p>
|
<p>
|
||||||
For each of your checks, healthchecks.io keeps a
|
For each of your checks, healthchecks.io keeps a
|
||||||
historic log of the received pings. The log can be useful
|
historic log of the received pings. The log can be useful
|
||||||
|
Loading…
x
Reference in New Issue
Block a user