From 6ada656df4e35ee90d2e475744333d8652d7ca50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Tue, 13 Dec 2016 13:09:53 +0200 Subject: [PATCH] UI for entering cron expression and setting timezone. --- ...205_0833.py => 0027_auto_20161213_1059.py} | 9 ++- hc/api/models.py | 7 ++- hc/front/forms.py | 5 +- hc/front/tests/test_update_timeout.py | 23 +++++++- hc/front/views.py | 6 ++ static/css/my_checks.css | 8 +++ static/js/checks.js | 50 +++++++++++----- templates/front/my_checks.html | 59 +++++++++++++++---- templates/front/my_checks_desktop.html | 9 ++- templates/front/my_checks_mobile.html | 10 ++++ 10 files changed, 152 insertions(+), 34 deletions(-) rename hc/api/migrations/{0027_auto_20161205_0833.py => 0027_auto_20161213_1059.py} (72%) diff --git a/hc/api/migrations/0027_auto_20161205_0833.py b/hc/api/migrations/0027_auto_20161213_1059.py similarity index 72% rename from hc/api/migrations/0027_auto_20161205_0833.py rename to hc/api/migrations/0027_auto_20161213_1059.py index fcf8af90..4232fab5 100644 --- a/hc/api/migrations/0027_auto_20161205_0833.py +++ b/hc/api/migrations/0027_auto_20161213_1059.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.1 on 2016-12-05 08:33 +# Generated by Django 1.10.1 on 2016-12-13 10:59 from __future__ import unicode_literals from django.db import migrations, models @@ -12,10 +12,15 @@ class Migration(migrations.Migration): ] operations = [ + migrations.AddField( + model_name='check', + name='kind', + field=models.CharField(choices=[('simple', 'Simple'), ('cron', 'Cron')], default='simple', max_length=10), + ), migrations.AddField( model_name='check', name='schedule', - field=models.CharField(blank=True, max_length=100), + field=models.CharField(default='* * * * *', max_length=100), ), migrations.AddField( model_name='check', diff --git a/hc/api/models.py b/hc/api/models.py index 39e3af5b..17d422fe 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -22,6 +22,9 @@ STATUSES = ( ) DEFAULT_TIMEOUT = td(days=1) DEFAULT_GRACE = td(hours=1) +CHECK_KINDS = (("simple", "Simple"), + ("cron", "Cron")) + CHANNEL_KINDS = (("email", "Email"), ("webhook", "Webhook"), ("hipchat", "HipChat"), @@ -52,9 +55,11 @@ class Check(models.Model): code = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True) user = models.ForeignKey(User, blank=True, null=True) created = models.DateTimeField(auto_now_add=True) + kind = models.CharField(max_length=10, default="simple", + choices=CHECK_KINDS) timeout = models.DurationField(default=DEFAULT_TIMEOUT) grace = models.DurationField(default=DEFAULT_GRACE) - schedule = models.CharField(max_length=100, blank=True) + schedule = models.CharField(max_length=100, default="* * * * *") tz = models.CharField(max_length=36, default="UTC") n_pings = models.IntegerField(default=0) last_ping = models.DateTimeField(null=True, blank=True) diff --git a/hc/front/forms.py b/hc/front/forms.py index 17afd8c6..8bcb112f 100644 --- a/hc/front/forms.py +++ b/hc/front/forms.py @@ -1,6 +1,6 @@ from django import forms from hc.front.validators import WebhookValidator -from hc.api.models import Channel +from hc.api.models import CHECK_KINDS, Channel class NameTagsForm(forms.Form): @@ -19,7 +19,10 @@ class NameTagsForm(forms.Form): class TimeoutForm(forms.Form): + kind = forms.ChoiceField(choices=CHECK_KINDS) timeout = forms.IntegerField(min_value=60, max_value=2592000) + schedule = forms.CharField(required=False, max_length=100) + tz = forms.CharField(required=False, max_length=36) grace = forms.IntegerField(min_value=60, max_value=2592000) diff --git a/hc/front/tests/test_update_timeout.py b/hc/front/tests/test_update_timeout.py index 96e02af7..cea51751 100644 --- a/hc/front/tests/test_update_timeout.py +++ b/hc/front/tests/test_update_timeout.py @@ -13,22 +13,41 @@ class UpdateTimeoutTestCase(BaseTestCase): def test_it_works(self): url = "/checks/%s/timeout/" % self.check.code - payload = {"timeout": 3600, "grace": 60} + payload = {"kind": "simple", "timeout": 3600, "grace": 60} self.client.login(username="alice@example.org", password="password") r = self.client.post(url, data=payload) self.assertRedirects(r, "/checks/") self.check.refresh_from_db() + self.assertEqual(self.check.kind, "simple") self.assertEqual(self.check.timeout.total_seconds(), 3600) self.assertEqual(self.check.grace.total_seconds(), 60) # alert_after should be updated too self.assertEqual(self.check.alert_after, self.check.get_alert_after()) + def test_it_saves_cron_expression(self): + url = "/checks/%s/timeout/" % self.check.code + payload = { + "kind": "cron", + "schedule": "* * * * *", + "tz": "UTC", + "timeout": 60, + "grace": 60 + } + + self.client.login(username="alice@example.org", password="password") + r = self.client.post(url, data=payload) + self.assertRedirects(r, "/checks/") + + self.check.refresh_from_db() + self.assertEqual(self.check.kind, "cron") + self.assertEqual(self.check.schedule, "* * * * *") + def test_team_access_works(self): url = "/checks/%s/timeout/" % self.check.code - payload = {"timeout": 7200, "grace": 60} + payload = {"kind": "simple", "timeout": 7200, "grace": 60} # Logging in as bob, not alice. Bob has team access so this # should work. diff --git a/hc/front/views.py b/hc/front/views.py index 3b4f6236..51dd6d98 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -163,12 +163,18 @@ def update_timeout(request, code): form = TimeoutForm(request.POST) if form.is_valid(): + check.kind = form.cleaned_data["kind"] check.timeout = td(seconds=form.cleaned_data["timeout"]) check.grace = td(seconds=form.cleaned_data["grace"]) + check.schedule = form.cleaned_data["schedule"] + check.tz = form.cleaned_data["tz"] + if check.last_ping: check.alert_after = check.get_alert_after() check.save() + else: + assert 0, "form is not valid! %s" % form.errors return redirect("hc-checks") diff --git a/static/css/my_checks.css b/static/css/my_checks.css index 89344664..03033f7f 100644 --- a/static/css/my_checks.css +++ b/static/css/my_checks.css @@ -23,6 +23,14 @@ } +#type-simple, #type-cron { + width: 70px; +} + +#schedule-block { + margin: 0 50px; +} + #period-slider { margin: 20px 50px 80px 50px; } diff --git a/static/js/checks.js b/static/js/checks.js index 220687fe..b9394e72 100644 --- a/static/js/checks.js +++ b/static/js/checks.js @@ -91,11 +91,9 @@ $(function () { $('[data-toggle="tooltip"]').tooltip(); $(".my-checks-name").click(function() { - var $this = $(this); - - $("#update-name-form").attr("action", $this.data("url")); - $("#update-name-input").val($this.data("name")); - $("#update-tags-input").val($this.data("tags")); + $("#update-name-form").attr("action", this.dataset.url); + $("#update-name-input").val(this.dataset.name); + $("#update-tags-input").val(this.dataset.tags); $('#update-name-modal').modal("show"); $("#update-name-input").focus(); @@ -103,21 +101,45 @@ $(function () { }); $(".timeout-grace").click(function() { - var $this = $(this); + $("#update-timeout-form").attr("action", this.dataset.url); + periodSlider.noUiSlider.set(this.dataset.timeout); + graceSlider.noUiSlider.set(this.dataset.grace); + $("#schedule").val(this.dataset.schedule); + $("#tz").val(this.dataset.tz); + + if (this.dataset.kind == "cron") { + $("#type-simple").removeClass("active"); + $("#type-cron").addClass("active"); + $("#type-cron input").prop("checked", true); + + $("#period-block").hide(); + $("#schedule-block").show(); + } else { + $("#type-simple").addClass("active"); + $("#type-simple input").prop("checked", true); + $("#type-cron").removeClass("active"); + + $("#period-block").show(); + $("#schedule-block").hide(); + } - $("#update-timeout-form").attr("action", $this.data("url")); - periodSlider.noUiSlider.set($this.data("timeout")) - graceSlider.noUiSlider.set($this.data("grace")) $('#update-timeout-modal').modal({"show":true, "backdrop":"static"}); - return false; }); - $(".check-menu-remove").click(function() { - var $this = $(this); + $("#type-simple").click(function() { + $("#period-block").show(); + $("#schedule-block").hide(); + }); + + $("#type-cron").click(function() { + $("#period-block").hide(); + $("#schedule-block").show(); + }); - $("#remove-check-form").attr("action", $this.data("url")); - $(".remove-check-name").text($this.data("name")); + $(".check-menu-remove").click(function() { + $("#remove-check-form").attr("action", this.dataset.url); + $(".remove-check-name").text(this.dataset.name); $('#remove-check-modal').modal("show"); return false; diff --git a/templates/front/my_checks.html b/templates/front/my_checks.html index 072f6c2e..052ece40 100644 --- a/templates/front/my_checks.html +++ b/templates/front/my_checks.html @@ -118,20 +118,42 @@