diff --git a/hc/api/management/commands/ensuretriggers.py b/hc/api/management/commands/ensuretriggers.py index 17693f8e..e180fa5b 100644 --- a/hc/api/management/commands/ensuretriggers.py +++ b/hc/api/management/commands/ensuretriggers.py @@ -13,7 +13,7 @@ CREATE OR REPLACE FUNCTION update_alert_after() RETURNS trigger AS $update_alert_after$ BEGIN IF NEW.last_ping IS NOT NULL THEN - NEW.alert_after := NEW.last_ping + NEW.timeout + '1 hour'; + NEW.alert_after := NEW.last_ping + NEW.timeout + NEW.grace; END IF; RETURN NEW; END; diff --git a/hc/api/migrations/0006_check_grace.py b/hc/api/migrations/0006_check_grace.py new file mode 100644 index 00000000..c5180359 --- /dev/null +++ b/hc/api/migrations/0006_check_grace.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import datetime + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0005_auto_20150630_2021'), + ] + + operations = [ + migrations.AddField( + model_name='check', + name='grace', + field=models.DurationField(default=datetime.timedelta(0, 3600)), + ), + ] diff --git a/hc/api/models.py b/hc/api/models.py index 8c816864..0b3471c1 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -10,7 +10,13 @@ from hc.lib.emails import send STATUSES = (("up", "Up"), ("down", "Down"), ("new", "New")) DEFAULT_TIMEOUT = td(days=1) -TIMEOUT_CHOICES = ( +DEFAULT_GRACE = td(hours=1) + +DURATION_CHOICES = ( + ("1 minute", td(minutes=1)), + ("2 minutes", td(minutes=2)), + ("5 minutes", td(minutes=5)), + ("10 minutes", td(minutes=10)), ("15 minutes", td(minutes=15)), ("30 minutes", td(minutes=30)), ("1 hour", td(hours=1)), @@ -30,6 +36,7 @@ class Check(models.Model): user = models.ForeignKey(User, blank=True, null=True) created = models.DateTimeField(auto_now_add=True) timeout = models.DurationField(default=DEFAULT_TIMEOUT) + grace = models.DurationField(default=DEFAULT_GRACE) last_ping = models.DateTimeField(null=True, blank=True) alert_after = models.DateTimeField(null=True, blank=True, editable=False) status = models.CharField(max_length=6, choices=STATUSES, default="new") @@ -39,7 +46,7 @@ class Check(models.Model): def send_alert(self): ctx = { - "timeout_choices": TIMEOUT_CHOICES, + "timeout_choices": DURATION_CHOICES, "check": self, "checks": self.user.check_set.order_by("created"), "now": timezone.now() diff --git a/hc/front/forms.py b/hc/front/forms.py index 1450c2d8..ec5da871 100644 --- a/hc/front/forms.py +++ b/hc/front/forms.py @@ -1,7 +1,6 @@ from django import forms -from hc.api.models import TIMEOUT_CHOICES - class TimeoutForm(forms.Form): - timeout = forms.ChoiceField(choices=TIMEOUT_CHOICES) + timeout = forms.IntegerField(min_value=60, max_value=604800) + grace = forms.IntegerField(min_value=60, max_value=604800) diff --git a/hc/front/views.py b/hc/front/views.py index 7563e2c7..ba86cc66 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -1,10 +1,12 @@ +from datetime import timedelta as td + from django.contrib.auth.decorators import login_required from django.http import HttpResponseForbidden from django.shortcuts import redirect, render from django.utils import timezone -from hc.api.models import Check -from hc.front.forms import TimeoutForm, TIMEOUT_CHOICES +from hc.api.models import Check, DURATION_CHOICES +from hc.front.forms import TimeoutForm def _welcome(request): @@ -42,7 +44,7 @@ def _my_checks(request): ctx = { "checks": checks, "now": timezone.now(), - "timeout_choices": TIMEOUT_CHOICES + "duration_choices": DURATION_CHOICES } return render(request, "front/my_checks.html", ctx) @@ -100,7 +102,8 @@ def update_timeout(request, code): form = TimeoutForm(request.POST) if form.is_valid(): - check.timeout = form.cleaned_data["timeout"] + check.timeout = td(seconds=form.cleaned_data["timeout"]) + check.grace = td(seconds=form.cleaned_data["grace"]) check.save() return redirect("hc-index") diff --git a/static/css/nouislider.min.css b/static/css/nouislider.min.css new file mode 100644 index 00000000..d8777171 --- /dev/null +++ b/static/css/nouislider.min.css @@ -0,0 +1,4 @@ +/*! nouislider - 8.0.2 - 2015-07-06 13:22:09 */ + + +.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-user-select:none;-ms-touch-action:none;-ms-user-select:none;-moz-user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative;direction:ltr}.noUi-base{width:100%;height:100%;position:relative;z-index:1}.noUi-origin{position:absolute;right:0;top:0;left:0;bottom:0}.noUi-handle{position:relative;z-index:1}.noUi-stacking .noUi-handle{z-index:10}.noUi-state-tap .noUi-origin{-webkit-transition:left .3s,top .3s;transition:left .3s,top .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-base{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;left:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;left:-6px;top:-17px}.noUi-background{background:#FAFAFA;box-shadow:inset 0 1px 1px #f0f0f0}.noUi-connect{background:#3FB8AF;box-shadow:inset 0 0 3px rgba(51,51,51,.45);-webkit-transition:background 450ms;transition:background 450ms}.noUi-origin{border-radius:2px}.noUi-target{border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-target.noUi-connect{box-shadow:inset 0 0 3px rgba(51,51,51,.45),0 3px 6px -5px #BBB}.noUi-dragable{cursor:w-resize}.noUi-vertical .noUi-dragable{cursor:n-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect,[disabled].noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-origin{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;font:400 12px Arial;color:#999}.noUi-value{width:40px;position:absolute;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-large,.noUi-marker-sub{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:50px;top:100%;left:0;width:100%}.noUi-value-horizontal{margin-left:-20px;padding-top:20px}.noUi-value-horizontal.noUi-value-sub{padding-top:15px}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{width:15px;margin-left:20px;margin-top:-5px}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px} \ No newline at end of file diff --git a/static/css/nouislider.pips.css b/static/css/nouislider.pips.css new file mode 100644 index 00000000..f4f97bf2 --- /dev/null +++ b/static/css/nouislider.pips.css @@ -0,0 +1,98 @@ + +/* Base; + * + */ +.noUi-pips, +.noUi-pips * { +-moz-box-sizing: border-box; + box-sizing: border-box; +} +.noUi-pips { + position: absolute; + font: 400 12px Arial; + color: #999; +} + +/* Values; + * + */ +.noUi-value { + width: 40px; + position: absolute; + text-align: center; +} +.noUi-value-sub { + color: #ccc; + font-size: 10px; +} + +/* Markings; + * + */ +.noUi-marker { + position: absolute; + background: #CCC; +} +.noUi-marker-sub { + background: #AAA; +} +.noUi-marker-large { + background: #AAA; +} + +/* Horizontal layout; + * + */ +.noUi-pips-horizontal { + padding: 10px 0; + height: 50px; + top: 100%; + left: 0; + width: 100%; +} +.noUi-value-horizontal { + margin-left: -20px; + padding-top: 20px; +} +.noUi-value-horizontal.noUi-value-sub { + padding-top: 15px; +} + +.noUi-marker-horizontal.noUi-marker { + margin-left: -1px; + width: 2px; + height: 5px; +} +.noUi-marker-horizontal.noUi-marker-sub { + height: 10px; +} +.noUi-marker-horizontal.noUi-marker-large { + height: 15px; +} + +/* Vertical layout; + * + */ +.noUi-pips-vertical { + padding: 0 10px; + height: 100%; + top: 0; + left: 100%; +} +.noUi-value-vertical { + width: 15px; + margin-left: 20px; + margin-top: -5px; +} + +.noUi-marker-vertical.noUi-marker { + width: 5px; + height: 2px; + margin-top: -1px; +} +.noUi-marker-vertical.noUi-marker-sub { + width: 10px; +} +.noUi-marker-vertical.noUi-marker-large { + width: 15px; +} diff --git a/static/css/style.css b/static/css/style.css index 1f1bc7f2..71c577b6 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -121,25 +121,16 @@ table.table tr > th.th-name { vertical-align: middle; } -.name-edit.inactive .input-name { +.my-checks-name { border: 1px solid rgba(0, 0, 0, 0); - background: none; - box-shadow: none; - transition: none; + padding: 6px; + display: block; } -.name-edit.inactive .input-name:hover { +.my-checks-name:hover { border: 1px dotted #AAA; } -.name-edit.inactive button { - visibility: hidden; -} - -.name-edit button { - opacity: 1; -} - .url-cell { font-size: small; } @@ -153,25 +144,70 @@ td.inactive .popover { position: absolute; top: auto; left: auto; - margin-top: 32px; + margin-top: 57px; margin-left: -77px; } -.timeout { +#checks-table > tbody > tr > th.th-frequency { + padding-left: 15px; +} + +.timeout_grace { border: 1px solid rgba(0, 0, 0, 0); padding: 6px; + display: block; } - -.timeout:hover { - color: #337ab7; +.timeout_grace:hover { border: 1px dotted #AAA; } +.checks-subline { + color: #888; +} + .check-menu { visibility: hidden; } tr:hover .check-menu { visibility: visible; -} \ No newline at end of file +} + + +.update-timeout-info { + line-height: 22px; +} + +.update-timeout-label { + position: relative; + right: 3px; + display: inline-block; + text-align: right; + width: 100px; +} + +.update-timeout-value { + font-size: 22px; + display: inline-block; + width: 100px; + text-align: left; + white-space: nowrap; + +} + +#frequency-slider { + margin: 20px 50px 80px 50px; +} + +#frequency-slider.noUi-connect { + background: #5cb85c; +} + +#grace-slider { + margin: 20px 50px 60px 50px; +} + +#grace-slider.noUi-connect { + background: #f0ad4e; +} diff --git a/static/js/checks.js b/static/js/checks.js index 32181dd7..ec36e051 100644 --- a/static/js/checks.js +++ b/static/js/checks.js @@ -1,40 +1,97 @@ $(function () { - $('[data-toggle="tooltip"]').tooltip(); - $(".name-edit input").click(function() { - $form = $(this.parentNode); - if (!$form.hasClass("inactive")) - return; + var secsToText = function(total) { + total = Math.floor(total / 60); + var m = total % 60; total = Math.floor(total / 60); + var h = total % 24; total = Math.floor(total / 24); + var d = total % 7; total = Math.floor(total / 7); + var w = total; + + var result = ""; + if (w) result += w + (w == 1 ? " week " : " weeks "); + if (d) result += d + (d == 1 ? " day " : " days "); + if (h) result += h + (h == 1 ? " hour " : " hours "); + if (m) result += m + (m == 1 ? " minute " : " minutes "); - // Click on all X buttons - $(".name-edit:not(.inactive) .name-edit-cancel").click(); + return result; + } - // Make this form editable and store its initial value - $form - .removeClass("inactive") - .data("originalValue", this.value); + var frequencySlider = document.getElementById("frequency-slider"); + noUiSlider.create(frequencySlider, { + start: [20], + connect: "lower", + range: { + 'min': [60, 60], + '30%': [3600, 3600], + '82.80%': [86400, 86400], + 'max': 604800 + }, + pips: { + mode: 'values', + values: [60, 1800, 3600, 43200, 86400, 604800], + density: 5, + format: { + to: secsToText, + from: function() {} + } + } }); - $(".name-edit-cancel").click(function(){ - var $form = $(this.parentNode); - var v = $form.data("originalValue"); + frequencySlider.noUiSlider.on("update", function(a, b, value) { + var rounded = Math.round(value); + $("#frequency-slider-value").text(secsToText(rounded)); + $("#update-timeout-timeout").val(rounded); + }); - $form - .addClass("inactive") - .find(".input-name").val(v); - return false; + var graceSlider = document.getElementById("grace-slider"); + noUiSlider.create(graceSlider, { + start: [20], + connect: "lower", + range: { + 'min': [60, 60], + '30%': [3600, 3600], + '82.80%': [86400, 86400], + 'max': 604800 + }, + pips: { + mode: 'values', + values: [60, 1800, 3600, 43200, 86400, 604800], + density: 5, + format: { + to: secsToText, + from: function() {} + } + } }); - $(".timeout").click(function() { - $(".timeout-cell").addClass("inactive"); + graceSlider.noUiSlider.on("update", function(a, b, value) { + var rounded = Math.round(value); + $("#grace-slider-value").text(secsToText(rounded)); + $("#update-timeout-grace").val(rounded); + }); + + + $('[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.text()); + $('#update-name-modal').modal("show"); - $cell = $(this.parentNode); - $cell.removeClass("inactive"); + return false; }); - $(".timeout-edit-cancel").click(function() { - $(this).parents("td").addClass("inactive"); + $(".timeout_grace").click(function() { + var $this = $(this); + + $("#update-timeout-form").attr("action", $this.data("url")); + frequencySlider.noUiSlider.set($this.data("timeout")) + graceSlider.noUiSlider.set($this.data("grace")) + $('#update-timeout-modal').modal("show"); + return false; }); @@ -48,4 +105,5 @@ $(function () { return false; }); + }); \ No newline at end of file diff --git a/static/js/nouislider.min.js b/static/js/nouislider.min.js new file mode 100644 index 00000000..5d41dbd0 --- /dev/null +++ b/static/js/nouislider.min.js @@ -0,0 +1,3 @@ +/*! nouislider - 8.0.2 - 2015-07-06 13:22:09 */ + +!function(a){if("function"==typeof define&&define.amd)define([],a);else if("object"==typeof exports){var b=require("fs");module.exports=a(),module.exports.css=function(){return b.readFileSync(__dirname+"/nouislider.min.css","utf8")}}else window.noUiSlider=a()}(function(){"use strict";function a(a){return a.filter(function(a){return this[a]?!1:this[a]=!0},{})}function b(a,b){return Math.round(a/b)*b}function c(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.defaultView||c.parentWindow,e=c.documentElement,f=d.pageXOffset;return/webkit.*Chrome.*Mobile/i.test(navigator.userAgent)&&(f=0),{top:b.top+d.pageYOffset-e.clientTop,left:b.left+f-e.clientLeft}}function d(a){return"number"==typeof a&&!isNaN(a)&&isFinite(a)}function e(a){var b=Math.pow(10,7);return Number((Math.round(a*b)/b).toFixed(7))}function f(a,b,c){j(a,b),setTimeout(function(){k(a,b)},c)}function g(a){return Math.max(Math.min(a,100),0)}function h(a){return Array.isArray(a)?a:[a]}function i(a){var b=a.split(".");return b.length>1?b[1].length:0}function j(a,b){a.classList?a.classList.add(b):a.className+=" "+b}function k(a,b){a.classList?a.classList.remove(b):a.className=a.className.replace(new RegExp("(^|\\b)"+b.split(" ").join("|")+"(\\b|$)","gi")," ")}function l(a,b){a.classList?a.classList.contains(b):new RegExp("(^| )"+b+"( |$)","gi").test(a.className)}function m(a,b){return 100/(b-a)}function n(a,b){return 100*b/(a[1]-a[0])}function o(a,b){return n(a,a[0]<0?b+Math.abs(a[0]):b-a[0])}function p(a,b){return b*(a[1]-a[0])/100+a[0]}function q(a,b){for(var c=1;a>=b[c];)c+=1;return c}function r(a,b,c){if(c>=a.slice(-1)[0])return 100;var d,e,f,g,h=q(c,a);return d=a[h-1],e=a[h],f=b[h-1],g=b[h],f+o([d,e],c)/m(f,g)}function s(a,b,c){if(c>=100)return a.slice(-1)[0];var d,e,f,g,h=q(c,b);return d=a[h-1],e=a[h],f=b[h-1],g=b[h],p([d,e],(c-f)*m(f,g))}function t(a,c,d,e){if(100===e)return e;var f,g,h=q(e,a);return d?(f=a[h-1],g=a[h],e-f>(g-f)/2?g:f):c[h-1]?a[h-1]+b(e-a[h-1],c[h-1]):e}function u(a,b,c){var e;if("number"==typeof b&&(b=[b]),"[object Array]"!==Object.prototype.toString.call(b))throw new Error("noUiSlider: 'range' contains invalid value.");if(e="min"===a?0:"max"===a?100:parseFloat(a),!d(e)||!d(b[0]))throw new Error("noUiSlider: 'range' value isn't numeric.");c.xPct.push(e),c.xVal.push(b[0]),e?c.xSteps.push(isNaN(b[1])?!1:b[1]):isNaN(b[1])||(c.xSteps[0]=b[1])}function v(a,b,c){return b?void(c.xSteps[a]=n([c.xVal[a],c.xVal[a+1]],b)/m(c.xPct[a],c.xPct[a+1])):!0}function w(a,b,c,d){this.xPct=[],this.xVal=[],this.xSteps=[d||!1],this.xNumSteps=[!1],this.snap=b,this.direction=c;var e,f=[];for(e in a)a.hasOwnProperty(e)&&f.push([a[e],e]);for(f.sort(function(a,b){return a[0]-b[0]}),e=0;e2)throw new Error("noUiSlider: 'start' option is incorrect.");a.handles=b.length,a.start=b}function A(a,b){if(a.snap=b,"boolean"!=typeof b)throw new Error("noUiSlider: 'snap' option must be a boolean.")}function B(a,b){if(a.animate=b,"boolean"!=typeof b)throw new Error("noUiSlider: 'animate' option must be a boolean.")}function C(a,b){if("lower"===b&&1===a.handles)a.connect=1;else if("upper"===b&&1===a.handles)a.connect=2;else if(b===!0&&2===a.handles)a.connect=3;else{if(b!==!1)throw new Error("noUiSlider: 'connect' option doesn't match handle count.");a.connect=0}}function D(a,b){switch(b){case"horizontal":a.ort=0;break;case"vertical":a.ort=1;break;default:throw new Error("noUiSlider: 'orientation' option is invalid.")}}function E(a,b){if(!d(b))throw new Error("noUiSlider: 'margin' option must be numeric.");if(a.margin=a.spectrum.getMargin(b),!a.margin)throw new Error("noUiSlider: 'margin' option is only supported on linear sliders.")}function F(a,b){if(!d(b))throw new Error("noUiSlider: 'limit' option must be numeric.");if(a.limit=a.spectrum.getMargin(b),!a.limit)throw new Error("noUiSlider: 'limit' option is only supported on linear sliders.")}function G(a,b){switch(b){case"ltr":a.dir=0;break;case"rtl":a.dir=1,a.connect=[0,2,1,3][a.connect];break;default:throw new Error("noUiSlider: 'direction' option was not recognized.")}}function H(a,b){if("string"!=typeof b)throw new Error("noUiSlider: 'behaviour' must be a string containing options.");var c=b.indexOf("tap")>=0,d=b.indexOf("drag")>=0,e=b.indexOf("fixed")>=0,f=b.indexOf("snap")>=0;a.events={tap:c||f,drag:d,fixed:e,snap:f}}function I(a,b){if(a.format=b,"function"==typeof b.to&&"function"==typeof b.from)return!0;throw new Error("noUiSlider: 'format' requires 'to' and 'from' methods.")}function J(a){var b,c={margin:0,limit:0,animate:!0,format:U};b={step:{r:!1,t:x},start:{r:!0,t:z},connect:{r:!0,t:C},direction:{r:!0,t:G},snap:{r:!1,t:A},animate:{r:!1,t:B},range:{r:!0,t:y},orientation:{r:!1,t:D},margin:{r:!1,t:E},limit:{r:!1,t:F},behaviour:{r:!0,t:H},format:{r:!1,t:I}};var d={connect:!1,direction:"ltr",behaviour:"tap",orientation:"horizontal"};return Object.keys(d).forEach(function(b){void 0===a[b]&&(a[b]=d[b])}),Object.keys(b).forEach(function(d){var e=b[d];if(void 0===a[d]){if(e.r)throw new Error("noUiSlider: '"+d+"' is required.");return!0}e.t(c,a[d])}),c.pips=a.pips,c.style=c.ort?"top":"left",c}function K(a,b,c){var d=a+b[0],e=a+b[1];return c?(0>d&&(e+=Math.abs(d)),e>100&&(d-=e-100),[g(d),g(e)]):[d,e]}function L(a){a.preventDefault();var b,c,d=0===a.type.indexOf("touch"),e=0===a.type.indexOf("mouse"),f=0===a.type.indexOf("pointer"),g=a;return 0===a.type.indexOf("MSPointer")&&(f=!0),d&&(b=a.changedTouches[0].pageX,c=a.changedTouches[0].pageY),(e||f)&&(b=a.clientX+window.pageXOffset,c=a.clientY+window.pageYOffset),g.points=[b,c],g.cursor=e||f,g}function M(a,b){var c=document.createElement("div"),d=document.createElement("div"),e=["-lower","-upper"];return a&&e.reverse(),j(d,T[3]),j(d,T[3]+e[b]),j(c,T[2]),c.appendChild(d),c}function N(a,b,c){switch(a){case 1:j(b,T[7]),j(c[0],T[6]);break;case 3:j(c[1],T[6]);case 2:j(c[0],T[7]);case 0:j(b,T[6])}}function O(a,b,c){var d,e=[];for(d=0;a>d;d+=1)e.push(c.appendChild(M(b,d)));return e}function P(a,b,c){j(c,T[0]),j(c,T[8+a]),j(c,T[4+b]);var d=document.createElement("div");return j(d,T[1]),c.appendChild(d),d}function Q(b,d){function e(a,b,c){if("range"===a||"steps"===a)return M.xVal;if("count"===a){var d,e=100/(b-1),f=0;for(b=[];(d=f++*e)<=100;)b.push(d);a="positions"}return"positions"===a?b.map(function(a){return M.fromStepping(c?M.getStep(a):a)}):"values"===a?c?b.map(function(a){return M.fromStepping(M.getStep(M.toStepping(a)))}):b:void 0}function m(b,c,d){var e=M.direction,f={},g=M.xVal[0],h=M.xVal[M.xVal.length-1],i=!1,j=!1,k=0;return M.direction=0,d=a(d.slice().sort(function(a,b){return a-b})),d[0]!==g&&(d.unshift(g),i=!0),d[d.length-1]!==h&&(d.push(h),j=!0),d.forEach(function(a,e){var g,h,l,m,n,o,p,q,r,s,t=a,u=d[e+1];if("steps"===c&&(g=M.xNumSteps[e]),g||(g=u-t),t!==!1&&void 0!==u)for(h=t;u>=h;h+=g){for(m=M.toStepping(h),n=m-k,q=n/b,r=Math.round(q),s=n/r,l=1;r>=l;l+=1)o=k+l*s,f[o.toFixed(5)]=["x",0];p=d.indexOf(h)>-1?1:"steps"===c?2:0,!e&&i&&(p=0),h===u&&j||(f[m.toFixed(5)]=[h,p]),k=m}}),M.direction=e,f}function n(a,b,c){function e(a){return["-normal","-large","-sub"][a]}function f(a,b,c){return'class="'+b+" "+b+"-"+h+" "+b+e(c[1])+'" style="'+d.style+": "+a+'%"'}function g(a,d){M.direction&&(a=100-a),d[1]=d[1]&&b?b(d[0],d[1]):d[1],i.innerHTML+="
",d[1]&&(i.innerHTML+="
"+c.to(d[0])+"
")}var h=["horizontal","vertical"][d.ort],i=document.createElement("div");return j(i,"noUi-pips"),j(i,"noUi-pips-"+h),Object.keys(a).forEach(function(b){g(b,a[b])}),i}function o(a){var b=a.mode,c=a.density||1,d=a.filter||!1,f=a.values||!1,g=a.stepped||!1,h=e(b,f,g),i=m(c,b,h),j=a.format||{to:Math.round};return I.appendChild(n(i,d,j))}function p(){return G["offset"+["Width","Height"][d.ort]]}function q(a,b){void 0!==b&&(b=Math.abs(b-d.dir)),Object.keys(R).forEach(function(c){var d=c.split(".")[0];a===d&&R[c].forEach(function(a){a(h(B()),b,r(Array.prototype.slice.call(Q)))})})}function r(a){return 1===a.length?a[0]:d.dir?a.reverse():a}function s(a,b,c,e){var f=function(b){return I.hasAttribute("disabled")?!1:l(I,T[14])?!1:(b=L(b),a===S.start&&void 0!==b.buttons&&b.buttons>1?!1:(b.calcPoint=b.points[d.ort],void c(b,e)))},g=[];return a.split(" ").forEach(function(a){b.addEventListener(a,f,!1),g.push([a,f])}),g}function t(a,b){var c,d,e=b.handles||H,f=!1,g=100*(a.calcPoint-b.start)/p(),h=e[0]===H[0]?0:1;if(c=K(g,b.positions,e.length>1),f=y(e[0],c[h],1===e.length),e.length>1){if(f=y(e[1],c[h?0:1],!1)||f)for(d=0;d1&&j(I,T[12]);var f=function(){return!1};document.body.noUiListener=f,document.body.addEventListener("selectstart",f,!1)}}function w(a){var b,e,g=a.calcPoint,h=0;return a.stopPropagation(),H.forEach(function(a){h+=c(a)[d.style]}),b=h/2>g||1===H.length?0:1,g-=c(G)[d.style],e=100*g/p(),d.events.snap||f(I,T[14],300),H[b].hasAttribute("disabled")?!1:(y(H[b],e),q("slide",b),q("set",b),q("change",b),void(d.events.snap&&v(a,{handles:[H[h]]})))}function x(a){var b,c;if(!a.fixed)for(b=0;b1&&(b=e?Math.max(b,f):Math.min(b,h)),c!==!1&&d.limit&&H.length>1&&(b=e?Math.min(b,i):Math.max(b,l)),b=M.getStep(b),b=g(parseFloat(b.toFixed(7))),b===J[e]?!1:(a.style[d.style]=b+"%",a.previousSibling||(k(a,T[17]),b>50&&j(a,T[17])),J[e]=b,Q[e]=M.fromStepping(b),q("update",e),!0)}function z(a,b){var c,e,f;for(d.limit&&(a+=1),c=0;a>c;c+=1)e=c%2,f=b[e],null!==f&&f!==!1&&("number"==typeof f&&(f=String(f)),f=d.format.from(f),(f===!1||isNaN(f)||y(H[e],M.toStepping(f),c===3-d.dir)===!1)&&q("update",e))}function A(a){var b,c,e=h(a);for(d.dir&&d.handles>1&&e.reverse(),d.animate&&-1!==J[0]&&f(I,T[14],300),b=H.length>1?3:1,1===e.length&&(b=1),z(b,e),c=0;c=c[1]?c[2]:c[0]||!1;return[h,f]});return r(a)}function E(a,b){R[a]=R[a]||[],R[a].push(b),"update"===a.split(".")[0]&&H.forEach(function(a,b){q("update",b)})}function F(a){var b=a.split(".")[0],c=a.substring(b.length);Object.keys(R).forEach(function(a){var d=a.split(".")[0],e=a.substring(d.length);b&&b!==d||c&&c!==e||delete R[a]})}var G,H,I=b,J=[-1,-1],M=d.spectrum,Q=[],R={};if(I.noUiSlider)throw new Error("Slider was already initialized.");return G=P(d.dir,d.ort,I),H=O(d.handles,d.dir,G),N(d.connect,I,H),x(d.events),d.pips&&o(d.pips),{destroy:C,steps:D,on:E,off:F,get:B,set:A}}function R(a,b){if(!a.nodeName)throw new Error("noUiSlider.create requires a single element.");var c=J(b,a),d=Q(a,c);d.set(c.start),a.noUiSlider=d}var S=window.navigator.pointerEnabled?{start:"pointerdown",move:"pointermove",end:"pointerup"}:window.navigator.msPointerEnabled?{start:"MSPointerDown",move:"MSPointerMove",end:"MSPointerUp"}:{start:"mousedown touchstart",move:"mousemove touchmove",end:"mouseup touchend"},T=["noUi-target","noUi-base","noUi-origin","noUi-handle","noUi-horizontal","noUi-vertical","noUi-background","noUi-connect","noUi-ltr","noUi-rtl","noUi-dragable","","noUi-state-drag","","noUi-state-tap","noUi-active","","noUi-stacking"];w.prototype.getMargin=function(a){return 2===this.xPct.length?n(this.xVal,a):!1},w.prototype.toStepping=function(a){return a=r(this.xVal,this.xPct,a),this.direction&&(a=100-a),a},w.prototype.fromStepping=function(a){return this.direction&&(a=100-a),e(s(this.xVal,this.xPct,a))},w.prototype.getStep=function(a){return this.direction&&(a=100-a),a=t(this.xPct,this.xSteps,this.snap,a),this.direction&&(a=100-a),a},w.prototype.getApplicableStep=function(a){var b=q(a,this.xPct),c=100===a?2:1;return[this.xNumSteps[b-2],this.xVal[b-c],this.xNumSteps[b-c]]},w.prototype.convert=function(a){return this.getStep(this.toStepping(a))};var U={to:function(a){return a.toFixed(2)},from:Number};return{create:R}}); \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index e7fd7ba7..22936b9f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,6 +9,8 @@ {% load staticfiles %} + + diff --git a/templates/front/my_checks.html b/templates/front/my_checks.html index 15aafa2c..6584bf53 100644 --- a/templates/front/my_checks.html +++ b/templates/front/my_checks.html @@ -14,7 +14,10 @@ Name URL - Frequency + + Frequency
+ Grace + Last Ping @@ -30,62 +33,31 @@ {% endif %} -
- {% csrf_token %} - - - - -
+ {{ check.name }} {{ check.url }} -
-
-
-
- {% csrf_token %} - - - -
-
-
- - {% for label, value in timeout_choices %} + + {% for label, value in duration_choices %} {% if check.timeout == value %} {{ label }} {% endif %} {% endfor %} +
+ + {% for label, value in duration_choices %} + {% if check.grace == value %} + {{ label }} + {% endif %} + {% endfor %} +
@@ -135,11 +107,89 @@ + + + +