diff --git a/runtests.py b/runtests.py index 672c275..ac76ef7 100644 --- a/runtests.py +++ b/runtests.py @@ -41,4 +41,4 @@ def run_tests(*test_args): if __name__ == '__main__': - run_tests(*sys.argv[1:]) \ No newline at end of file + run_tests(*sys.argv[1:]) diff --git a/tests/test_views.py b/tests/test_views.py index 41f34cb..2e1505b 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -118,3 +118,10 @@ def test_response_404_when_none_specified(self): def tearDown(self): pass + + +class TestWatchmanDashboard(unittest.TestCase): + def test_dashboard_response_code(self): + request = RequestFactory().get('/') + response = views.dashboard(request) + self.assertEqual(response.status_code, 200) diff --git a/watchman/templates/watchman/dashboard.html b/watchman/templates/watchman/dashboard.html new file mode 100644 index 0000000..d5d1875 --- /dev/null +++ b/watchman/templates/watchman/dashboard.html @@ -0,0 +1,87 @@ +{% load i18n %} + + + + + + + + + + + {% block content %} +
+

{% trans 'System Status' %}

+

+ {% if overall_status %} + {% trans 'All systems up and running!' %} + {% else %} + {% trans 'WARNING - Some systems down!' %} + {% endif %}{# overall_status #} +

+ + + + + + + + + + + + {% for type in checks %} + {% for status in type.statuses %} + + + + + {% if status.ok %} + + + {% else %} + + {% endif %}{# status.ok #} + + + {% endfor %}{# for status in type.statuses #} + {% empty %} + + + + {% endfor %}{# for type in checks #} + +
{% trans 'Type' %}{% trans 'Name' %}{% trans 'Status' %}
{{ type.type|title }}{{ status.name|title }}{% trans 'OK' %} + +
{% trans 'No checks indicated.' %}
+ + {% if not overall_status %}{% trans 'Click an error button to see the full traceback' %}{% endif %}{# overall_status #} +
+ {% endblock %}{# status_content #} + + + {% for type in checks %} + {% for status in type.statuses %} + {% if not status.ok %} + + {% endif %}{# not status.ok #} + {% endfor %}{# for type in checks #} + {% endfor %}{# for status in type.statuses #} + + diff --git a/watchman/urls.py b/watchman/urls.py index fdb1f57..01ec8b0 100644 --- a/watchman/urls.py +++ b/watchman/urls.py @@ -5,4 +5,5 @@ urlpatterns = patterns( '', url(r'^$', 'watchman.views.status', name="status"), + url(r'^dashboard/$', 'watchman.views.dashboard', name="dashboard"), ) diff --git a/watchman/views.py b/watchman/views.py index d2363d3..fcc9004 100644 --- a/watchman/views.py +++ b/watchman/views.py @@ -3,6 +3,8 @@ from __future__ import unicode_literals from django.http import Http404 +from django.shortcuts import render +from django.utils.translation import ugettext as _ from jsonview.decorators import json_view from watchman.decorators import token_required @@ -28,6 +30,87 @@ def status(request): response.update(check()) if len(response) == 0: - raise Http404('No checks found') + raise Http404(_('No checks found')) return response + + +@token_required +def dashboard(request): + check_types = [] + + for check in get_checks(None, None): + if callable(check): + _check = check() + + for _type in _check: + # For other systems (eg: email, storage) _check[_type] is a + # dictionary of status + # + # Example: + # { + # 'ok': True, # Status + # } + # + # Example: + # { + # 'ok': False, # Status + # 'error': "RuntimeError", + # 'stacktrace': "...", + # } + # + # For some systems (eg: cache, database) _check[_type] is a + # list of dictionaries of dictionaries of statuses + # + # Example: + # [ + # { + # 'default': { # Cache/database name + # 'ok': True, # Status + # } + # }, + # { + # 'non-default': { # Cache/database name + # 'ok': False, # Status + # 'error': "RuntimeError", + # 'stacktrace': "...", + # } + # }, + # ] + # + statuses = [] + + if type(_check[_type]) == dict: + result = _check[_type] + statuses = [{ + 'name': '', + 'ok': result['ok'], + 'error': '' if result['ok'] else result['error'], + 'stacktrace': '' if result['ok'] else result['stacktrace'], + }] + + type_overall_status = _check[_type]['ok'] + + elif type(_check[_type]) == list: + for result in _check[_type]: + for name in result: + statuses.append({ + 'name': name, + 'ok': result[name]['ok'], + 'error': '' if result[name]['ok'] else result[name]['error'], + 'stacktrace': '' if result[name]['ok'] else result[name]['stacktrace'], + }) + + type_overall_status = all([s['ok'] for s in statuses]) + + check_types.append({ + 'type': _type, + 'ok': type_overall_status, + 'statuses': statuses}) + + overall_status = all([type_status['ok'] for type_status in check_types]) + + return render(request, 'watchman/dashboard.html', { + 'checks': check_types, + 'overall_status': overall_status + })