Browse Source

All plans now have team access, but different team size limits.

pull/140/head
Pēteris Caune 7 years ago
parent
commit
0723476a0c
11 changed files with 78 additions and 44 deletions
  1. +15
    -5
      hc/accounts/admin.py
  2. +20
    -0
      hc/accounts/migrations/0010_profile_team_limit.py
  3. +5
    -0
      hc/accounts/models.py
  4. +7
    -11
      hc/accounts/tests/test_profile.py
  5. +1
    -4
      hc/accounts/views.py
  6. +1
    -1
      hc/payments/tests/test_cancel_plan.py
  7. +1
    -2
      hc/payments/tests/test_create_plan.py
  8. +3
    -0
      hc/payments/views.py
  9. +0
    -1
      hc/test.py
  10. +9
    -7
      templates/accounts/profile.html
  11. +16
    -13
      templates/payments/pricing.html

+ 15
- 5
hc/accounts/admin.py View File

@ -1,6 +1,7 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.db.models import Count
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.safestring import mark_safe
@ -25,7 +26,7 @@ class ProfileFieldset(Fieldset):
class TeamFieldset(Fieldset):
name = "Team"
fields = ("team_name", "team_access_allowed", "check_limit",
fields = ("team_name", "team_limit", "check_limit",
"ping_log_limit", "sms_limit", "sms_sent", "last_sms_date",
"bill_to")
@ -41,17 +42,23 @@ class ProfileAdmin(admin.ModelAdmin):
readonly_fields = ("user", "email")
raw_id_fields = ("current_team", )
list_select_related = ("user", )
list_display = ("id", "users", "checks", "team_access_allowed",
list_display = ("id", "users", "checks", "invited",
"reports_allowed", "ping_log_limit", "sms")
search_fields = ["id", "user__email"]
list_filter = ("team_access_allowed", "reports_allowed",
list_filter = ("team_access_allowed", "team_limit", "reports_allowed",
"check_limit", "next_report_date")
fieldsets = (ProfileFieldset.tuple(), TeamFieldset.tuple())
def get_queryset(self, request):
qs = super(ProfileAdmin, self).get_queryset(request)
qs = qs.annotate(Count("member", distinct=True))
qs = qs.annotate(Count("user__check", distinct=True))
return qs
@mark_safe
def users(self, obj):
if obj.member_set.count() == 0:
if obj.member__count == 0:
return obj.user.email
else:
return render_to_string("admin/profile_list_team.html", {
@ -60,7 +67,7 @@ class ProfileAdmin(admin.ModelAdmin):
@mark_safe
def checks(self, obj):
num_checks = Check.objects.filter(user=obj.user).count()
num_checks = obj.user__check__count
pct = 100 * num_checks / max(obj.check_limit, 1)
pct = min(100, int(pct))
@ -69,6 +76,9 @@ class ProfileAdmin(admin.ModelAdmin):
  %d of %d
""" % (pct, num_checks, obj.check_limit)
def invited(self, obj):
return "%d of %d" % (obj.member__count, obj.team_limit)
def sms(self, obj):
return "%d of %d" % (obj.sms_sent, obj.sms_limit)


+ 20
- 0
hc/accounts/migrations/0010_profile_team_limit.py View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-09-02 11:52
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0009_auto_20170714_1734'),
]
operations = [
migrations.AddField(
model_name='profile',
name='team_limit',
field=models.IntegerField(default=2),
),
]

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

@ -28,6 +28,7 @@ class ProfileManager(models.Manager):
# If not using payments, set high limits
profile.check_limit = 500
profile.sms_limit = 500
profile.team_limit = 500
profile.save()
return profile
@ -49,6 +50,7 @@ class Profile(models.Model):
last_sms_date = models.DateTimeField(null=True, blank=True)
sms_limit = models.IntegerField(default=0)
sms_sent = models.IntegerField(default=0)
team_limit = models.IntegerField(default=2)
objects = ProfileManager()
@ -121,6 +123,9 @@ class Profile(models.Model):
emails.report(self.user.email, ctx)
def can_invite(self):
return self.member_set.count() < self.team_limit
def invite(self, user):
member = Member(team=self, user=user)
member.save()


+ 7
- 11
hc/accounts/tests/test_profile.py View File

@ -75,15 +75,18 @@ class ProfileTestCase(BaseTestCase):
# And an email should have been sent
subj = ('You have been invited to join'
' [email protected] on {0}'.format(getattr(settings, "SITE_NAME")))
' [email protected] on %s' % settings.SITE_NAME)
self.assertEqual(mail.outbox[0].subject, subj)
def test_add_team_member_checks_team_access_allowed_flag(self):
self.client.login(username="[email protected]", password="password")
def test_it_checks_team_size(self):
self.profile.team_limit = 0
self.profile.save()
self.client.login(username="[email protected]", password="password")
form = {"invite_team_member": "1", "email": "[email protected]"}
r = self.client.post("/accounts/profile/", form)
assert r.status_code == 403
self.assertEqual(r.status_code, 403)
def test_it_removes_team_member(self):
self.client.login(username="[email protected]", password="password")
@ -107,13 +110,6 @@ class ProfileTestCase(BaseTestCase):
self.alice.profile.refresh_from_db()
self.assertEqual(self.alice.profile.team_name, "Alpha Team")
def test_set_team_name_checks_team_access_allowed_flag(self):
self.client.login(username="[email protected]", password="password")
form = {"set_team_name": "1", "team_name": "Charlies Team"}
r = self.client.post("/accounts/profile/", form)
assert r.status_code == 403
def test_it_switches_to_own_team(self):
self.client.login(username="[email protected]", password="password")


+ 1
- 4
hc/accounts/views.py View File

@ -189,7 +189,7 @@ def profile(request):
elif "show_api_key" in request.POST:
ctx["show_api_key"] = True
elif "invite_team_member" in request.POST:
if not profile.team_access_allowed:
if not profile.can_invite():
return HttpResponseForbidden()
form = InviteTeamMemberForm(request.POST)
@ -220,9 +220,6 @@ def profile(request):
ctx["team_member_removed"] = email
ctx["team_status"] = "info"
elif "set_team_name" in request.POST:
if not profile.team_access_allowed:
return HttpResponseForbidden()
form = TeamNameForm(request.POST)
if form.is_valid():
profile.team_name = form.cleaned_data["team_name"]


+ 1
- 1
hc/payments/tests/test_cancel_plan.py View File

@ -34,5 +34,5 @@ class CancelPlanTestCase(BaseTestCase):
profile = Profile.objects.get(user=self.alice)
self.assertEqual(profile.ping_log_limit, 100)
self.assertEqual(profile.check_limit, 20)
self.assertEqual(profile.team_limit, 2)
self.assertEqual(profile.sms_limit, 0)
self.assertFalse(profile.team_access_allowed)

+ 1
- 2
hc/payments/tests/test_create_plan.py View File

@ -28,7 +28,6 @@ class CreatePlanTestCase(BaseTestCase):
def test_it_works(self, mock):
self._setup_mock(mock)
self.profile.team_access_allowed = False
self.profile.sms_limit = 0
self.profile.sms_sent = 1
self.profile.save()
@ -47,9 +46,9 @@ class CreatePlanTestCase(BaseTestCase):
self.profile.refresh_from_db()
self.assertEqual(self.profile.ping_log_limit, 1000)
self.assertEqual(self.profile.check_limit, 500)
self.assertEqual(self.profile.team_limit, 9)
self.assertEqual(self.profile.sms_limit, 50)
self.assertEqual(self.profile.sms_sent, 0)
self.assertTrue(self.profile.team_access_allowed)
# braintree.Subscription.cancel should have not been called
assert not mock.Subscription.cancel.called


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

@ -110,6 +110,7 @@ def create_plan(request):
if plan_id == "P5":
profile.ping_log_limit = 1000
profile.check_limit = 500
profile.team_limit = 9
profile.sms_limit = 50
profile.sms_sent = 0
profile.team_access_allowed = True
@ -117,6 +118,7 @@ def create_plan(request):
elif plan_id == "P50":
profile.ping_log_limit = 1000
profile.check_limit = 500
profile.team_limit = 500
profile.sms_limit = 500
profile.sms_sent = 0
profile.team_access_allowed = True
@ -169,6 +171,7 @@ def cancel_plan(request):
profile = request.user.profile
profile.ping_log_limit = 100
profile.check_limit = 20
profile.team_limit = 2
profile.sms_limit = 0
profile.team_access_allowed = False
profile.save()


+ 0
- 1
hc/test.py View File

@ -15,7 +15,6 @@ class BaseTestCase(TestCase):
self.alice.save()
self.profile = Profile(user=self.alice, api_key="abc")
self.profile.team_access_allowed = True
self.profile.sms_limit = 50
self.profile.save()


+ 9
- 7
templates/accounts/profile.html View File

@ -134,23 +134,25 @@
Share access to your checks and configured integrations
without having to share a login.
</p>
{% if not profile.team_access_allowed %}
<p>
To enable team access, please upgrade to
one of the <a href="{% url 'hc-pricing' %}">paid plans</a>.
</p>
{% endif %}
{% endif %}
<br />
{% if profile.team_access_allowed %}
{% if not profile.can_invite %}
<div class="alert alert-info">
<strong>Team size limit reached.</strong>
To invite more members to your team, please
<a href="{% url 'hc-pricing' %}">upgrade your account!</a>
</div>
{% endif %}
<a
href="#"
class="btn btn-default"
data-toggle="modal"
data-target="#set-team-name-modal">Set Team Name</a>
{% if profile.can_invite %}
<a
href="#"
class="btn btn-primary pull-right"


+ 16
- 13
templates/payments/pricing.html View File

@ -70,7 +70,7 @@
<li class="list-group-item"><i class="fa fa-check"></i> 20 Checks</li>
<li class="list-group-item">100 log entries per check</li>
<li class="list-group-item"><i class="fa fa-check"></i> Personal or Commercial use</li>
<li class="list-group-item">Single User</li>
<li class="list-group-item">3 Team Members</li>
<li class="list-group-item">&nbsp;</li>
<li class="list-group-item">&nbsp;</li>
</ul>
@ -105,7 +105,7 @@
<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">Team Access</li>
<li class="list-group-item">10 Team Members</li>
<li class="list-group-item">50 SMS alerts per month</li>
<li class="list-group-item">Email Support</li>
</ul>
@ -147,7 +147,7 @@
<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">Team Access</li>
<li class="list-group-item">Unlimited Team Members</li>
<li class="list-group-item">500 SMS alerts per month</li>
<li class="list-group-item">Priority Email Support</li>
</ul>
@ -213,6 +213,19 @@
</div>
<div class="col-sm-6">
<h1>Premium Features</h1>
<h2>What's "3 / 10 / Unlimited Team Members"?</h2>
<p>
Invite your colleagues
to your account so they can access your checks,
logs, and configured integrations. Inviting team members
is <strong>more convenient and more secure</strong>
than sharing a single login and password.
</p>
<p>
Each plan has a specific team size limit. When you reach
the limit, you cannot invite more team members.
</p>
<h2>What is the "log entries per check" number?</h2>
<p>
For each of your checks, healthchecks.io keeps a
@ -236,16 +249,6 @@
log entries will only cover 8 hours.
</p>
<h2>What's Team Access?</h2>
<p>
With Team Access enabled, you can "invite" your colleagues
to your account. They will be able to access your checks,
logs, and configured integrations.
</p>
<p>
Team Access is more convenient and more secure than
sharing a single login and password.
</p>
</div>
</div>


Loading…
Cancel
Save