diff --git a/hc/accounts/admin.py b/hc/accounts/admin.py
index 3f1fc81a..81491d59 100644
--- a/hc/accounts/admin.py
+++ b/hc/accounts/admin.py
@@ -3,6 +3,7 @@ from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.template.loader import render_to_string
from django.urls import reverse
+from django.utils.safestring import mark_safe
from hc.accounts.models import Profile
from hc.api.models import Channel, Check
@@ -47,6 +48,7 @@ class ProfileAdmin(admin.ModelAdmin):
fieldsets = (ProfileFieldset.tuple(), TeamFieldset.tuple())
+ @mark_safe
def users(self, obj):
if obj.member_set.count() == 0:
return obj.user.email
@@ -55,6 +57,7 @@ class ProfileAdmin(admin.ModelAdmin):
"profile": obj
})
+ @mark_safe
def checks(self, obj):
num_checks = Check.objects.filter(user=obj.user).count()
pct = 100 * num_checks / max(obj.check_limit, 1)
@@ -68,9 +71,6 @@ class ProfileAdmin(admin.ModelAdmin):
def email(self, obj):
return obj.user.email
- users.allow_tags = True
- checks.allow_tags = True
-
class HcUserAdmin(UserAdmin):
actions = ["send_report"]
diff --git a/hc/accounts/backends.py b/hc/accounts/backends.py
index b0ba119d..b8c08717 100644
--- a/hc/accounts/backends.py
+++ b/hc/accounts/backends.py
@@ -15,7 +15,7 @@ class BasicBackend(object):
# Authenticate against the token in user's profile.
class ProfileBackend(BasicBackend):
- def authenticate(self, username=None, token=None):
+ def authenticate(self, request=None, username=None, token=None):
try:
profiles = Profile.objects.select_related("user")
profile = profiles.get(user__username=username)
@@ -30,7 +30,7 @@ class ProfileBackend(BasicBackend):
class EmailBackend(BasicBackend):
- def authenticate(self, username=None, password=None):
+ def authenticate(self, request=None, username=None, password=None):
try:
user = User.objects.get(email=username)
except User.DoesNotExist:
diff --git a/hc/accounts/migrations/0001_initial.py b/hc/accounts/migrations/0001_initial.py
index a71ce40b..1a85801e 100644
--- a/hc/accounts/migrations/0001_initial.py
+++ b/hc/accounts/migrations/0001_initial.py
@@ -18,7 +18,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
('next_report_date', models.DateTimeField(null=True, blank=True)),
('reports_allowed', models.BooleanField(default=True)),
- ('user', models.OneToOneField(blank=True, to=settings.AUTH_USER_MODEL, null=True)),
+ ('user', models.OneToOneField(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)),
],
),
]
diff --git a/hc/accounts/migrations/0006_profile_current_team.py b/hc/accounts/migrations/0006_profile_current_team.py
index 2d93e974..40cf933f 100644
--- a/hc/accounts/migrations/0006_profile_current_team.py
+++ b/hc/accounts/migrations/0006_profile_current_team.py
@@ -16,6 +16,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='profile',
name='current_team',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.Profile'),
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.Profile'),
),
]
diff --git a/hc/accounts/models.py b/hc/accounts/models.py
index 369fab24..da0f13fc 100644
--- a/hc/accounts/models.py
+++ b/hc/accounts/models.py
@@ -104,5 +104,5 @@ class Profile(models.Model):
class Member(models.Model):
- team = models.ForeignKey(Profile)
- user = models.ForeignKey(User)
+ team = models.ForeignKey(Profile, models.CASCADE)
+ user = models.ForeignKey(User, models.CASCADE)
diff --git a/hc/accounts/tests/test_admin.py b/hc/accounts/tests/test_admin.py
new file mode 100644
index 00000000..48fb6691
--- /dev/null
+++ b/hc/accounts/tests/test_admin.py
@@ -0,0 +1,18 @@
+from hc.test import BaseTestCase
+
+
+class AccountsAdminTestCase(BaseTestCase):
+
+ def setUp(self):
+ super(AccountsAdminTestCase, self).setUp()
+
+ self.alice.is_staff = True
+ self.alice.is_superuser = True
+ self.alice.save()
+
+ def test_it_shows_profiles(self):
+ self.client.login(username="alice@example.org", password="password")
+
+ r = self.client.get("/admin/accounts/profile/")
+ self.assertContains(r, "alice@example.org")
+ self.assertContains(r, "bob@example.org")
diff --git a/hc/api/admin.py b/hc/api/admin.py
index 8b4b40d4..f22ed320 100644
--- a/hc/api/admin.py
+++ b/hc/api/admin.py
@@ -1,6 +1,7 @@
from django.contrib import admin
from django.core.paginator import Paginator
from django.db import connection
+from django.utils.safestring import mark_safe
from hc.api.models import Channel, Check, Notification, Ping
from hc.lib.date import format_duration
@@ -164,14 +165,14 @@ class ChannelsAdmin(admin.ModelAdmin):
def email(self, obj):
return obj.user.email if obj.user else None
+ @mark_safe
def formatted_kind(self, obj):
if obj.kind == "email" and not obj.email_verified:
- return "Email (unverified)"
+ return "Email (unconfirmed)"
return obj.get_kind_display()
formatted_kind.short_description = "Kind"
- formatted_kind.allow_tags = True
def num_notifications(self, obj):
return Notification.objects.filter(channel=obj).count()
diff --git a/hc/api/migrations/0001_initial.py b/hc/api/migrations/0001_initial.py
index 77cd5512..084a1b40 100644
--- a/hc/api/migrations/0001_initial.py
+++ b/hc/api/migrations/0001_initial.py
@@ -19,7 +19,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)),
('code', models.UUIDField(default=uuid.uuid4, editable=False)),
('last_ping', models.DateTimeField(null=True, blank=True)),
- ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
+ ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
),
]
diff --git a/hc/api/migrations/0005_auto_20150630_2021.py b/hc/api/migrations/0005_auto_20150630_2021.py
index 02620d22..40ec1533 100644
--- a/hc/api/migrations/0005_auto_20150630_2021.py
+++ b/hc/api/migrations/0005_auto_20150630_2021.py
@@ -15,6 +15,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='check',
name='user',
- field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True),
+ field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
),
]
diff --git a/hc/api/migrations/0007_ping.py b/hc/api/migrations/0007_ping.py
index b70f4722..9ead6d13 100644
--- a/hc/api/migrations/0007_ping.py
+++ b/hc/api/migrations/0007_ping.py
@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
('method', models.CharField(max_length=10)),
('ua', models.CharField(max_length=100, blank=True)),
('body', models.TextField(blank=True)),
- ('owner', models.ForeignKey(to='api.Check')),
+ ('owner', models.ForeignKey(to='api.Check', on_delete=models.CASCADE)),
],
),
]
diff --git a/hc/api/migrations/0010_channel.py b/hc/api/migrations/0010_channel.py
index 280f2c81..4e03edb0 100644
--- a/hc/api/migrations/0010_channel.py
+++ b/hc/api/migrations/0010_channel.py
@@ -24,7 +24,7 @@ class Migration(migrations.Migration):
('value', models.CharField(max_length=200, blank=True)),
('email_verified', models.BooleanField(default=False)),
('checks', models.ManyToManyField(to='api.Check')),
- ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
+ ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
),
]
diff --git a/hc/api/migrations/0011_notification.py b/hc/api/migrations/0011_notification.py
index 21a079c4..7557363f 100644
--- a/hc/api/migrations/0011_notification.py
+++ b/hc/api/migrations/0011_notification.py
@@ -18,8 +18,8 @@ class Migration(migrations.Migration):
('check_status', models.CharField(max_length=6)),
('created', models.DateTimeField(auto_now_add=True)),
('status', models.IntegerField(default=0)),
- ('channel', models.ForeignKey(to='api.Channel')),
- ('owner', models.ForeignKey(to='api.Check')),
+ ('channel', models.ForeignKey(to='api.Channel', on_delete=models.CASCADE)),
+ ('owner', models.ForeignKey(to='api.Check', on_delete=models.CASCADE)),
],
),
]
diff --git a/hc/api/models.py b/hc/api/models.py
index 4b69ad85..68555e9a 100644
--- a/hc/api/models.py
+++ b/hc/api/models.py
@@ -56,7 +56,7 @@ class Check(models.Model):
name = models.CharField(max_length=100, blank=True)
tags = models.CharField(max_length=500, blank=True)
code = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
- user = models.ForeignKey(User, blank=True, null=True)
+ user = models.ForeignKey(User, models.CASCADE, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
kind = models.CharField(max_length=10, default="simple",
choices=CHECK_KINDS)
@@ -204,7 +204,7 @@ class Check(models.Model):
class Ping(models.Model):
n = models.IntegerField(null=True)
- owner = models.ForeignKey(Check)
+ owner = models.ForeignKey(Check, models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
scheme = models.CharField(max_length=10, default="http")
remote_addr = models.GenericIPAddressField(blank=True, null=True)
@@ -214,7 +214,7 @@ class Ping(models.Model):
class Channel(models.Model):
code = models.UUIDField(default=uuid.uuid4, editable=False)
- user = models.ForeignKey(User)
+ user = models.ForeignKey(User, models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
kind = models.CharField(max_length=20, choices=CHANNEL_KINDS)
value = models.TextField(blank=True)
@@ -378,9 +378,9 @@ class Notification(models.Model):
get_latest_by = "created"
code = models.UUIDField(default=uuid.uuid4, null=True, editable=False)
- owner = models.ForeignKey(Check)
+ owner = models.ForeignKey(Check, models.CASCADE)
check_status = models.CharField(max_length=6)
- channel = models.ForeignKey(Channel)
+ channel = models.ForeignKey(Channel, models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
error = models.CharField(max_length=200, blank=True)
diff --git a/hc/api/tests/test_admin.py b/hc/api/tests/test_admin.py
index 7f55787f..20e0a075 100644
--- a/hc/api/tests/test_admin.py
+++ b/hc/api/tests/test_admin.py
@@ -28,4 +28,4 @@ class ApiAdminTestCase(BaseTestCase):
value="foo@example.org")
r = self.client.get("/admin/api/channel/")
- self.assertContains(r, "Email (unverified)")
+ self.assertContains(r, "Email (unconfirmed)")
diff --git a/hc/api/tests/test_notify.py b/hc/api/tests/test_notify.py
index d9ab80d5..b56e3f4d 100644
--- a/hc/api/tests/test_notify.py
+++ b/hc/api/tests/test_notify.py
@@ -1,6 +1,8 @@
+from datetime import timedelta as td
import json
from django.core import mail
+from django.utils.timezone import now
from hc.api.models import Channel, Check, Notification
from hc.test import BaseTestCase
from mock import patch
@@ -13,6 +15,7 @@ class NotifyTestCase(BaseTestCase):
self.check = Check()
self.check.status = status
self.check.user = self.alice
+ self.check.last_ping = now() - td(minutes=61)
self.check.save()
self.channel = Channel(user=self.alice)
@@ -172,7 +175,7 @@ class NotifyTestCase(BaseTestCase):
payload = kwargs["json"]
attachment = payload["attachments"][0]
fields = {f["title"]: f["value"] for f in attachment["fields"]}
- self.assertEqual(fields["Last Ping"], "Never")
+ self.assertEqual(fields["Last Ping"], "an hour ago")
@patch("hc.api.transports.requests.request")
def test_slack_with_complex_value(self, mock_post):
@@ -280,7 +283,7 @@ class NotifyTestCase(BaseTestCase):
payload = kwargs["json"]
attachment = payload["attachments"][0]
fields = {f["title"]: f["value"] for f in attachment["fields"]}
- self.assertEqual(fields["Last Ping"], "Never")
+ self.assertEqual(fields["Last Ping"], "an hour ago")
@patch("hc.api.transports.requests.request")
def test_pushbullet(self, mock_post):
diff --git a/hc/payments/migrations/0001_initial.py b/hc/payments/migrations/0001_initial.py
index 0f87d3de..48b241a7 100644
--- a/hc/payments/migrations/0001_initial.py
+++ b/hc/payments/migrations/0001_initial.py
@@ -19,7 +19,7 @@ class Migration(migrations.Migration):
('customer_id', models.CharField(blank=True, max_length=36)),
('payment_method_token', models.CharField(blank=True, max_length=35)),
('subscription_id', models.CharField(blank=True, max_length=10)),
- ('user', models.OneToOneField(blank=True, null=True, to=settings.AUTH_USER_MODEL)),
+ ('user', models.OneToOneField(blank=True, null=True, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
),
]
diff --git a/hc/payments/models.py b/hc/payments/models.py
index 6c05ef4c..caf008bb 100644
--- a/hc/payments/models.py
+++ b/hc/payments/models.py
@@ -18,7 +18,7 @@ class SubscriptionManager(models.Manager):
class Subscription(models.Model):
- user = models.OneToOneField(User, blank=True, null=True)
+ user = models.OneToOneField(User, models.CASCADE, blank=True, null=True)
customer_id = models.CharField(max_length=36, blank=True)
payment_method_token = models.CharField(max_length=35, blank=True)
subscription_id = models.CharField(max_length=10, blank=True)
diff --git a/hc/urls.py b/hc/urls.py
index 5b9b7d4e..090dc567 100644
--- a/hc/urls.py
+++ b/hc/urls.py
@@ -5,7 +5,7 @@ from hc.accounts.views import login as hc_login
urlpatterns = [
url(r'^admin/login/', hc_login, {"show_password": True}),
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
url(r'^accounts/', include('hc.accounts.urls')),
url(r'^', include('hc.api.urls')),
url(r'^', include('hc.front.urls')),