Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Inital automated status check work
  • Loading branch information
audaciouscode committed May 30, 2017
1 parent 51bc87b commit ed4021a
Show file tree
Hide file tree
Showing 14 changed files with 406 additions and 7 deletions.
9 changes: 8 additions & 1 deletion admin.py
@@ -1,7 +1,7 @@
from django.contrib.gis import admin

from .models import DataPoint, DataBundle, DataSource, DataSourceGroup, \
DataPointVisualizations, ReportJob
DataPointVisualizations, ReportJob, DataSourceAlert

@admin.register(DataPointVisualizations)
class DataPointVisualizationsAdmin(admin.OSMGeoAdmin):
Expand Down Expand Up @@ -34,3 +34,10 @@ class DataSourceAdmin(admin.OSMGeoAdmin):
class ReportJobAdmin(admin.OSMGeoAdmin):
list_display = ('requester', 'requested', 'started', 'completed')
list_filter = ('requested', 'started', 'completed',)

@admin.register(DataSourceAlert)
class DataSourceAlertAdmin(admin.OSMGeoAdmin):
list_display = ('alert_name', 'data_source', 'generator_identifier', 'active', 'alert_level', \
'created', 'updated',)
list_filter = ('active', 'alert_level', 'created', 'alert_name', 'generator_identifier', \
'data_source',)
15 changes: 15 additions & 0 deletions management/commands/pdk_run_status_checks.py
@@ -0,0 +1,15 @@
from django.core.management import call_command, get_commands
from django.core.management.base import BaseCommand

from ...decorators import handle_lock

class Command(BaseCommand):
help = 'Runs data sanity checks to generate any alerts for potential data issues.'

@handle_lock
def handle(self, *args, **options):
command_names = get_commands().keys()

for command_name in command_names:
if command_name.startswith('pdk_status_check_'):
call_command(command_name)
87 changes: 87 additions & 0 deletions management/commands/pdk_status_check_battery.py
@@ -0,0 +1,87 @@
# pylint: disable=line-too-long, no-member

from django.core.management.base import BaseCommand
from django.utils import timezone

from ...decorators import handle_lock

from ...models import DataPoint, DataSource, DataSourceAlert

GENERATOR = 'pdk-device-battery'
CRITICAL_LEVEL = 20
WARNING_LEVEL = 33

class Command(BaseCommand):
help = 'Runs the battery level status check to alert when battery level is low.'

@handle_lock
def handle(self, *args, **options): # pylint: disable=too-many-branches, too-many-statements
for source in DataSource.objects.all():
last_battery = DataPoint.objects.filter(source=source.identifier, generator_identifier=GENERATOR).order_by('-created').first()
last_alert = DataSourceAlert.objects.filter(data_source=source, generator_identifier=GENERATOR, active=True).order_by('-created').first()

alert_name = None
alert_details = {}
alert_level = 'info'

if last_battery is not None:
properties = last_battery.fetch_properties()

if properties['level'] < CRITICAL_LEVEL:
alert_name = 'Battery Level Critically Low'
alert_details['message'] = 'Latest battery level is ' + str(properties['level']) + '%.'
alert_level = 'critical'

elif properties['level'] < WARNING_LEVEL:
alert_name = 'Battery Level Low'
alert_details['message'] = 'Latest battery level is ' + str(properties['level']) + '%.'
alert_level = 'warning'

if alert_name is not None:
if last_alert is None or last_alert.alert_name != alert_name or last_alert.alert_level != alert_level:
if last_alert is not None:
last_alert.active = False
last_alert.updated = timezone.now()
last_alert.save()

new_alert = DataSourceAlert(alert_name=alert_name, data_source=source, generator_identifier=GENERATOR)
new_alert.alert_level = alert_level
new_alert.update_alert_details(alert_details)
new_alert.created = timezone.now()
new_alert.updated = timezone.now()
new_alert.active = True

new_alert.save()
else:
last_alert.updated = timezone.now()
last_alert.update_alert_details(alert_details)

last_alert.save()
elif last_alert is not None:
last_alert.updated = timezone.now()
last_alert.active = False

last_alert.save()
else:
alert_name = 'No Battery Levels Logged'
alert_details['message'] = 'No battery levels have been logged for this device yet.'

if last_alert is None or last_alert.alert_name != alert_name or last_alert.alert_level != alert_level:
if last_alert is not None:
last_alert.active = False
last_alert.updated = timezone.now()
last_alert.save()

new_alert = DataSourceAlert(alert_name=alert_name, data_source=source, generator_identifier=GENERATOR)
new_alert.alert_level = alert_level
new_alert.update_alert_details(alert_details)
new_alert.created = timezone.now()
new_alert.updated = timezone.now()
new_alert.active = True

new_alert.save()
else:
last_alert.updated = timezone.now()
last_alert.update_alert_details(alert_details)

last_alert.save()
31 changes: 31 additions & 0 deletions migrations/0017_datasourcealert.py
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.1 on 2017-05-30 14:21
# pylint: skip-file

from __future__ import unicode_literals

import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('passive_data_kit', '0016_datapoint_secondary_identifier'),
]

operations = [
migrations.CreateModel(
name='DataSourceAlert',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('alert_name', models.CharField(max_length=1024)),
('alert_details', django.contrib.postgres.fields.jsonb.JSONField()),
('generator_identifier', models.CharField(blank=True, max_length=1024, null=True)),
('created', models.DateTimeField()),
('active', models.BooleanField(default=True)),
('data_source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='alerts', to='passive_data_kit.DataSource')),
],
),
]
22 changes: 22 additions & 0 deletions migrations/0018_datasourcealert_updated.py
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.1 on 2017-05-30 14:38
# pylint: skip-file

from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('passive_data_kit', '0017_datasourcealert'),
]

operations = [
migrations.AddField(
model_name='datasourcealert',
name='updated',
field=models.DateTimeField(blank=True, null=True),
),
]
22 changes: 22 additions & 0 deletions migrations/0019_datasourcealert_alert_level.py
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.1 on 2017-05-30 14:56
# pylint: skip-file

from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('passive_data_kit', '0018_datasourcealert_updated'),
]

operations = [
migrations.AddField(
model_name='datasourcealert',
name='alert_level',
field=models.CharField(choices=[('info', 'Informative'), ('warning', 'Warning'), ('critical', 'Critical')], default='info', max_length=64),
),
]
34 changes: 34 additions & 0 deletions models.py
Expand Up @@ -161,6 +161,40 @@ def generator_statistics(self):

return generators

ALERT_LEVEL_CHOICES = (
('info', 'Informative'),
('warning', 'Warning'),
('critical', 'Critical'),
)

class DataSourceAlert(models.Model):
alert_name = models.CharField(max_length=1024)
alert_level = models.CharField(max_length=64, choices=ALERT_LEVEL_CHOICES, default='info')

if install_supports_jsonfield():
alert_details = JSONField()
else:
alert_details = models.TextField(max_length=(32 * 1024 * 1024 * 1024))

data_source = models.ForeignKey(DataSource, related_name='alerts')
generator_identifier = models.CharField(max_length=1024, null=True, blank=True)

created = models.DateTimeField()
updated = models.DateTimeField(null=True, blank=True)

active = models.BooleanField(default=True)

def fetch_alert_details(self):
if install_supports_jsonfield():
return self.alert_details

return json.loads(self.alert_details)

def update_alert_details(self, details):
if install_supports_jsonfield():
self.alert_details = details
else:
self.alert_details = json.dumps(details, indent=2)

class DataPointVisualizations(models.Model):
source = models.CharField(max_length=1024, db_index=True)
Expand Down
2 changes: 1 addition & 1 deletion templates/pdk_base.html
Expand Up @@ -8,7 +8,7 @@
<meta name="description" content="" />
<meta name="author" content="" />
<link rel="icon" href="../../favicon.ico" />
<title>Dashboard Template for Bootstrap</title>
<title>Passive Data Kit</title>
<link href="{% static 'pdk/css/lib/bootstrap.min.css' %}" rel="stylesheet" />
<link href="{% static 'pdk/css/lib/bootstrap-theme.min.css' %}" rel="stylesheet" />
<link href="{% static 'pdk/css/lib/bootstrap-table.css' %}" rel="stylesheet" />
Expand Down
6 changes: 4 additions & 2 deletions templates/pdk_home.html
Expand Up @@ -4,7 +4,9 @@

{% block sidebar %}
<ul class="nav nav-sidebar">
<li><a href="#alerts">Alerts <span class="badge pull-right">?</span></a></li>
<li>
<a href="#alerts">Alerts {% system_alerts_badge %}</a>
</li>
</ul>
<ul class="nav nav-sidebar">
<li><label>Groups</label></li>
Expand All @@ -28,7 +30,7 @@ <h2 class="sub-header"><a name="alerts"></a>Alerts</h2>
</div>

<div class="col-md-12">
<div class="alert alert-info" role="alert">Alerts will show up here once sanity checks are wired up...</div>
{% system_alerts_table %}
</div>
</div>
{% for group in groups %}
Expand Down
9 changes: 6 additions & 3 deletions templates/pdk_source.html
Expand Up @@ -4,7 +4,7 @@

{% block sidebar %}
<ul class="nav nav-sidebar">
<li><a href="#alerts">Alerts <span class="badge pull-right">?</span></a></li>
<li><a href="#alerts">Alerts {% source_alerts_badge source %}</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><label>TODO</label></li>
Expand All @@ -29,7 +29,7 @@ <h3 class="sub-header"><a name="alerts"></a>Alerts</h3>
</div>

<div class="col-md-12">
<div class="alert alert-info" role="alert">Alerts will show up here once sanity checks are wired up...</div>
{% source_alerts_table source %}
</div>
</div>

Expand All @@ -43,4 +43,7 @@ <h3 class="sub-header"><a name="generators"></a>Generators</h3>
</div>

<script data-main="{% static 'pdk/js/home.js' %}" src="{% static 'pdk/js/lib/require.js' %}"></script>
{% endblock %}
{% endblock %}


pdk_run_status_checks
1 change: 1 addition & 0 deletions templates/tag_date_ago.html
@@ -1 +1,2 @@
<span style="display: none;">{{ date.isoformat }}</span>
<span data-toggle="tooltip" data-placement="top" title="{{ date }}">{{ ago }}</span>
47 changes: 47 additions & 0 deletions templates/tag_source_alerts_table.html
@@ -0,0 +1,47 @@
{% load passive_data_kit %}
<table class="alerts_table table-striped" data-toggle="table" data-pagination="true" data-search="true" style="z-index: 10;">
<thead>
<tr>
<th data-sortable="true"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span></th>
<th data-sortable="true">Generator</th>
<th data-sortable="true" data-sort-order="desc">Created</th>
<th data-sortable="true" data-sort-order="desc">Updated</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{% for alert in alerts %}
{% if alert.alert_level == 'warning' %}
<tr class="warning">
<td>
<span style="display: none;">3</span>
<span class="glyphicon glyphicon-alert" aria-hidden="true">
</td>
{% elif alert.alert_level == 'critical' %}
<tr class="danger">
<td>
<span style="display: none;">2</span>
<span class="glyphicon glyphicon-alert" aria-hidden="true">
</td>
{% else %}
<tr>
<td>
<span style="display: none;">1</span>
<span class="glyphicon glyphicon-info-sign" aria-hidden="true">
</td>
{% endif %}
<td>
<span style="display: none;">{{ alert.data_source.name }}</span>
<a href="{% url 'pdk_source' alert.data_source.identifier %}">{{ alert.data_source.name }}</a>
</td>
<td>
{% date_ago alert.created %}
</td>
<td>
{% date_ago alert.updated %}
</td>
<td>{{ alert.fetch_alert_details.message }}</td>
</tr>
{% endfor %}
</tbody>
</table>

0 comments on commit ed4021a

Please sign in to comment.