Source formatted with Black

This commit is contained in:
Pēteris Caune 2019-05-15 14:27:50 +03:00
parent 1b948f4d5a
commit cdfc9840a7
No known key found for this signature in database
GPG Key ID: E28D7679E9A9EDE2
207 changed files with 2050 additions and 1918 deletions

View File

@ -20,35 +20,58 @@ class Fieldset:
class ProfileFieldset(Fieldset): class ProfileFieldset(Fieldset):
name = "User Profile" name = "User Profile"
fields = ("email", "current_project", "reports_allowed", fields = (
"next_report_date", "nag_period", "next_nag_date", "email",
"deletion_notice_date", "current_project",
"token", "sort") "reports_allowed",
"next_report_date",
"nag_period",
"next_nag_date",
"deletion_notice_date",
"token",
"sort",
)
class TeamFieldset(Fieldset): class TeamFieldset(Fieldset):
name = "Team" name = "Team"
fields = ("team_limit", "check_limit", "ping_log_limit", "sms_limit", fields = (
"sms_sent", "last_sms_date") "team_limit",
"check_limit",
"ping_log_limit",
"sms_limit",
"sms_sent",
"last_sms_date",
)
@admin.register(Profile) @admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin): class ProfileAdmin(admin.ModelAdmin):
class Media: class Media:
css = { css = {"all": ("css/admin/profiles.css",)}
'all': ('css/admin/profiles.css',)
}
readonly_fields = ("user", "email") readonly_fields = ("user", "email")
raw_id_fields = ("current_project", ) raw_id_fields = ("current_project",)
search_fields = ["id", "user__email"] search_fields = ["id", "user__email"]
list_per_page = 50 list_per_page = 50
list_select_related = ("user", ) list_select_related = ("user",)
list_display = ("id", "email", "engagement", "date_joined", "last_login", list_display = (
"projects", "invited", "sms", "reports_allowed") "id",
list_filter = ("user__date_joined", "user__last_login", "email",
"reports_allowed", "check_limit") "engagement",
"date_joined",
"last_login",
"projects",
"invited",
"sms",
"reports_allowed",
)
list_filter = (
"user__date_joined",
"user__last_login",
"reports_allowed",
"check_limit",
)
fieldsets = (ProfileFieldset.tuple(), TeamFieldset.tuple()) fieldsets = (ProfileFieldset.tuple(), TeamFieldset.tuple())
@ -83,23 +106,21 @@ class ProfileAdmin(admin.ModelAdmin):
@mark_safe @mark_safe
def email(self, obj): def email(self, obj):
s = escape(obj.user.email) s = escape(obj.user.email)
if obj.plan: if obj.plan:
return "<span title='%s'>%s</span>" % (obj.plan, s) return "<span title='%s'>%s</span>" % (obj.plan, s)
return s return s
def last_login(self, obj): def last_login(self, obj):
return obj.user.last_login return obj.user.last_login
def date_joined(self, obj): def date_joined(self, obj):
return obj.user.date_joined return obj.user.date_joined
@mark_safe @mark_safe
def projects(self, obj): def projects(self, obj):
return render_to_string("admin/profile_list_projects.html", { return render_to_string("admin/profile_list_projects.html", {"profile": obj})
"profile": obj
})
def invited(self, obj): def invited(self, obj):
return "%d of %d" % (obj.num_members, obj.team_limit) return "%d of %d" % (obj.num_members, obj.team_limit)
@ -111,14 +132,12 @@ class ProfileAdmin(admin.ModelAdmin):
@admin.register(Project) @admin.register(Project)
class ProjectAdmin(admin.ModelAdmin): class ProjectAdmin(admin.ModelAdmin):
readonly_fields = ("code", "owner") readonly_fields = ("code", "owner")
list_select_related = ("owner", ) list_select_related = ("owner",)
list_display = ("id", "name_", "users", "engagement", "switch") list_display = ("id", "name_", "users", "engagement", "switch")
search_fields = ["id", "name", "owner__email"] search_fields = ["id", "name", "owner__email"]
class Media: class Media:
css = { css = {"all": ("css/admin/projects.css",)}
'all': ('css/admin/projects.css',)
}
def get_queryset(self, request): def get_queryset(self, request):
qs = super(ProjectAdmin, self).get_queryset(request) qs = super(ProjectAdmin, self).get_queryset(request)
@ -138,9 +157,7 @@ class ProjectAdmin(admin.ModelAdmin):
if obj.num_members == 0: if obj.num_members == 0:
return obj.owner.email return obj.owner.email
else: else:
return render_to_string("admin/project_list_team.html", { return render_to_string("admin/project_list_team.html", {"project": obj})
"project": obj
})
def email(self, obj): def email(self, obj):
return obj.owner.email return obj.owner.email
@ -173,8 +190,14 @@ class ProjectAdmin(admin.ModelAdmin):
class HcUserAdmin(UserAdmin): class HcUserAdmin(UserAdmin):
actions = ["send_report"] actions = ["send_report"]
list_display = ('id', 'email', 'engagement', 'date_joined', 'last_login', list_display = (
'is_staff') "id",
"email",
"engagement",
"date_joined",
"last_login",
"is_staff",
)
list_display_links = ("id", "email") list_display_links = ("id", "email")
list_filter = ("last_login", "date_joined", "is_staff", "is_active") list_filter = ("last_login", "date_joined", "is_staff", "is_active")

View File

@ -3,11 +3,9 @@ from hc.accounts.models import Profile
class BasicBackend(object): class BasicBackend(object):
def get_user(self, user_id): def get_user(self, user_id):
try: try:
q = User.objects.select_related("profile", q = User.objects.select_related("profile", "profile__current_project")
"profile__current_project")
return q.get(pk=user_id) return q.get(pk=user_id)
except User.DoesNotExist: except User.DoesNotExist:
@ -16,7 +14,6 @@ class BasicBackend(object):
# Authenticate against the token in user's profile. # Authenticate against the token in user's profile.
class ProfileBackend(BasicBackend): class ProfileBackend(BasicBackend):
def authenticate(self, request=None, username=None, token=None): def authenticate(self, request=None, username=None, token=None):
try: try:
profiles = Profile.objects.select_related("user") profiles = Profile.objects.select_related("user")
@ -31,7 +28,6 @@ class ProfileBackend(BasicBackend):
class EmailBackend(BasicBackend): class EmailBackend(BasicBackend):
def authenticate(self, request=None, username=None, password=None): def authenticate(self, request=None, username=None, password=None):
try: try:
user = User.objects.get(email=username) user = User.objects.get(email=username)

View File

@ -6,7 +6,6 @@ from hc.api.models import TokenBucket
class LowercaseEmailField(forms.EmailField): class LowercaseEmailField(forms.EmailField):
def clean(self, value): def clean(self, value):
value = super(LowercaseEmailField, self).clean(value) value = super(LowercaseEmailField, self).clean(value)
return value.lower() return value.lower()
@ -15,12 +14,16 @@ class LowercaseEmailField(forms.EmailField):
class AvailableEmailForm(forms.Form): class AvailableEmailForm(forms.Form):
# Call it "identity" instead of "email" # Call it "identity" instead of "email"
# to avoid some of the dumber bots # to avoid some of the dumber bots
identity = LowercaseEmailField(error_messages={'required': 'Please enter your email address.'}) identity = LowercaseEmailField(
error_messages={"required": "Please enter your email address."}
)
def clean_identity(self): def clean_identity(self):
v = self.cleaned_data["identity"] v = self.cleaned_data["identity"]
if User.objects.filter(email=v).exists(): if User.objects.filter(email=v).exists():
raise forms.ValidationError("An account with this email address already exists.") raise forms.ValidationError(
"An account with this email address already exists."
)
return v return v
@ -48,8 +51,8 @@ class PasswordLoginForm(forms.Form):
password = forms.CharField() password = forms.CharField()
def clean(self): def clean(self):
username = self.cleaned_data.get('email') username = self.cleaned_data.get("email")
password = self.cleaned_data.get('password') password = self.cleaned_data.get("password")
if username and password: if username and password:
if not TokenBucket.authorize_login_password(username): if not TokenBucket.authorize_login_password(username):

View File

@ -51,10 +51,7 @@ class Command(BaseCommand):
profile.deletion_notice_date = now() profile.deletion_notice_date = now()
profile.save() profile.save()
ctx = { ctx = {"email": profile.user.email, "support_email": settings.SUPPORT_EMAIL}
"email": profile.user.email,
"support_email": settings.SUPPORT_EMAIL
}
emails.deletion_notice(profile.user.email, ctx) emails.deletion_notice(profile.user.email, ctx)
# Throttle so we don't send too many emails at once: # Throttle so we don't send too many emails at once:
time.sleep(1) time.sleep(1)

View File

@ -7,18 +7,32 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Profile', name="Profile",
fields=[ fields=[
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)), (
('next_report_date', models.DateTimeField(null=True, blank=True)), "id",
('reports_allowed', models.BooleanField(default=True)), models.AutoField(
('user', models.OneToOneField(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)), 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,
on_delete=models.CASCADE,
),
),
], ],
), )
] ]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0001_initial")]
('accounts', '0001_initial'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='ping_log_limit', name="ping_log_limit",
field=models.IntegerField(default=100), field=models.IntegerField(default=100),
), )
] ]

View File

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0002_profile_ping_log_limit")]
('accounts', '0002_profile_ping_log_limit'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='token', name="token",
field=models.CharField(blank=True, max_length=128), field=models.CharField(blank=True, max_length=128),
), )
] ]

View File

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0003_profile_token")]
('accounts', '0003_profile_token'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='api_key', name="api_key",
field=models.CharField(blank=True, max_length=128), field=models.CharField(blank=True, max_length=128),
), )
] ]

View File

@ -11,34 +11,46 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0004_profile_api_key'), ("accounts", "0004_profile_api_key"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Member', name="Member",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
)
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='team_access_allowed', name="team_access_allowed",
field=models.BooleanField(default=False), field=models.BooleanField(default=False),
), ),
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='team_name', name="team_name",
field=models.CharField(blank=True, max_length=200), field=models.CharField(blank=True, max_length=200),
), ),
migrations.AddField( migrations.AddField(
model_name='member', model_name="member",
name='team', name="team",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.Profile'), field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="accounts.Profile"
),
), ),
migrations.AddField( migrations.AddField(
model_name='member', model_name="member",
name='user', name="user",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
), ),
] ]

View File

@ -8,14 +8,16 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0005_auto_20160509_0801")]
('accounts', '0005_auto_20160509_0801'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='current_team', name="current_team",
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.Profile'), field=models.ForeignKey(
), null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="accounts.Profile",
),
)
] ]

View File

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0006_profile_current_team")]
('accounts', '0006_profile_current_team'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='check_limit', name="check_limit",
field=models.IntegerField(default=20), field=models.IntegerField(default=20),
), )
] ]

View File

@ -7,14 +7,10 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0007_profile_check_limit")]
('accounts', '0007_profile_check_limit'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile", name="bill_to", field=models.TextField(blank=True)
name='bill_to', )
field=models.TextField(blank=True),
),
] ]

View File

@ -7,24 +7,18 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0008_profile_bill_to")]
('accounts', '0008_profile_bill_to'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='last_sms_date', name="last_sms_date",
field=models.DateTimeField(blank=True, null=True), field=models.DateTimeField(blank=True, null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile", name="sms_limit", field=models.IntegerField(default=0)
name='sms_limit',
field=models.IntegerField(default=0),
), ),
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile", name="sms_sent", field=models.IntegerField(default=0)
name='sms_sent',
field=models.IntegerField(default=0),
), ),
] ]

View File

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0009_auto_20170714_1734")]
('accounts', '0009_auto_20170714_1734'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='team_limit', name="team_limit",
field=models.IntegerField(default=2), field=models.IntegerField(default=2),
), )
] ]

View File

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0010_profile_team_limit")]
('accounts', '0010_profile_team_limit'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='sort', name="sort",
field=models.CharField(default='created', max_length=20), field=models.CharField(default="created", max_length=20),
), )
] ]

View File

@ -8,19 +8,24 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0011_profile_sort")]
('accounts', '0011_profile_sort'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='nag_period', name="nag_period",
field=models.DurationField(choices=[(datetime.timedelta(0), 'Disabled'), (datetime.timedelta(0, 3600), 'Hourly'), (datetime.timedelta(1), 'Daily')], default=datetime.timedelta(0)), field=models.DurationField(
choices=[
(datetime.timedelta(0), "Disabled"),
(datetime.timedelta(0, 3600), "Hourly"),
(datetime.timedelta(1), "Daily"),
],
default=datetime.timedelta(0),
),
), ),
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='next_nag_date', name="next_nag_date",
field=models.DateTimeField(blank=True, null=True), field=models.DateTimeField(blank=True, null=True),
), ),
] ]

View File

@ -7,13 +7,8 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0012_auto_20171014_1002")]
('accounts', '0012_auto_20171014_1002'),
]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(model_name="profile", name="team_access_allowed")
model_name='profile',
name='team_access_allowed',
),
] ]

View File

@ -9,14 +9,16 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0013_remove_profile_team_access_allowed")]
('accounts', '0013_remove_profile_team_access_allowed'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='member', model_name="member",
name='user', name="user",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberships', to=settings.AUTH_USER_MODEL), field=models.ForeignKey(
), on_delete=django.db.models.deletion.CASCADE,
related_name="memberships",
to=settings.AUTH_USER_MODEL,
),
)
] ]

View File

@ -5,19 +5,17 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0014_auto_20171227_1530")]
('accounts', '0014_auto_20171227_1530'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='api_key_id', name="api_key_id",
field=models.CharField(blank=True, max_length=128), field=models.CharField(blank=True, max_length=128),
), ),
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='api_key_readonly', name="api_key_readonly",
field=models.CharField(blank=True, max_length=128), field=models.CharField(blank=True, max_length=128),
), ),
] ]

View File

@ -5,13 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0015_auto_20181029_1858")]
('accounts', '0015_auto_20181029_1858'),
]
operations = [ operations = [migrations.RemoveField(model_name="profile", name="bill_to")]
migrations.RemoveField(
model_name='profile',
name='bill_to',
),
]

View File

@ -10,29 +10,54 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0016_remove_profile_bill_to'), ("accounts", "0016_remove_profile_bill_to"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Project', name="Project",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('code', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), "id",
('name', models.CharField(blank=True, max_length=200)), models.AutoField(
('api_key', models.CharField(blank=True, max_length=128)), auto_created=True,
('api_key_readonly', models.CharField(blank=True, max_length=128)), primary_key=True,
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), serialize=False,
verbose_name="ID",
),
),
(
"code",
models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
),
("name", models.CharField(blank=True, max_length=200)),
("api_key", models.CharField(blank=True, max_length=128)),
("api_key_readonly", models.CharField(blank=True, max_length=128)),
(
"owner",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='member', model_name="member",
name='project', name="project",
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.Project'), field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="accounts.Project",
),
), ),
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='current_project', name="current_project",
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.Project'), field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="accounts.Project",
),
), ),
] ]

View File

@ -28,10 +28,6 @@ def create_projects(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0017_auto_20190112_1426")]
('accounts', '0017_auto_20190112_1426'),
]
operations = [ operations = [migrations.RunPython(create_projects, migrations.RunPython.noop)]
migrations.RunPython(create_projects, migrations.RunPython.noop),
]

View File

@ -5,14 +5,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0018_auto_20190112_1426")]
('accounts', '0018_auto_20190112_1426'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='project', model_name="project",
name='badge_key', name="badge_key",
field=models.CharField(blank=True, max_length=150, null=True), field=models.CharField(blank=True, max_length=150, null=True),
), )
] ]

View File

@ -12,10 +12,6 @@ def set_badge_key(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0019_project_badge_key")]
('accounts', '0019_project_badge_key'),
]
operations = [ operations = [migrations.RunPython(set_badge_key, migrations.RunPython.noop)]
migrations.RunPython(set_badge_key, migrations.RunPython.noop),
]

View File

@ -5,14 +5,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0020_auto_20190112_1950")]
('accounts', '0020_auto_20190112_1950'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='project', model_name="project",
name='badge_key', name="badge_key",
field=models.CharField(max_length=150, unique=True), field=models.CharField(max_length=150, unique=True),
), )
] ]

View File

@ -6,14 +6,14 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0021_auto_20190112_2005")]
('accounts', '0021_auto_20190112_2005'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='member', model_name="member",
name='project', name="project",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.Project'), field=models.ForeignKey(
), on_delete=django.db.models.deletion.CASCADE, to="accounts.Project"
),
)
] ]

View File

@ -5,21 +5,10 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0022_auto_20190114_0857")]
('accounts', '0022_auto_20190114_0857'),
]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(model_name="profile", name="api_key"),
model_name='profile', migrations.RemoveField(model_name="profile", name="api_key_id"),
name='api_key', migrations.RemoveField(model_name="profile", name="api_key_readonly"),
),
migrations.RemoveField(
model_name='profile',
name='api_key_id',
),
migrations.RemoveField(
model_name='profile',
name='api_key_readonly',
),
] ]

View File

@ -5,17 +5,9 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0023_auto_20190117_1419")]
('accounts', '0023_auto_20190117_1419'),
]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(model_name="profile", name="current_team"),
model_name='profile', migrations.RemoveField(model_name="profile", name="team_name"),
name='current_team',
),
migrations.RemoveField(
model_name='profile',
name='team_name',
),
] ]

View File

@ -5,13 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0024_auto_20190119_1540")]
('accounts', '0024_auto_20190119_1540'),
]
operations = [ operations = [migrations.RemoveField(model_name="member", name="team")]
migrations.RemoveField(
model_name='member',
name='team',
),
]

View File

@ -6,24 +6,22 @@ import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0025_remove_member_team")]
('accounts', '0025_remove_member_team'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='project', model_name="project",
name='api_key', name="api_key",
field=models.CharField(blank=True, db_index=True, max_length=128), field=models.CharField(blank=True, db_index=True, max_length=128),
), ),
migrations.AlterField( migrations.AlterField(
model_name='project', model_name="project",
name='api_key_readonly', name="api_key_readonly",
field=models.CharField(blank=True, db_index=True, max_length=128), field=models.CharField(blank=True, db_index=True, max_length=128),
), ),
migrations.AlterField( migrations.AlterField(
model_name='project', model_name="project",
name='code', name="code",
field=models.UUIDField(default=uuid.uuid4, unique=True), field=models.UUIDField(default=uuid.uuid4, unique=True),
), ),
] ]

View File

@ -5,14 +5,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("accounts", "0026_auto_20190204_2042")]
('accounts', '0026_auto_20190204_2042'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='profile', model_name="profile",
name='deletion_notice_date', name="deletion_notice_date",
field=models.DateTimeField(blank=True, null=True), field=models.DateTimeField(blank=True, null=True),
), )
] ]

View File

@ -15,9 +15,11 @@ from hc.lib import emails
NO_NAG = timedelta() NO_NAG = timedelta()
NAG_PERIODS = ((NO_NAG, "Disabled"), NAG_PERIODS = (
(timedelta(hours=1), "Hourly"), (NO_NAG, "Disabled"),
(timedelta(days=1), "Daily")) (timedelta(hours=1), "Hourly"),
(timedelta(days=1), "Daily"),
)
def month(dt): def month(dt):
@ -90,26 +92,20 @@ class Profile(models.Model):
ctx = { ctx = {
"button_text": "Sign In", "button_text": "Sign In",
"button_url": settings.SITE_ROOT + path, "button_url": settings.SITE_ROOT + path,
"inviting_project": inviting_project "inviting_project": inviting_project,
} }
emails.login(self.user.email, ctx) emails.login(self.user.email, ctx)
def send_set_password_link(self): def send_set_password_link(self):
token = self.prepare_token("set-password") token = self.prepare_token("set-password")
path = reverse("hc-set-password", args=[token]) path = reverse("hc-set-password", args=[token])
ctx = { ctx = {"button_text": "Set Password", "button_url": settings.SITE_ROOT + path}
"button_text": "Set Password",
"button_url": settings.SITE_ROOT + path
}
emails.set_password(self.user.email, ctx) emails.set_password(self.user.email, ctx)
def send_change_email_link(self): def send_change_email_link(self):
token = self.prepare_token("change-email") token = self.prepare_token("change-email")
path = reverse("hc-change-email", args=[token]) path = reverse("hc-change-email", args=[token])
ctx = { ctx = {"button_text": "Change Email", "button_url": settings.SITE_ROOT + path}
"button_text": "Change Email",
"button_url": settings.SITE_ROOT + path
}
emails.change_email(self.user.email, ctx) emails.change_email(self.user.email, ctx)
def projects(self): def projects(self):
@ -140,6 +136,7 @@ class Profile(models.Model):
project_ids = self.projects().values("id") project_ids = self.projects().values("id")
from hc.api.models import Check from hc.api.models import Check
return Check.objects.filter(project_id__in=project_ids) return Check.objects.filter(project_id__in=project_ids)
def send_report(self, nag=False): def send_report(self, nag=False):
@ -168,10 +165,7 @@ class Profile(models.Model):
unsub_url = self.reports_unsub_url() unsub_url = self.reports_unsub_url()
headers = { headers = {"List-Unsubscribe": unsub_url, "X-Bounce-Url": unsub_url}
"List-Unsubscribe": unsub_url,
"X-Bounce-Url": unsub_url
}
ctx = { ctx = {
"checks": checks, "checks": checks,
@ -181,7 +175,7 @@ class Profile(models.Model):
"notifications_url": self.notifications_url(), "notifications_url": self.notifications_url(),
"nag": nag, "nag": nag,
"nag_period": self.nag_period.total_seconds(), "nag_period": self.nag_period.total_seconds(),
"num_down": num_down "num_down": num_down,
} }
emails.report(self.user.email, ctx, headers) emails.report(self.user.email, ctx, headers)
@ -228,6 +222,7 @@ class Project(models.Model):
def num_checks_available(self): def num_checks_available(self):
from hc.api.models import Check from hc.api.models import Check
num_used = Check.objects.filter(project__owner=self.owner).count() num_used = Check.objects.filter(project__owner=self.owner).count()
return self.owner_profile.check_limit - num_used return self.owner_profile.check_limit - num_used

View File

@ -3,7 +3,6 @@ from hc.test import BaseTestCase
class RemoveProjectTestCase(BaseTestCase): class RemoveProjectTestCase(BaseTestCase):
def setUp(self): def setUp(self):
super(RemoveProjectTestCase, self).setUp() super(RemoveProjectTestCase, self).setUp()

View File

@ -2,7 +2,6 @@ from hc.test import BaseTestCase
class AccountsAdminTestCase(BaseTestCase): class AccountsAdminTestCase(BaseTestCase):
def setUp(self): def setUp(self):
super(AccountsAdminTestCase, self).setUp() super(AccountsAdminTestCase, self).setUp()

View File

@ -4,7 +4,6 @@ from hc.test import BaseTestCase
class ChangeEmailTestCase(BaseTestCase): class ChangeEmailTestCase(BaseTestCase):
def test_it_shows_form(self): def test_it_shows_form(self):
self.profile.token = make_password("foo", "change-email") self.profile.token = make_password("foo", "change-email")
self.profile.save() self.profile.save()

View File

@ -3,7 +3,6 @@ from hc.test import BaseTestCase
class CheckTokenTestCase(BaseTestCase): class CheckTokenTestCase(BaseTestCase):
def setUp(self): def setUp(self):
super(CheckTokenTestCase, self).setUp() super(CheckTokenTestCase, self).setUp()
self.profile.token = make_password("secret-token", "login") self.profile.token = make_password("secret-token", "login")

View File

@ -6,7 +6,6 @@ from mock import patch
class CloseAccountTestCase(BaseTestCase): class CloseAccountTestCase(BaseTestCase):
@patch("hc.payments.models.Subscription.cancel") @patch("hc.payments.models.Subscription.cancel")
def test_it_works(self, mock_cancel): def test_it_works(self, mock_cancel):
Check.objects.create(project=self.project, tags="foo a-B_1 baz@") Check.objects.create(project=self.project, tags="foo a-B_1 baz@")

View File

@ -6,7 +6,6 @@ from hc.test import BaseTestCase
class LoginTestCase(BaseTestCase): class LoginTestCase(BaseTestCase):
def setUp(self): def setUp(self):
super(LoginTestCase, self).setUp() super(LoginTestCase, self).setUp()
self.checks_url = "/projects/%s/checks/" % self.project.code self.checks_url = "/projects/%s/checks/" % self.project.code
@ -63,11 +62,7 @@ class LoginTestCase(BaseTestCase):
self.assertIn("login", self.profile.token) self.assertIn("login", self.profile.token)
def test_it_handles_password(self): def test_it_handles_password(self):
form = { form = {"action": "login", "email": "alice@example.org", "password": "password"}
"action": "login",
"email": "alice@example.org",
"password": "password"
}
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)
self.assertRedirects(r, self.checks_url) self.assertRedirects(r, self.checks_url)
@ -79,11 +74,7 @@ class LoginTestCase(BaseTestCase):
obj.tokens = 0 obj.tokens = 0
obj.save() obj.save()
form = { form = {"action": "login", "email": "alice@example.org", "password": "password"}
"action": "login",
"email": "alice@example.org",
"password": "password"
}
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)
self.assertContains(r, "Too many attempts") self.assertContains(r, "Too many attempts")
@ -91,27 +82,16 @@ class LoginTestCase(BaseTestCase):
def test_it_handles_password_login_with_redirect(self): def test_it_handles_password_login_with_redirect(self):
check = Check.objects.create(project=self.project) check = Check.objects.create(project=self.project)
form = { form = {"action": "login", "email": "alice@example.org", "password": "password"}
"action": "login",
"email": "alice@example.org",
"password": "password"
}
samples = [ samples = ["/integrations/add_slack/", "/checks/%s/details/" % check.code]
"/integrations/add_slack/",
"/checks/%s/details/" % check.code
]
for s in samples: for s in samples:
r = self.client.post("/accounts/login/?next=%s" % s, form) r = self.client.post("/accounts/login/?next=%s" % s, form)
self.assertRedirects(r, s) self.assertRedirects(r, s)
def test_it_handles_bad_next_parameter(self): def test_it_handles_bad_next_parameter(self):
form = { form = {"action": "login", "email": "alice@example.org", "password": "password"}
"action": "login",
"email": "alice@example.org",
"password": "password"
}
r = self.client.post("/accounts/login/?next=/evil/", form) r = self.client.post("/accounts/login/?next=/evil/", form)
self.assertRedirects(r, self.checks_url) self.assertRedirects(r, self.checks_url)
@ -120,7 +100,7 @@ class LoginTestCase(BaseTestCase):
form = { form = {
"action": "login", "action": "login",
"email": "alice@example.org", "email": "alice@example.org",
"password": "wrong password" "password": "wrong password",
} }
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)

View File

@ -5,7 +5,6 @@ from hc.test import BaseTestCase
class NotificationsTestCase(BaseTestCase): class NotificationsTestCase(BaseTestCase):
def test_it_saves_reports_allowed_true(self): def test_it_saves_reports_allowed_true(self):
self.profile.reports_allowed = False self.profile.reports_allowed = False
self.profile.save() self.profile.save()

View File

@ -8,7 +8,6 @@ from hc.api.models import Check
class ProfileTestCase(BaseTestCase): class ProfileTestCase(BaseTestCase):
def test_it_sends_set_password_link(self): def test_it_sends_set_password_link(self):
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
@ -38,7 +37,7 @@ class ProfileTestCase(BaseTestCase):
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
message = mail.outbox[0] message = mail.outbox[0]
self.assertEqual(message.subject, 'Monthly Report') self.assertEqual(message.subject, "Monthly Report")
self.assertIn("Test Check", message.body) self.assertIn("Test Check", message.body)
def test_it_skips_report_if_no_pings(self): def test_it_skips_report_if_no_pings(self):
@ -76,7 +75,7 @@ class ProfileTestCase(BaseTestCase):
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
message = mail.outbox[0] message = mail.outbox[0]
self.assertEqual(message.subject, 'Reminder: 1 check still down') self.assertEqual(message.subject, "Reminder: 1 check still down")
self.assertIn("Test Check", message.body) self.assertIn("Test Check", message.body)
def test_it_skips_nag_if_none_down(self): def test_it_skips_nag_if_none_down(self):

View File

@ -72,8 +72,9 @@ class ProjectTestCase(BaseTestCase):
members = self.project.member_set.all() members = self.project.member_set.all()
self.assertEqual(members.count(), 2) self.assertEqual(members.count(), 2)
member = Member.objects.get(project=self.project, member = Member.objects.get(
user__email="frank@example.org") project=self.project, user__email="frank@example.org"
)
profile = member.user.profile profile = member.user.profile
self.assertEqual(profile.current_project, self.project) self.assertEqual(profile.current_project, self.project)
@ -81,8 +82,10 @@ class ProjectTestCase(BaseTestCase):
self.assertFalse(member.user.project_set.exists()) self.assertFalse(member.user.project_set.exists())
# And an email should have been sent # And an email should have been sent
subj = ("You have been invited to join" subj = (
" Alice&#39;s Project on %s" % settings.SITE_NAME) "You have been invited to join"
" Alice&#39;s Project on %s" % settings.SITE_NAME
)
self.assertEqual(mail.outbox[0].subject, subj) self.assertEqual(mail.outbox[0].subject, subj)
@override_settings(SECRET_KEY="test-secret") @override_settings(SECRET_KEY="test-secret")

View File

@ -4,7 +4,6 @@ from hc.api.models import Check
class ProjectModelTestCase(BaseTestCase): class ProjectModelTestCase(BaseTestCase):
def test_num_checks_available_handles_multiple_projects(self): def test_num_checks_available_handles_multiple_projects(self):
# One check in Alice's primary project: # One check in Alice's primary project:
Check.objects.create(project=self.project) Check.objects.create(project=self.project)

View File

@ -3,7 +3,6 @@ from hc.test import BaseTestCase
class RemoveProjectTestCase(BaseTestCase): class RemoveProjectTestCase(BaseTestCase):
def setUp(self): def setUp(self):
super(RemoveProjectTestCase, self).setUp() super(RemoveProjectTestCase, self).setUp()

View File

@ -2,7 +2,6 @@ from hc.test import BaseTestCase
class SetPasswordTestCase(BaseTestCase): class SetPasswordTestCase(BaseTestCase):
def test_it_shows_form(self): def test_it_shows_form(self):
token = self.profile.prepare_token("set-password") token = self.profile.prepare_token("set-password")

View File

@ -8,7 +8,6 @@ from django.conf import settings
class SignupTestCase(TestCase): class SignupTestCase(TestCase):
def test_it_sends_link(self): def test_it_sends_link(self):
form = {"identity": "alice@example.org"} form = {"identity": "alice@example.org"}

View File

@ -4,7 +4,6 @@ from hc.accounts.models import Profile
class TeamAccessMiddlewareTestCase(TestCase): class TeamAccessMiddlewareTestCase(TestCase):
def test_it_handles_missing_profile(self): def test_it_handles_missing_profile(self):
user = User(username="ned", email="ned@example.org") user = User(username="ned", email="ned@example.org")
user.set_password("password") user.set_password("password")

View File

@ -6,7 +6,6 @@ from hc.test import BaseTestCase
class UnsubscribeReportsTestCase(BaseTestCase): class UnsubscribeReportsTestCase(BaseTestCase):
def test_it_unsubscribes(self): def test_it_unsubscribes(self):
self.profile.next_report_date = now() self.profile.next_report_date = now()
self.profile.nag_period = td(hours=1) self.profile.nag_period = td(hours=1)

View File

@ -2,32 +2,25 @@ from django.urls import path
from hc.accounts import views from hc.accounts import views
urlpatterns = [ urlpatterns = [
path('login/', views.login, name="hc-login"), path("login/", views.login, name="hc-login"),
path('logout/', views.logout, name="hc-logout"), path("logout/", views.logout, name="hc-logout"),
path('signup/', views.signup, name="hc-signup"), path("signup/", views.signup, name="hc-signup"),
path('login_link_sent/', path("login_link_sent/", views.login_link_sent, name="hc-login-link-sent"),
views.login_link_sent, name="hc-login-link-sent"), path("link_sent/", views.link_sent, name="hc-link-sent"),
path(
path('link_sent/', "check_token/<slug:username>/<slug:token>/",
views.link_sent, name="hc-link-sent"), views.check_token,
name="hc-check-token",
path('check_token/<slug:username>/<slug:token>/', ),
views.check_token, name="hc-check-token"), path("profile/", views.profile, name="hc-profile"),
path("profile/notifications/", views.notifications, name="hc-notifications"),
path('profile/', views.profile, name="hc-profile"), path("close/", views.close, name="hc-close"),
path('profile/notifications/', views.notifications, name="hc-notifications"), path(
path('close/', views.close, name="hc-close"), "unsubscribe_reports/<str:username>/",
views.unsubscribe_reports,
path('unsubscribe_reports/<str:username>/', name="hc-unsubscribe-reports",
views.unsubscribe_reports, name="hc-unsubscribe-reports"), ),
path("set_password/<slug:token>/", views.set_password, name="hc-set-password"),
path('set_password/<slug:token>/', path("change_email/done/", views.change_email_done, name="hc-change-email-done"),
views.set_password, name="hc-set-password"), path("change_email/<slug:token>/", views.change_email, name="hc-change-email"),
path('change_email/done/',
views.change_email_done, name="hc-change-email-done"),
path('change_email/<slug:token>/',
views.change_email, name="hc-change-email"),
] ]

View File

@ -9,28 +9,39 @@ from django.contrib.auth import authenticate
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core import signing from django.core import signing
from django.http import (HttpResponseForbidden, HttpResponseBadRequest, from django.http import (
HttpResponseNotFound) HttpResponseForbidden,
HttpResponseBadRequest,
HttpResponseNotFound,
)
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.timezone import now from django.utils.timezone import now
from django.urls import resolve, Resolver404 from django.urls import resolve, Resolver404
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from hc.accounts.forms import (ChangeEmailForm, PasswordLoginForm, from hc.accounts.forms import (
InviteTeamMemberForm, RemoveTeamMemberForm, ChangeEmailForm,
ReportSettingsForm, SetPasswordForm, PasswordLoginForm,
ProjectNameForm, AvailableEmailForm, InviteTeamMemberForm,
EmailLoginForm) RemoveTeamMemberForm,
ReportSettingsForm,
SetPasswordForm,
ProjectNameForm,
AvailableEmailForm,
EmailLoginForm,
)
from hc.accounts.models import Profile, Project, Member from hc.accounts.models import Profile, Project, Member
from hc.api.models import Channel, Check, TokenBucket from hc.api.models import Channel, Check, TokenBucket
from hc.payments.models import Subscription from hc.payments.models import Subscription
NEXT_WHITELIST = ("hc-checks", NEXT_WHITELIST = (
"hc-details", "hc-checks",
"hc-log", "hc-details",
"hc-channels", "hc-log",
"hc-add-slack", "hc-channels",
"hc-add-pushover") "hc-add-slack",
"hc-add-pushover",
)
def _is_whitelisted(path): def _is_whitelisted(path):
@ -92,7 +103,7 @@ def login(request):
form = PasswordLoginForm() form = PasswordLoginForm()
magic_form = EmailLoginForm() magic_form = EmailLoginForm()
if request.method == 'POST': if request.method == "POST":
if request.POST.get("action") == "login": if request.POST.get("action") == "login":
form = PasswordLoginForm(request.POST) form = PasswordLoginForm(request.POST)
if form.is_valid(): if form.is_valid():
@ -115,7 +126,7 @@ def login(request):
"page": "login", "page": "login",
"form": form, "form": form,
"magic_form": magic_form, "magic_form": magic_form,
"bad_link": bad_link "bad_link": bad_link,
} }
return render(request, "accounts/login.html", ctx) return render(request, "accounts/login.html", ctx)
@ -182,11 +193,7 @@ def check_token(request, username, token):
def profile(request): def profile(request):
profile = request.profile profile = request.profile
ctx = { ctx = {"page": "profile", "profile": profile, "my_projects_status": "default"}
"page": "profile",
"profile": profile,
"my_projects_status": "default"
}
if request.method == "POST": if request.method == "POST":
if "change_email" in request.POST: if "change_email" in request.POST:
@ -198,8 +205,7 @@ def profile(request):
elif "leave_project" in request.POST: elif "leave_project" in request.POST:
code = request.POST["code"] code = request.POST["code"]
try: try:
project = Project.objects.get(code=code, project = Project.objects.get(code=code, member__user=request.user)
member__user=request.user)
except Project.DoesNotExist: except Project.DoesNotExist:
return HttpResponseBadRequest() return HttpResponseBadRequest()
@ -253,7 +259,7 @@ def project(request, code):
"show_api_keys": "show_api_keys" in request.GET, "show_api_keys": "show_api_keys" in request.GET,
"project_name_status": "default", "project_name_status": "default",
"api_status": "default", "api_status": "default",
"team_status": "default" "team_status": "default",
} }
if request.method == "POST": if request.method == "POST":
@ -308,8 +314,7 @@ def project(request, code):
farewell_user.profile.current_project = None farewell_user.profile.current_project = None
farewell_user.profile.save() farewell_user.profile.save()
Member.objects.filter(project=project, Member.objects.filter(project=project, user=farewell_user).delete()
user=farewell_user).delete()
ctx["team_member_removed"] = form.cleaned_data["email"] ctx["team_member_removed"] = form.cleaned_data["email"]
ctx["team_status"] = "info" ctx["team_status"] = "info"
@ -335,11 +340,7 @@ def project(request, code):
def notifications(request): def notifications(request):
profile = request.profile profile = request.profile
ctx = { ctx = {"status": "default", "page": "profile", "profile": profile}
"status": "default",
"page": "profile",
"profile": profile
}
if request.method == "POST": if request.method == "POST":
form = ReportSettingsForm(request.POST) form = ReportSettingsForm(request.POST)

View File

@ -9,18 +9,23 @@ from hc.lib.date import format_duration
@admin.register(Check) @admin.register(Check)
class ChecksAdmin(admin.ModelAdmin): class ChecksAdmin(admin.ModelAdmin):
class Media: class Media:
css = { css = {"all": ("css/admin/checks.css",)}
'all': ('css/admin/checks.css',)
}
search_fields = ["name", "code", "project__owner__email"] search_fields = ["name", "code", "project__owner__email"]
raw_id_fields = ("project", ) raw_id_fields = ("project",)
list_display = ("id", "name_tags", "email", "created", "n_pings", list_display = (
"timeout_schedule", "status", "last_start", "last_ping") "id",
list_filter = ("status", "kind", "last_ping", "name_tags",
"last_start") "email",
"created",
"n_pings",
"timeout_schedule",
"status",
"last_start",
"last_ping",
)
list_filter = ("status", "kind", "last_ping", "last_start")
actions = ["send_alert"] actions = ["send_alert"]
@ -60,14 +65,10 @@ class ChecksAdmin(admin.ModelAdmin):
class SchemeListFilter(admin.SimpleListFilter): class SchemeListFilter(admin.SimpleListFilter):
title = "Scheme" title = "Scheme"
parameter_name = 'scheme' parameter_name = "scheme"
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
return ( return (("http", "HTTP"), ("https", "HTTPS"), ("email", "Email"))
('http', "HTTP"),
('https', "HTTPS"),
('email', "Email"),
)
def queryset(self, request, queryset): def queryset(self, request, queryset):
if self.value(): if self.value():
@ -77,7 +78,7 @@ class SchemeListFilter(admin.SimpleListFilter):
class MethodListFilter(admin.SimpleListFilter): class MethodListFilter(admin.SimpleListFilter):
title = "Method" title = "Method"
parameter_name = 'method' parameter_name = "method"
methods = ["HEAD", "GET", "POST", "PUT", "DELETE"] methods = ["HEAD", "GET", "POST", "PUT", "DELETE"]
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
@ -91,7 +92,7 @@ class MethodListFilter(admin.SimpleListFilter):
class KindListFilter(admin.SimpleListFilter): class KindListFilter(admin.SimpleListFilter):
title = "Kind" title = "Kind"
parameter_name = 'kind' parameter_name = "kind"
kinds = ["start", "fail"] kinds = ["start", "fail"]
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
@ -112,8 +113,10 @@ class LargeTablePaginator(Paginator):
def _get_estimate(self): def _get_estimate(self):
try: try:
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("SELECT reltuples FROM pg_class WHERE relname = %s", cursor.execute(
[self.object_list.query.model._meta.db_table]) "SELECT reltuples FROM pg_class WHERE relname = %s",
[self.object_list.query.model._meta.db_table],
)
return int(cursor.fetchone()[0]) return int(cursor.fetchone()[0])
except: except:
return 0 return 0
@ -138,18 +141,17 @@ class LargeTablePaginator(Paginator):
# (i.e. is of type list). # (i.e. is of type list).
self._count = len(self.object_list) self._count = len(self.object_list)
return self._count return self._count
count = property(_get_count) count = property(_get_count)
@admin.register(Ping) @admin.register(Ping)
class PingsAdmin(admin.ModelAdmin): class PingsAdmin(admin.ModelAdmin):
search_fields = ("owner__name", "owner__code") search_fields = ("owner__name", "owner__code")
readonly_fields = ("owner", ) readonly_fields = ("owner",)
list_select_related = ("owner", ) list_select_related = ("owner",)
list_display = ("id", "created", "owner", "scheme", "method", list_display = ("id", "created", "owner", "scheme", "method", "ua")
"ua") list_filter = ("created", SchemeListFilter, MethodListFilter, KindListFilter)
list_filter = ("created", SchemeListFilter, MethodListFilter,
KindListFilter)
paginator = LargeTablePaginator paginator = LargeTablePaginator
show_full_result_count = False show_full_result_count = False
@ -158,15 +160,19 @@ class PingsAdmin(admin.ModelAdmin):
@admin.register(Channel) @admin.register(Channel)
class ChannelsAdmin(admin.ModelAdmin): class ChannelsAdmin(admin.ModelAdmin):
class Media: class Media:
css = { css = {"all": ("css/admin/channels.css",)}
'all': ('css/admin/channels.css',)
}
search_fields = ["value", "project__owner__email"] search_fields = ["value", "project__owner__email"]
list_display = ("id", "name", "email", "formatted_kind", "value", list_display = (
"num_notifications") "id",
list_filter = ("kind", ) "name",
raw_id_fields = ("project", "checks", ) "email",
"formatted_kind",
"value",
"num_notifications",
)
list_filter = ("kind",)
raw_id_fields = ("project", "checks")
def get_queryset(self, request): def get_queryset(self, request):
qs = super().get_queryset(request) qs = super().get_queryset(request)
@ -196,8 +202,14 @@ class ChannelsAdmin(admin.ModelAdmin):
class NotificationsAdmin(admin.ModelAdmin): class NotificationsAdmin(admin.ModelAdmin):
search_fields = ["owner__name", "owner__code", "channel__value"] search_fields = ["owner__name", "owner__code", "channel__value"]
list_select_related = ("owner", "channel") list_select_related = ("owner", "channel")
list_display = ("id", "created", "check_status", "owner", list_display = (
"channel_kind", "channel_value") "id",
"created",
"check_status",
"owner",
"channel_kind",
"channel_value",
)
list_filter = ("created", "check_status", "channel__kind") list_filter = ("created", "check_status", "channel__kind")
def channel_kind(self, obj): def channel_kind(self, obj):
@ -209,6 +221,5 @@ class NotificationsAdmin(admin.ModelAdmin):
@admin.register(Flip) @admin.register(Flip)
class FlipsAdmin(admin.ModelAdmin): class FlipsAdmin(admin.ModelAdmin):
list_display = ("id", "created", "processed", "owner", "old_status", list_display = ("id", "created", "processed", "owner", "old_status", "new_status")
"new_status") raw_id_fields = ("owner",)
raw_id_fields = ("owner", )

View File

@ -28,6 +28,7 @@ def authorize(f):
return error("wrong api key", 401) return error("wrong api key", 401)
return f(request, *args, **kwds) return f(request, *args, **kwds)
return wrapper return wrapper
@ -50,6 +51,7 @@ def authorize_read(f):
return error("wrong api key", 401) return error("wrong api key", 401)
return f(request, *args, **kwds) return f(request, *args, **kwds)
return wrapper return wrapper
@ -80,7 +82,9 @@ def validate_json(schema=None):
return error("json validation error: %s" % e) return error("json validation error: %s" % e)
return f(request, *args, **kwds) return f(request, *args, **kwds)
return wrapper return wrapper
return decorator return decorator
@ -106,4 +110,5 @@ def cors(*methods):
return response return response
return wrapper return wrapper
return decorator return decorator

View File

@ -5,7 +5,7 @@ from hc.api.models import Notification, Check
class Command(BaseCommand): class Command(BaseCommand):
help = 'Prune stored notifications' help = "Prune stored notifications"
def handle(self, *args, **options): def handle(self, *args, **options):
total = 0 total = 0
@ -13,8 +13,9 @@ class Command(BaseCommand):
q = Check.objects.filter(n_pings__gt=100) q = Check.objects.filter(n_pings__gt=100)
q = q.annotate(min_ping_date=Min("ping__created")) q = q.annotate(min_ping_date=Min("ping__created"))
for check in q: for check in q:
qq = Notification.objects.filter(owner_id=check.id, qq = Notification.objects.filter(
created__lt=check.min_ping_date) owner_id=check.id, created__lt=check.min_ping_date
)
num_deleted, _ = qq.delete() num_deleted, _ = qq.delete()
total += num_deleted total += num_deleted

View File

@ -6,7 +6,7 @@ from hc.api.models import Ping
class Command(BaseCommand): class Command(BaseCommand):
help = 'Prune pings based on limits in user profiles' help = "Prune pings based on limits in user profiles"
def handle(self, *args, **options): def handle(self, *args, **options):
# Create any missing user profiles # Create any missing user profiles

View File

@ -29,7 +29,8 @@ class Command(BaseCommand):
q = q.filter(n__gt=0) q = q.filter(n__gt=0)
n_pruned, _ = q.delete() n_pruned, _ = q.delete()
self.stdout.write("Pruned %d pings for check %s (%s)" % self.stdout.write(
(n_pruned, check.id, check.name)) "Pruned %d pings for check %s (%s)" % (n_pruned, check.id, check.name)
)
return "Done!" return "Done!"

View File

@ -6,7 +6,7 @@ from hc.api.models import TokenBucket
class Command(BaseCommand): class Command(BaseCommand):
help = 'Prune pings based on limits in user profiles' help = "Prune pings based on limits in user profiles"
def handle(self, *args, **options): def handle(self, *args, **options):

View File

@ -35,23 +35,23 @@ def notify_on_thread(flip_id, stdout):
class Command(BaseCommand): class Command(BaseCommand):
help = 'Sends UP/DOWN email alerts' help = "Sends UP/DOWN email alerts"
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument( parser.add_argument(
'--no-loop', "--no-loop",
action='store_false', action="store_false",
dest='loop', dest="loop",
default=True, default=True,
help='Do not keep running indefinitely in a 2 second wait loop', help="Do not keep running indefinitely in a 2 second wait loop",
) )
parser.add_argument( parser.add_argument(
'--no-threads', "--no-threads",
action='store_false', action="store_false",
dest='use_threads', dest="use_threads",
default=False, default=False,
help='Send alerts synchronously, without using threads', help="Send alerts synchronously, without using threads",
) )
def process_one_flip(self, use_threads=True): def process_one_flip(self, use_threads=True):

View File

@ -9,13 +9,13 @@ from hc.api.models import Check
def num_pinged_checks(profile): def num_pinged_checks(profile):
q = Check.objects.filter(user_id=profile.user.id,) q = Check.objects.filter(user_id=profile.user.id)
q = q.filter(last_ping__isnull=False) q = q.filter(last_ping__isnull=False)
return q.count() return q.count()
class Command(BaseCommand): class Command(BaseCommand):
help = 'Send due monthly reports and nags' help = "Send due monthly reports and nags"
tmpl = "Sent monthly report to %s" tmpl = "Sent monthly report to %s"
def pause(self): def pause(self):
@ -23,11 +23,11 @@ class Command(BaseCommand):
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument( parser.add_argument(
'--loop', "--loop",
action='store_true', action="store_true",
dest='loop', dest="loop",
default=False, default=False,
help='Keep running indefinitely in a 300 second wait loop', help="Keep running indefinitely in a 300 second wait loop",
) )
def handle_one_monthly_report(self): def handle_one_monthly_report(self):
@ -48,8 +48,9 @@ class Command(BaseCommand):
# A sort of optimistic lock. Try to update next_report_date, # A sort of optimistic lock. Try to update next_report_date,
# and if does get modified, we're in drivers seat: # and if does get modified, we're in drivers seat:
qq = Profile.objects.filter(id=profile.id, qq = Profile.objects.filter(
next_report_date=profile.next_report_date) id=profile.id, next_report_date=profile.next_report_date
)
num_updated = qq.update(next_report_date=month_after) num_updated = qq.update(next_report_date=month_after)
if num_updated != 1: if num_updated != 1:
@ -72,8 +73,7 @@ class Command(BaseCommand):
if profile is None: if profile is None:
return False return False
qq = Profile.objects.filter(id=profile.id, qq = Profile.objects.filter(id=profile.id, next_nag_date=profile.next_nag_date)
next_nag_date=profile.next_nag_date)
num_updated = qq.update(next_nag_date=now + profile.nag_period) num_updated = qq.update(next_nag_date=now + profile.nag_period)
if num_updated != 1: if num_updated != 1:

View File

@ -16,7 +16,7 @@ class Command(BaseCommand):
form = { form = {
"url": settings.SITE_ROOT + reverse("hc-telegram-webhook"), "url": settings.SITE_ROOT + reverse("hc-telegram-webhook"),
"allowed_updates": ["message"] "allowed_updates": ["message"],
} }
url = SETWEBHOOK_TMPL % settings.TELEGRAM_TOKEN url = SETWEBHOOK_TMPL % settings.TELEGRAM_TOKEN

View File

@ -7,7 +7,9 @@ from django.core.management.base import BaseCommand
from django.db import connections from django.db import connections
from hc.api.models import Check from hc.api.models import Check
RE_UUID = re.compile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") RE_UUID = re.compile(
"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$"
)
class Listener(SMTPServer): class Listener(SMTPServer):
@ -15,7 +17,9 @@ class Listener(SMTPServer):
self.stdout = stdout self.stdout = stdout
super(Listener, self).__init__(localaddr, None, decode_data=False) super(Listener, self).__init__(localaddr, None, decode_data=False)
def process_message(self, peer, mailfrom, rcpttos, data, mail_options=None, rcpt_options=None): def process_message(
self, peer, mailfrom, rcpttos, data, mail_options=None, rcpt_options=None
):
# get a new db connection in case the old one has timed out: # get a new db connection in case the old one has timed out:
connections.close_all() connections.close_all()
@ -53,13 +57,12 @@ class Command(BaseCommand):
help = "Listen for ping emails" help = "Listen for ping emails"
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument("--host", parser.add_argument(
help="ip address to listen on, default 0.0.0.0", "--host", help="ip address to listen on, default 0.0.0.0", default="0.0.0.0"
default="0.0.0.0") )
parser.add_argument('--port', parser.add_argument(
help="port to listen on, default 25", "--port", help="port to listen on, default 25", type=int, default=25
type=int, )
default=25)
def handle(self, host, port, *args, **options): def handle(self, host, port, *args, **options):
listener = Listener((host, port), self.stdout) listener = Listener((host, port), self.stdout)

View File

@ -8,18 +8,29 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Check', name="Check",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)), (
('code', models.UUIDField(default=uuid.uuid4, editable=False)), "id",
('last_ping', models.DateTimeField(null=True, blank=True)), models.AutoField(
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), 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, on_delete=models.CASCADE
),
),
], ],
), )
] ]

View File

@ -7,29 +7,44 @@ import datetime
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0001_initial")]
('api', '0001_initial'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='alert_after', name="alert_after",
field=models.DateTimeField(null=True, blank=True), field=models.DateTimeField(null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='check', model_name="check", name="enabled", field=models.BooleanField(default=True)
name='enabled',
field=models.BooleanField(default=True),
), ),
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='status', name="status",
field=models.CharField(max_length=6, choices=[('up', 'Up'), ('down', 'Down'), ('new', 'New')], default='new'), field=models.CharField(
max_length=6,
choices=[("up", "Up"), ("down", "Down"), ("new", "New")],
default="new",
),
), ),
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='timeout', name="timeout",
field=models.DurationField(choices=[(datetime.timedelta(0, 300), '5 minutes'), (datetime.timedelta(0, 600), '10 minutes'), (datetime.timedelta(0, 1800), '30 minutes'), (datetime.timedelta(0, 3600), '1 hour'), (datetime.timedelta(0, 7200), '2 hours'), (datetime.timedelta(0, 21600), '6 hours'), (datetime.timedelta(0, 43200), '12 hours'), (datetime.timedelta(1), '1 day'), (datetime.timedelta(2), '2 days'), (datetime.timedelta(7), '1 week'), (datetime.timedelta(14), '2 weeks')], default=datetime.timedelta(1)), field=models.DurationField(
choices=[
(datetime.timedelta(0, 300), "5 minutes"),
(datetime.timedelta(0, 600), "10 minutes"),
(datetime.timedelta(0, 1800), "30 minutes"),
(datetime.timedelta(0, 3600), "1 hour"),
(datetime.timedelta(0, 7200), "2 hours"),
(datetime.timedelta(0, 21600), "6 hours"),
(datetime.timedelta(0, 43200), "12 hours"),
(datetime.timedelta(1), "1 day"),
(datetime.timedelta(2), "2 days"),
(datetime.timedelta(7), "1 week"),
(datetime.timedelta(14), "2 weeks"),
],
default=datetime.timedelta(1),
),
), ),
] ]

View File

@ -7,24 +7,22 @@ import datetime
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0002_auto_20150616_0732")]
('api', '0002_auto_20150616_0732'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='name', name="name",
field=models.CharField(max_length=100, blank=True), field=models.CharField(max_length=100, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='check', model_name="check",
name='alert_after', name="alert_after",
field=models.DateTimeField(editable=False, null=True, blank=True), field=models.DateTimeField(editable=False, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='check', model_name="check",
name='timeout', name="timeout",
field=models.DurationField(default=datetime.timedelta(1)), field=models.DurationField(default=datetime.timedelta(1)),
), ),
] ]

View File

@ -8,19 +8,17 @@ import datetime
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0003_auto_20150616_1249")]
('api', '0003_auto_20150616_1249'),
]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(model_name="check", name="enabled"),
model_name='check',
name='enabled',
),
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='created', name="created",
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2015, 6, 16, 13, 19, 17, 218278, tzinfo=utc)), field=models.DateTimeField(
auto_now_add=True,
default=datetime.datetime(2015, 6, 16, 13, 19, 17, 218278, tzinfo=utc),
),
preserve_default=False, preserve_default=False,
), ),
] ]

View File

@ -7,14 +7,17 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0004_auto_20150616_1319")]
('api', '0004_auto_20150616_1319'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='check', model_name="check",
name='user', name="user",
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE), field=models.ForeignKey(
), blank=True,
to=settings.AUTH_USER_MODEL,
null=True,
on_delete=models.CASCADE,
),
)
] ]

View File

@ -7,14 +7,12 @@ import datetime
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0005_auto_20150630_2021")]
('api', '0005_auto_20150630_2021'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='grace', name="grace",
field=models.DurationField(default=datetime.timedelta(0, 3600)), field=models.DurationField(default=datetime.timedelta(0, 3600)),
), )
] ]

View File

@ -6,21 +6,27 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0006_check_grace")]
('api', '0006_check_grace'),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Ping', name="Ping",
fields=[ fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), (
('created', models.DateTimeField(auto_now_add=True)), "id",
('remote_addr', models.GenericIPAddressField()), models.AutoField(
('method', models.CharField(max_length=10)), primary_key=True,
('ua', models.CharField(max_length=100, blank=True)), serialize=False,
('body', models.TextField(blank=True)), auto_created=True,
('owner', models.ForeignKey(to='api.Check', on_delete=models.CASCADE)), verbose_name="ID",
),
),
("created", models.DateTimeField(auto_now_add=True)),
("remote_addr", models.GenericIPAddressField()),
("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", on_delete=models.CASCADE)),
], ],
), )
] ]

View File

@ -6,14 +6,12 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0007_ping")]
('api', '0007_ping'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='ping', model_name="ping",
name='ua', name="ua",
field=models.CharField(max_length=200, blank=True), field=models.CharField(max_length=200, blank=True),
), )
] ]

View File

@ -6,24 +6,22 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0008_auto_20150801_1213")]
('api', '0008_auto_20150801_1213'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='ping', model_name="ping",
name='scheme', name="scheme",
field=models.CharField(max_length=10, default='http'), field=models.CharField(max_length=10, default="http"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='ping', model_name="ping",
name='method', name="method",
field=models.CharField(blank=True, max_length=10), field=models.CharField(blank=True, max_length=10),
), ),
migrations.AlterField( migrations.AlterField(
model_name='ping', model_name="ping",
name='remote_addr', name="remote_addr",
field=models.GenericIPAddressField(blank=True, null=True), field=models.GenericIPAddressField(blank=True, null=True),
), ),
] ]

View File

@ -10,21 +10,44 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('api', '0009_auto_20150801_1250'), ("api", "0009_auto_20150801_1250"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Channel', name="Channel",
fields=[ fields=[
('id', models.AutoField(primary_key=True, auto_created=True, verbose_name='ID', serialize=False)), (
('code', models.UUIDField(editable=False, default=uuid.uuid4)), "id",
('created', models.DateTimeField(auto_now_add=True)), models.AutoField(
('kind', models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('pd', 'PagerDuty')], max_length=20)), primary_key=True,
('value', models.CharField(max_length=200, blank=True)), auto_created=True,
('email_verified', models.BooleanField(default=False)), verbose_name="ID",
('checks', models.ManyToManyField(to='api.Check')), serialize=False,
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ),
),
("code", models.UUIDField(editable=False, default=uuid.uuid4)),
("created", models.DateTimeField(auto_now_add=True)),
(
"kind",
models.CharField(
choices=[
("email", "Email"),
("webhook", "Webhook"),
("pd", "PagerDuty"),
],
max_length=20,
),
),
("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, on_delete=models.CASCADE
),
),
], ],
), )
] ]

View File

@ -6,20 +6,29 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0010_channel")]
('api', '0010_channel'),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Notification', name="Notification",
fields=[ fields=[
('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), (
('check_status', models.CharField(max_length=6)), "id",
('created', models.DateTimeField(auto_now_add=True)), models.AutoField(
('status', models.IntegerField(default=0)), serialize=False,
('channel', models.ForeignKey(to='api.Channel', on_delete=models.CASCADE)), auto_created=True,
('owner', models.ForeignKey(to='api.Check', on_delete=models.CASCADE)), verbose_name="ID",
primary_key=True,
),
),
("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", on_delete=models.CASCADE),
),
("owner", models.ForeignKey(to="api.Check", on_delete=models.CASCADE)),
], ],
), )
] ]

View File

@ -6,14 +6,20 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0011_notification")]
('api', '0011_notification'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('slack', 'Slack'), ('pd', 'PagerDuty')], max_length=20), field=models.CharField(
), choices=[
("email", "Email"),
("webhook", "Webhook"),
("slack", "Slack"),
("pd", "PagerDuty"),
],
max_length=20,
),
)
] ]

View File

@ -6,14 +6,21 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0012_auto_20150930_1922")]
('api', '0012_auto_20150930_1922'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(max_length=20, choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty')]), field=models.CharField(
), max_length=20,
choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
],
),
)
] ]

View File

@ -7,14 +7,12 @@ import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0013_auto_20151001_2029")]
('api', '0013_auto_20151001_2029'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='check', model_name="check",
name='code', name="code",
field=models.UUIDField(default=uuid.uuid4, db_index=True, editable=False), field=models.UUIDField(default=uuid.uuid4, db_index=True, editable=False),
), )
] ]

View File

@ -6,13 +6,10 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0014_auto_20151019_2039")]
('api', '0014_auto_20151019_2039'),
]
operations = [ operations = [
migrations.AlterIndexTogether( migrations.AlterIndexTogether(
name='check', name="check", index_together=set([("status", "user", "alert_after")])
index_together=set([('status', 'user', 'alert_after')]), )
),
] ]

View File

@ -6,14 +6,21 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0015_auto_20151022_1008")]
('api', '0015_auto_20151022_1008'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='check', model_name="check",
name='status', name="status",
field=models.CharField(default='new', max_length=6, choices=[('up', 'Up'), ('down', 'Down'), ('new', 'New'), ('paused', 'Paused')]), field=models.CharField(
), default="new",
max_length=6,
choices=[
("up", "Up"),
("down", "Down"),
("new", "New"),
("paused", "Paused"),
],
),
)
] ]

View File

@ -6,14 +6,22 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0016_auto_20151030_1107")]
('api', '0016_auto_20151030_1107'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('po', 'Pushover')], max_length=20), field=models.CharField(
), choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
("po", "Pushover"),
],
max_length=20,
),
)
] ]

View File

@ -6,13 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0017_auto_20151117_1032")]
('api', '0017_auto_20151117_1032'),
]
operations = [ operations = [migrations.RemoveField(model_name="ping", name="body")]
migrations.RemoveField(
model_name='ping',
name='body',
),
]

View File

@ -6,14 +6,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0018_remove_ping_body")]
('api', '0018_remove_ping_body'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='tags', name="tags",
field=models.CharField(max_length=500, blank=True), field=models.CharField(max_length=500, blank=True),
), )
] ]

View File

@ -7,14 +7,10 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0019_check_tags")]
('api', '0019_check_tags'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check", name="n_pings", field=models.IntegerField(default=0)
name='n_pings', )
field=models.IntegerField(default=0),
),
] ]

View File

@ -7,14 +7,10 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0020_check_n_pings")]
('api', '0020_check_n_pings'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='ping', model_name="ping", name="n", field=models.IntegerField(null=True)
name='n', )
field=models.IntegerField(null=True),
),
] ]

View File

@ -7,18 +7,13 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0021_ping_n")]
('api', '0021_ping_n'),
]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(model_name="notification", name="status"),
model_name='notification',
name='status',
),
migrations.AddField( migrations.AddField(
model_name='notification', model_name="notification",
name='error', name="error",
field=models.CharField(blank=True, max_length=200), field=models.CharField(blank=True, max_length=200),
), ),
] ]

View File

@ -7,13 +7,10 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0022_auto_20160130_2042")]
('api', '0022_auto_20160130_2042'),
]
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='notification', name="notification", options={"get_latest_by": "created"}
options={'get_latest_by': 'created'}, )
),
] ]

View File

@ -7,14 +7,23 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0023_auto_20160131_1919")]
('api', '0023_auto_20160131_1919'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[(b'email', b'Email'), (b'webhook', b'Webhook'), (b'hipchat', b'HipChat'), (b'slack', b'Slack'), (b'pd', b'PagerDuty'), (b'po', b'Pushover'), (b'victorops', b'VictorOps')], max_length=20), field=models.CharField(
), choices=[
(b"email", b"Email"),
(b"webhook", b"Webhook"),
(b"hipchat", b"HipChat"),
(b"slack", b"Slack"),
(b"pd", b"PagerDuty"),
(b"po", b"Pushover"),
(b"victorops", b"VictorOps"),
],
max_length=20,
),
)
] ]

View File

@ -7,14 +7,23 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0024_auto_20160203_2227")]
('api', '0024_auto_20160203_2227'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('po', 'Pushover'), ('victorops', 'VictorOps')], max_length=20), field=models.CharField(
), choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
("po", "Pushover"),
("victorops", "VictorOps"),
],
max_length=20,
),
)
] ]

View File

@ -7,14 +7,10 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0025_auto_20160216_1214")]
('api', '0025_auto_20160216_1214'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel", name="value", field=models.TextField(blank=True)
name='value', )
field=models.TextField(blank=True),
),
] ]

View File

@ -7,29 +7,44 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0026_auto_20160415_1824")]
('api', '0026_auto_20160415_1824'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='kind', name="kind",
field=models.CharField(choices=[('simple', 'Simple'), ('cron', 'Cron')], default='simple', max_length=10), field=models.CharField(
choices=[("simple", "Simple"), ("cron", "Cron")],
default="simple",
max_length=10,
),
), ),
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='schedule', name="schedule",
field=models.CharField(default='* * * * *', max_length=100), field=models.CharField(default="* * * * *", max_length=100),
), ),
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='tz', name="tz",
field=models.CharField(default='UTC', max_length=36), field=models.CharField(default="UTC", max_length=36),
), ),
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps')], max_length=20), field=models.CharField(
choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
("po", "Pushover"),
("pushbullet", "Pushbullet"),
("opsgenie", "OpsGenie"),
("victorops", "VictorOps"),
],
max_length=20,
),
), ),
] ]

View File

@ -8,19 +8,31 @@ import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0027_auto_20161213_1059")]
('api', '0027_auto_20161213_1059'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='notification', model_name="notification",
name='code', name="code",
field=models.UUIDField(default=None, editable=False, null=True), field=models.UUIDField(default=None, editable=False, null=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord')], max_length=20), field=models.CharField(
choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
("po", "Pushover"),
("pushbullet", "Pushbullet"),
("opsgenie", "OpsGenie"),
("victorops", "VictorOps"),
("discord", "Discord"),
],
max_length=20,
),
), ),
] ]

View File

@ -8,14 +8,12 @@ import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0028_auto_20170305_1907")]
('api', '0028_auto_20170305_1907'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='notification', model_name="notification",
name='code', name="code",
field=models.UUIDField(default=uuid.uuid4, editable=False, null=True), field=models.UUIDField(default=uuid.uuid4, editable=False, null=True),
), )
] ]

View File

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0029_auto_20170507_1251")]
('api', '0029_auto_20170507_1251'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='last_ping_body', name="last_ping_body",
field=models.CharField(blank=True, max_length=1000), field=models.CharField(blank=True, max_length=1000),
), )
] ]

View File

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0030_check_last_ping_body")]
('api', '0030_check_last_ping_body'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='check', model_name="check",
name='last_ping_body', name="last_ping_body",
field=models.CharField(blank=True, max_length=10000), field=models.CharField(blank=True, max_length=10000),
), )
] ]

View File

@ -7,14 +7,27 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0031_auto_20170509_1320")]
('api', '0031_auto_20170509_1320'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord'), ('telegram', 'Telegram')], max_length=20), field=models.CharField(
), choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
("po", "Pushover"),
("pushbullet", "Pushbullet"),
("opsgenie", "OpsGenie"),
("victorops", "VictorOps"),
("discord", "Discord"),
("telegram", "Telegram"),
],
max_length=20,
),
)
] ]

View File

@ -7,14 +7,28 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0032_auto_20170608_1158")]
('api', '0032_auto_20170608_1158'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord'), ('telegram', 'Telegram'), ('sms', 'SMS')], max_length=20), field=models.CharField(
), choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
("po", "Pushover"),
("pushbullet", "Pushbullet"),
("opsgenie", "OpsGenie"),
("victorops", "VictorOps"),
("discord", "Discord"),
("telegram", "Telegram"),
("sms", "SMS"),
],
max_length=20,
),
)
] ]

View File

@ -7,14 +7,29 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0033_auto_20170714_1715")]
('api', '0033_auto_20170714_1715'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('pagertree', 'PagerTree'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord'), ('telegram', 'Telegram'), ('sms', 'SMS')], max_length=20), field=models.CharField(
), choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
("pagertree", "PagerTree"),
("po", "Pushover"),
("pushbullet", "Pushbullet"),
("opsgenie", "OpsGenie"),
("victorops", "VictorOps"),
("discord", "Discord"),
("telegram", "Telegram"),
("sms", "SMS"),
],
max_length=20,
),
)
] ]

View File

@ -7,14 +7,30 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0034_auto_20171227_1530")]
('api', '0034_auto_20171227_1530'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='channel', model_name="channel",
name='kind', name="kind",
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('pagertree', 'PagerTree'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord'), ('telegram', 'Telegram'), ('sms', 'SMS'), ('zendesk', 'Zendesk')], max_length=20), field=models.CharField(
), choices=[
("email", "Email"),
("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"),
("pd", "PagerDuty"),
("pagertree", "PagerTree"),
("po", "Pushover"),
("pushbullet", "Pushbullet"),
("opsgenie", "OpsGenie"),
("victorops", "VictorOps"),
("discord", "Discord"),
("telegram", "Telegram"),
("sms", "SMS"),
("zendesk", "Zendesk"),
],
max_length=20,
),
)
] ]

View File

@ -7,13 +7,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0035_auto_20171229_2008")]
('api', '0035_auto_20171229_2008'),
]
operations = [ operations = [migrations.AlterIndexTogether(name="check", index_together=set([]))]
migrations.AlterIndexTogether(
name='check',
index_together=set([]),
),
]

View File

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0036_auto_20180116_2243")]
('api', '0036_auto_20180116_2243'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='ping', model_name="ping",
name='id', name="id",
field=models.BigAutoField(primary_key=True, serialize=False), field=models.BigAutoField(primary_key=True, serialize=False),
), )
] ]

View File

@ -7,19 +7,17 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0037_auto_20180127_1215")]
('api', '0037_auto_20180127_1215'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='has_confirmation_link', name="has_confirmation_link",
field=models.BooleanField(default=False), field=models.BooleanField(default=False),
), ),
migrations.AddField( migrations.AddField(
model_name='ping', model_name="ping",
name='body', name="body",
field=models.CharField(blank=True, max_length=10000, null=True), field=models.CharField(blank=True, max_length=10000, null=True),
), ),
] ]

View File

@ -7,13 +7,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0038_auto_20180318_1306")]
('api', '0038_auto_20180318_1306'),
]
operations = [ operations = [migrations.RemoveField(model_name="check", name="last_ping_body")]
migrations.RemoveField(
model_name='check',
name='last_ping_body',
),
]

View File

@ -5,19 +5,15 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0039_remove_check_last_ping_body")]
('api', '0039_remove_check_last_ping_body'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check",
name='last_ping_was_fail', name="last_ping_was_fail",
field=models.NullBooleanField(default=False), field=models.NullBooleanField(default=False),
), ),
migrations.AddField( migrations.AddField(
model_name='ping', model_name="ping", name="fail", field=models.NullBooleanField(default=False)
name='fail',
field=models.NullBooleanField(default=False),
), ),
] ]

View File

@ -5,14 +5,10 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("api", "0040_auto_20180517_1336")]
('api', '0040_auto_20180517_1336'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='check', model_name="check", name="desc", field=models.TextField(blank=True)
name='desc', )
field=models.TextField(blank=True),
),
] ]

Some files were not shown because too many files have changed in this diff Show More