forked from allegro/ralph
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
36 changed files
with
683 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# Dashboards | ||
|
||
The dashboard provide basic mechanism for displaying data via bar or pie charts. | ||
|
||
|
||
## Getting started | ||
All example data in this tutorial was generated by Ralph's command - ``ralph make_demo_data``. | ||
|
||
### Goal | ||
Display graphs with quantity assets in each data centers. | ||
|
||
### First dashboard | ||
First of all we must create new dasboard object in Ralph by clicking in menu``Dashboards > Dashboards`` next click ``Add new dashboard`` to add new one. | ||
|
||
![add-dashboard](/img/dashboard-create-dasboard.png "Add dashboard") | ||
|
||
|
||
Next steps is create graph and configure it. | ||
![add-first-graph](/img/dashboard-create-graph-dc.png "Add first-graph") | ||
|
||
The important field of form above is ``Params`` - this field accepted configuration of graph in JSON format. Keys ``labels``, ``series``, ``filters`` are required. | ||
Below short description of these fields: | ||
|
||
- ``labels`` - which field in model are string representation, | ||
- ``series`` - aggregate by this field, | ||
- ``filters`` - Django ORM-like lookup (visit [Django documentation](https://docs.djangoproject.com/en/1.8/ref/models/querysets/#id4)). | ||
|
||
OK, after save go our new dashboard object. Now we can see item (``DC Capacity``) in ``Graphs`` fields - select them. After save go to ``Dashboards > Dashboards`` in list view click ``Link``. | ||
![link-to-dashboard](/img/dashboard-link.png "Link") | ||
|
||
Final result: | ||
![link-dashboard-final](/img/dashboard-final-dc.png "Final dashboard") | ||
|
||
|
||
## Special filters | ||
Special filters are some helpers to | ||
|
||
### from_now | ||
``from_now`` works only with date and date-time fields in ``filters`` section, e.g.: | ||
```json | ||
{ | ||
"labels": "name", | ||
"series": "serverroom__rack__datacenterasset", | ||
"filters": { | ||
"created__gt|from_now": "-1y", | ||
}, | ||
} | ||
``` | ||
The filter above limit query to objects which created from one year ago to now. Possible variants of period: | ||
|
||
- ``y`` - years, | ||
- ``m`` - months, | ||
- ``d`` - days, | ||
|
||
|
||
|
||
# TODO | ||
|
||
- free assets aggregate by model name with invoice date from 2 years ago | ||
- from GET params to dashboard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import json | ||
from itertools import groupby | ||
|
||
from django import forms | ||
from django.contrib.contenttypes.models import ContentType | ||
from django.core.urlresolvers import reverse | ||
from django.utils.translation import ugettext as _ | ||
|
||
from ralph.admin import RalphAdmin, register | ||
from ralph.admin.mixins import RalphAdminForm | ||
from ralph.dashboards.models import Dashboard, Graph | ||
|
||
|
||
class GraphForm(RalphAdminForm): | ||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
choices = [ | ||
ct for ct in ContentType.objects.all() | ||
if getattr(ct.model_class(), '_allow_in_dashboard', False) | ||
] | ||
|
||
def keyfunc(x): | ||
return x.app_label | ||
|
||
data = sorted(choices, key=keyfunc) | ||
self.fields['model'] = forms.ChoiceField(choices=( | ||
(k.capitalize(), list(map(lambda x: (x.id, x), g))) | ||
for k, g in groupby(data, keyfunc) | ||
)) | ||
|
||
def clean_model(self): | ||
ct_id = self.cleaned_data.get('model') | ||
return ContentType.objects.get(pk=ct_id) | ||
|
||
def clean_params(self): | ||
params = self.cleaned_data.get('params', '{}') | ||
try: | ||
params_dict = json.loads(params) | ||
except json.decoder.JSONDecodeError as e: | ||
raise forms.ValidationError(e.msg) | ||
if not params_dict.get('labels', None): | ||
raise forms.ValidationError('Please specify `labels` key') | ||
if not params_dict.get('series', None): | ||
raise forms.ValidationError('Please specify `series` key') | ||
return params | ||
|
||
class Meta: | ||
model = Graph | ||
fields = [ | ||
'name', 'description', 'model', 'aggregate_type', 'chart_type', | ||
'params', 'active' | ||
] | ||
|
||
|
||
@register(Graph) | ||
class GraphAdmin(RalphAdmin): | ||
form = GraphForm | ||
list_display = ['name', 'description', 'active'] | ||
|
||
|
||
@register(Dashboard) | ||
class DashboardAdmin(RalphAdmin): | ||
list_display = ['name', 'description', 'active', 'get_link'] | ||
|
||
def get_link(self, obj): | ||
return _('<a href="{}" target="_blank">Dashboard</a>'.format(reverse( | ||
'dashboard_view', args=(obj.pk,) | ||
))) | ||
get_link.short_description = _('Link') | ||
get_link.allow_tags = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import datetime | ||
import re | ||
|
||
from dateutil.relativedelta import relativedelta | ||
|
||
FILTER_FROM_NOW = re.compile(r'([+-]?\d+)(\w)') | ||
|
||
|
||
class FilterParser(object): | ||
|
||
def __init__(self, queryset, filters): | ||
self.queryset = queryset | ||
self.filters = filters | ||
|
||
def get_queryset(self): | ||
parsed_filters = {} | ||
for key, value in self.filters.items(): | ||
params = key.split('|') | ||
if len(params) == 1: | ||
parsed_filters[key] = value | ||
else: | ||
filter_func = getattr(self, 'filter_' + params[1], None) | ||
if not filter_func: | ||
continue | ||
parsed_filters[key] = filter_func(value) | ||
return self.queryset.filter(**parsed_filters) | ||
|
||
def filter_from_now(self, value): | ||
period_mapper = { | ||
'd': 'days', | ||
'm': 'months', | ||
'y': 'years', | ||
} | ||
val, period = FILTER_FROM_NOW.match(value).groups() | ||
result = datetime.date.today() + relativedelta(**{ | ||
period_mapper.get(period): int(val) | ||
}) | ||
return result.strftime('%Y-%m-%d') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
import django_extensions.db.fields.json | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('contenttypes', '0002_remove_content_type_name'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Dashboard', | ||
fields=[ | ||
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), | ||
('name', models.CharField(max_length=255, verbose_name='name', unique=True)), | ||
('created', models.DateTimeField(auto_now_add=True, verbose_name='date created')), | ||
('modified', models.DateTimeField(verbose_name='last modified', auto_now=True)), | ||
('active', models.BooleanField(default=True)), | ||
('description', models.CharField(max_length=250, verbose_name='description', blank=True)), | ||
('interval', models.PositiveSmallIntegerField(default=60)), | ||
], | ||
options={ | ||
'abstract': False, | ||
'ordering': ['name'], | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name='Graph', | ||
fields=[ | ||
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), | ||
('name', models.CharField(max_length=255, verbose_name='name', unique=True)), | ||
('created', models.DateTimeField(auto_now_add=True, verbose_name='date created')), | ||
('modified', models.DateTimeField(verbose_name='last modified', auto_now=True)), | ||
('description', models.CharField(max_length=250, verbose_name='description', blank=True)), | ||
('aggregate_type', models.PositiveIntegerField(choices=[(1, 'Count'), (2, 'Max'), (3, 'Sum')])), | ||
('chart_type', models.PositiveIntegerField(choices=[(1, 'Verical Bar'), (2, 'Horizontal Bar'), (3, 'Pie Chart')])), | ||
('params', django_extensions.db.fields.json.JSONField(blank=True)), | ||
('active', models.BooleanField(default=True)), | ||
('model', models.ForeignKey(to='contenttypes.ContentType')), | ||
], | ||
options={ | ||
'abstract': False, | ||
'ordering': ['name'], | ||
}, | ||
), | ||
migrations.AddField( | ||
model_name='dashboard', | ||
name='graphs', | ||
field=models.ManyToManyField(to='dashboards.Graph', blank=True), | ||
), | ||
] |
Empty file.
Oops, something went wrong.