Skip to content

Commit

Permalink
[feature #156577840] add nag mode to the dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
tibetegya authored and george tibetegya committed May 11, 2018
1 parent 657317a commit 20b6ab4
Show file tree
Hide file tree
Showing 17 changed files with 139 additions and 49 deletions.
32 changes: 0 additions & 32 deletions Pipfile

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

![Screenshot of My Checks page](/stuff/screenshots/my_checks.png?raw=true "My Checks Page")

![Screenshot of Period/Grace dialog](/stuff/screenshots/period_grace.png?raw=true "Period/Grace Dialog")
![Screenshot of Period/Grace dialog](/stuff/screenshots/nag_period_grace.png?raw=true "Period/Grace Dialog")

![Screenshot of Channels page](/stuff/screenshots/channels.png?raw=true "Channels Page")

Expand Down
6 changes: 5 additions & 1 deletion hc/api/management/commands/sendalerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ def handle_many(self):
going_down = query.filter(alert_after__lt=now, status="up") | query.filter(alert_after__lt=now, status="often")
going_up = query.filter(alert_after__gt=now, status="down")

# running_often = query.filter(alert_after__gt=now, status="often")
for_nag = query.filter(nag_after__lt=now, status="down", nag_mode=True)
# Don't combine this in one query so Postgres can query using index:
checks=list(going_down.iterator())+list(going_up.iterator())
checks = list(going_down.iterator()) + list(going_up.iterator()) + list(for_nag.iterator())
if not checks:
return False

Expand All @@ -45,6 +47,8 @@ def handle_one(self, check):
# Save the new status. If sendalerts crashes,
# it won't process this check again.
check.status = check.get_status()
if check.status == 'down' and timezone.now() > (check.last_ping + check.timeout + check.grace):
check.nag_after = timezone.now() + check.nag
check.save()
tmpl = "\nSending alert, status=%s, code=%s\n"
self.stdout.write(tmpl % (check.status, check.code))
Expand Down
7 changes: 5 additions & 2 deletions hc/api/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2018-05-11 07:47
# Generated by Django 1.10 on 2018-05-11 11:38
from __future__ import unicode_literals

import datetime
Expand All @@ -14,8 +14,8 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
Expand Down Expand Up @@ -47,9 +47,12 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(auto_now_add=True)),
('timeout', models.DurationField(default=datetime.timedelta(1))),
('grace', models.DurationField(default=datetime.timedelta(0, 3600))),
('nag', models.DurationField(default=datetime.timedelta(0, 3600))),
('nag_mode', models.BooleanField(default=False)),
('n_pings', models.IntegerField(default=0)),
('last_ping', models.DateTimeField(blank=True, null=True)),
('alert_after', models.DateTimeField(blank=True, editable=False, null=True)),
('nag_after', models.DateTimeField(blank=True, editable=False, null=True)),
('status', models.CharField(choices=[('up', 'Up'), ('down', 'Down'), ('new', 'New'), ('paused', 'Paused'), ('often', 'Often')], default='new', max_length=6)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
Expand Down
4 changes: 4 additions & 0 deletions hc/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
)
DEFAULT_TIMEOUT = td(days=1)
DEFAULT_GRACE = td(hours=1)
DEFAULT_NAG = td(hours=1)
CHANNEL_KINDS = (("email", "Email"), ("webhook", "Webhook"),
("hipchat", "HipChat"),
("slack", "Slack"), ("pd", "PagerDuty"), ("po", "Pushover"),
Expand Down Expand Up @@ -50,9 +51,12 @@ class Meta:
created = models.DateTimeField(auto_now_add=True)
timeout = models.DurationField(default=DEFAULT_TIMEOUT)
grace = models.DurationField(default=DEFAULT_GRACE)
nag = models.DurationField(default=DEFAULT_NAG)
nag_mode = models.BooleanField(default=False)
n_pings = models.IntegerField(default=0)
last_ping = models.DateTimeField(null=True, blank=True)
alert_after = models.DateTimeField(null=True, blank=True, editable=False)
nag_after = models.DateTimeField(null=True, blank=True, editable=False)
status = models.CharField(max_length=6, choices=STATUSES, default="new")

def name_then_code(self):
Expand Down
2 changes: 2 additions & 0 deletions hc/api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"tags": {"type": "string"},
"timeout": {"type": "number", "minimum": 60, "maximum": 604800},
"grace": {"type": "number", "minimum": 60, "maximum": 604800},
"nag": {"type": "number", "minimum": 60, "maximum": 604800},
"nag_mode": {"type": "string"},
"channels": {"type": "string"}
}
}
2 changes: 2 additions & 0 deletions hc/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ def checks(request):
check.timeout = td(seconds=request.json["timeout"])
if "grace" in request.json:
check.grace = td(seconds=request.json["grace"])
if "nag" in request.json:
check.nag = td(seconds=request.json["nag"])

check.save()

Expand Down
7 changes: 4 additions & 3 deletions hc/front/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ def clean_tags(self):


class TimeoutForm(forms.Form):
timeout = forms.IntegerField(min_value=60, max_value=31104000)
grace = forms.IntegerField(min_value=60, max_value=31104000)

timeout = forms.IntegerField(min_value=60, max_value=2592000)
grace = forms.IntegerField(min_value=60, max_value=2592000)
nag = forms.IntegerField(min_value=60, max_value=2592000)
nag_mode = forms.CharField(required=False)

class AddChannelForm(forms.ModelForm):

Expand Down
6 changes: 3 additions & 3 deletions hc/front/tests/test_update_timeout.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def setUp(self):

def test_it_works(self):
url = "/checks/%s/timeout/" % self.check.code
payload = {"timeout": 3600, "grace": 60}
payload = {"timeout": 3600, "grace": 60, "nag": 60}

self.client.login(username="alice@example.org", password="password")
r = self.client.post(url, data=payload)
Expand All @@ -23,7 +23,7 @@ def test_it_works(self):

def test_team_access_works(self):
url = "/checks/%s/timeout/" % self.check.code
payload = {"timeout": 7200, "grace": 60}
payload = {"timeout": 7200, "grace": 60, "nag": 60}

# Logging in as bob, not alice. Bob has team access so this
# should work.
Expand All @@ -35,7 +35,7 @@ def test_team_access_works(self):

def test_it_handles_bad_uuid(self):
url = "/checks/not-uuid/timeout/"
payload = {"timeout": 3600, "grace": 60}
payload = {"timeout": 3600, "grace": 60, "nag": 60}

self.client.login(username="alice@example.org", password="password")
r = self.client.post(url, data=payload)
Expand Down
8 changes: 7 additions & 1 deletion hc/front/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ def docs_api(request):
"SITE_ROOT": settings.SITE_ROOT,
"PING_ENDPOINT": settings.PING_ENDPOINT,
"default_timeout": int(DEFAULT_TIMEOUT.total_seconds()),
"default_grace": int(DEFAULT_GRACE.total_seconds())
"default_grace": int(DEFAULT_GRACE.total_seconds()),
"default_nag": int(DEFAULT_NAG.total_seconds())
}

return render(request, "front/docs_api.html", ctx)
Expand Down Expand Up @@ -295,6 +296,11 @@ def update_timeout(request, code):
if form.is_valid():
check.timeout = td(seconds=form.cleaned_data["timeout"])
check.grace = td(seconds=form.cleaned_data["grace"])
check.nag = td(seconds=form.cleaned_data["nag"])
if 'nag_state' in request.POST:
check.nag_mode = True
else:
check.nag_mode = False
check.save()

return redirect("hc-checks")
Expand Down
6 changes: 2 additions & 4 deletions hc/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
HOST = "localhost"
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

HOST = "localhost"
SECRET_KEY = os.environ.get("HC_SECRET_KEY")
DEBUG = True
ALLOWED_HOSTS = []
Expand Down Expand Up @@ -137,7 +136,7 @@

USE_TZ = True

SITE_ROOT = "http://localhost:8000"
SITE_ROOT = os.environ.get("HC_APP_HOST") or "http://localhost:8000"
PING_ENDPOINT = SITE_ROOT + "/ping/"
PING_EMAIL_DOMAIN = HOST
STATIC_URL = '/static/'
Expand All @@ -151,14 +150,13 @@
COMPRESS_OFFLINE = True

# EMAIL_BACKEND = "djmail.backends.default.EmailBackend"
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get("EMAIL_HOST")
EMAIL_PORT = os.environ.get("EMAIL_PORT")
EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD")
EMAIL_USE_TLS = True

# Slack integration -- override these in local_settings
SLACK_CLIENT_ID = None
SLACK_CLIENT_SECRET = None
Expand Down
27 changes: 25 additions & 2 deletions static/css/my_checks.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,34 @@
background: #f0ad4e;
}

#period-slider .noUi-value, #grace-slider .noUi-value {
#nag-slider {
margin: 20px 50px 140px 50px;
}

#nag-slider.noUi-connect {
background: #ee1010;
}

#period-slider .noUi-value, #grace-slider .noUi-value, #nag-slider .noUi-value {
width: 60px;
margin-left: -30px;
}

#my-box {
position: relative;
}
#inner-box {
position: absolute;
top: -40px;
left: 110px;
}
#inner-box-2 {
position: absolute;
top: -30px;
left: 130px;
}
#nag-text-help {
font-weight: bold;
}
.update-timeout-terms {
color: #999;
}
Expand Down
37 changes: 37 additions & 0 deletions static/js/checks.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,33 @@ $(function () {
$("#update-timeout-grace").val(rounded);
});

var nagSlider = document.getElementById("nag-slider");
noUiSlider.create(nagSlider, {
start: [20],
connect: "lower",
range: {
'min': [60, 60],
'33%': [3600, 3600],
'66%': [86400, 86400],
'83%': [604800, 604800],
'max': 2592000,
},
pips: {
mode: 'values',
values: [60, 1800, 3600, 43200, 86400, 604800, 2592000],
density: 4,
format: {
to: secsToText,
from: function() {}
}
}
});

nagSlider.noUiSlider.on("update", function(a, b, value) {
var rounded = Math.round(value);
$("#nag-slider-value").text(secsToText(rounded));
$("#update-timeout-nag").val(rounded);
});

$('[data-toggle="tooltip"]').tooltip();

Expand All @@ -108,6 +135,16 @@ $(function () {
$("#update-timeout-form").attr("action", $this.data("url"));
periodSlider.noUiSlider.set($this.data("timeout"))
graceSlider.noUiSlider.set($this.data("grace"))
nagSlider.noUiSlider.set($this.data("nag"))
if ($this.data("nag-mode") === "True") {
$("#nag-text").text("ON").addClass("label label-success");
$('#update-timeout-nag-mode').prop('checked', true);
$("#nag-text-help").text("Uncheck to turn off");
} else {
$("#nag-text").text("OFF").addClass("label label-default");
$('#update-timeout-nag-mode').prop('checked', false);
$("#nag-text-help").text("Check to turn On");
}
$('#update-timeout-modal').modal({"show":true, "backdrop":"static"});

return false;
Expand Down
Binary file added stuff/screenshots/nag_period_grace.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions templates/front/my_checks.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ <h4>
<button class="btn btn-danger btn-xs" data-toggle="button">{{ tag }}</button>
{% elif tag in grace_tags %}
<button class="btn btn-warning btn-xs" data-toggle="button">{{ tag }}</button>
{% elif tag in nag_tags %}
<button class="btn btn-danger btn-xs" data-toggle="button">{{ tag }}</button>
{% else %}
<button class="btn btn-default btn-xs" data-toggle="button">{{ tag }}</button>
{% endif %}
Expand Down Expand Up @@ -123,6 +125,7 @@ <h4 class="update-timeout-title">Name and Tags</h4>
{% csrf_token %}
<input type="hidden" name="timeout" id="update-timeout-timeout" />
<input type="hidden" name="grace" id="update-timeout-grace" />
<input type="hidden" name="nag" id="update-timeout-nag" />
<div class="modal-content">
<div class="modal-body">
<div class="update-timeout-info text-center">
Expand Down Expand Up @@ -156,6 +159,30 @@ <h4 class="update-timeout-title">Name and Tags</h4>

<div id="grace-slider"></div>

<div class="update-timeout-info text-center">
<span
class="update-timeout-label"
data-toggle="tooltip"
title="When a check is unresolved and a user does not address the issue they will be Reminded
to resolve the issue until they do for this specified interval">
Reminder
</span>
<span
id="nag-slider-value"
class="update-timeout-value">
1 day
</span>
</div>

<div id="nag-slider"></div>
<p>
<span>Nag Mode</span>
<span id="nag-text"></span>
<div id="my-box">
<div id="inner-box"><label class="checkbox-inline"><input id="update-timeout-nag-mode" name="nag_state" type="checkbox" ></label></div>
<div id="inner-box-2"><span id="nag-text-help"></span></div>
</div>
</p>
<div class="update-timeout-terms">
<p>
<span>Period</span>
Expand All @@ -165,6 +192,11 @@ <h4 class="update-timeout-title">Name and Tags</h4>
<span>Grace Time</span>
When a check is late, how much time to wait until alert is sent.
</p>
<p>
<span>Reminder Interval</span>
When a check is unresolved and a user does not address the issue they will be Reminded
to resolve the issue until they do for this specified interval .
</p>
</div>

</div>
Expand Down
4 changes: 4 additions & 0 deletions templates/front/my_checks_desktop.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,15 @@
data-url="{% url 'hc-update-timeout' check.code %}"
data-timeout="{{ check.timeout.total_seconds }}"
data-grace="{{ check.grace.total_seconds }}"
data-nag="{{ check.nag.total_seconds }}"
data-nag-mode="{{ check.nag_mode }}"
class="timeout-grace">
{{ check.timeout|hc_duration }}
<br />
<span class="checks-subline">
{{ check.grace|hc_duration }}
<br/>
{{ check.nag|hc_duration }}
</span>
</span>
</td>
Expand Down
Loading

0 comments on commit 20b6ab4

Please sign in to comment.