Humanized time boundaries and granularity

This commit is contained in:
Maxime 2015-07-17 06:46:00 +00:00
parent 16f7bf9054
commit 8b175638cd
7 changed files with 102 additions and 32 deletions

BIN
app.db

Binary file not shown.

View File

@ -21,7 +21,9 @@ class Datasource(Model, AuditMixin):
@property
def metrics_combo(self):
return [(m.metric_name, m.verbose_name) for m in self.metrics]
return sorted(
[(m.metric_name, m.verbose_name) for m in self.metrics],
key=lambda x: x[1])
def __repr__(self):
return self.datasource_name

View File

@ -2,13 +2,9 @@
{% block head_css %}
{{super()}}
<style>
form .row {
margin-left: 0;
margin-right: 0;
}
form .col {
padding-right:0px;
padding-left:0px;
.select2-container-multi .select2-choices {
height: 70px;
overflow: auto;
}
</style>
{% endblock %}
@ -24,8 +20,13 @@ form .col {
<form method="GET">
<div>{{ form.viz_type.label }}: {{ form.viz_type(class_="form-control select2") }}</div>
<div>{{ form.metric.label }}: {{ form.metric(class_="form-control select2") }}</div>
<div>{{ form.granularity.label }}: {{ form.granularity(class_="form-control select2") }}</div>
<div>{{ form.since.label }}: {{ form.since(class_="form-control select2") }}</div>
<div>{{ form.granularity.label }}: {{ form.granularity(class_="form-control select2_free_granularity") }}</div>
<div class="row">
<div class="form-group">
<div class="col-xs-6">{{ form.since.label }}: {{ form.since(class_="form-control select2_free_since") }}</div>
<div class="col-xs-6">{{ form.until.label }}: {{ form.until(class_="form-control select2_free_until") }}</div>
</div>
</div>
<div>{{ form.groupby.label }}: {{ form.groupby(class_="form-control select2") }}</div>
<div>{{ form.limit.label }}: {{ form.limit(class_="form-control select2") }}</div>
<hr>
@ -33,9 +34,7 @@ form .col {
<div id="filters">
{% for i in range(10) %}
<div id="flt{{ i }}" class="{{ "hidden" if i != 1 }}">
<div class="row">
<span class="" style="width: 100px;">{{ form['flt_col_' ~ i](class_="form-control select2 inc") }}</span>
</div>
<div class="row">
<span class="col col-md-3">{{ form['flt_op_' ~ i](class_="form-control select2 input-sm inc") }}</span>
<span class="col col-md-7">{{ form['flt_eq_' ~ i](class_="form-control inc") }}</span>
@ -83,7 +82,51 @@ form .col {
<script>
$( document ).ready(function() {
$(".select2").select2();
$(".select2_tags").select2({tags: true});
function create_choices (term, data) {
if ($(data).filter(function() {
return this.text.localeCompare(term)===0;
}).length===0)
{return {id:term, text:term};}
}
$(".select2_free_since").select2({
createSearchChoice: create_choices,
multiple: false,
data: [
{id: '-1 hour', text: '-1 hour'},
{id: '-12 hours', text: '-12 hours'},
{id: '-1 day', text: '-1 day'},
{id: '-7 days', text: '-7 days'},
{id: '-28 days', text: '-28 days'},
{id: '-90 days', text: '-90 days'},
{id: '{{ form.data.since }}', text: '{{ form.data.since }}'},
]
});
$(".select2_free_until").select2({
createSearchChoice: create_choices,
multiple: false,
data: [
{id: '{{ form.data.until }}', text: '{{ form.data.until }}'},
{id: 'now', text: 'now'},
{id: '-1 day', text: '-1 day'},
{id: '-7 days', text: '-7 days'},
{id: '-28 days', text: '-28 days'},
{id: '-90 days', text: '-90 days'},
]
});
$(".select2_free_granularity").select2({
createSearchChoice: create_choices,
multiple: false,
data: [
{id: '{{ form.data.granularity }}', text: '{{ form.data.granularity }}'},
{id: '5 seconds', text: '5 seconds'},
{id: '30 seconds', text: '30 seconds'},
{id: '1 minute', text: '1 minute'},
{id: '5 minutes', text: '5 minutes'},
{id: '1 day', text: '1 day'},
{id: '7 days', text: '7 days'},
]
});
});
</script>
{% endblock %}

View File

@ -2,13 +2,6 @@ import config
from datetime import timedelta, datetime
import parsedatetime
since_l = {
'1hour': timedelta(hours=1),
'1day': timedelta(days=1),
'7days': timedelta(days=7),
'28days': timedelta(days=28),
'all': timedelta(days=365*100)
}
def get_pydruid_client():
from pydruid import client
@ -26,6 +19,25 @@ def parse_human_datetime(s):
True
"""
cal = parsedatetime.Calendar()
d = cal.parse(s)[0]
return dttm_from_timtuple(cal.parse(s)[0])
def dttm_from_timtuple(d):
return datetime(
d.tm_year, d.tm_mon, d.tm_mday, d.tm_hour, d.tm_min, d.tm_sec)
def parse_human_timedelta(s):
"""
Use the parsedatetime lib to return ``datetime.datetime`` from human
generated strings
>>> parse_human_datetime("now") <= datetime.now()
True
"""
cal = parsedatetime.Calendar()
dttm = dttm_from_timtuple(datetime.now().timetuple())
d = cal.parse(s, dttm)[0]
d = datetime(
d.tm_year, d.tm_mon, d.tm_mday, d.tm_hour, d.tm_min, d.tm_sec)
return d - dttm

View File

@ -9,6 +9,7 @@ import config
from wtforms import Form, SelectMultipleField, SelectField, TextField
from wtforms.fields import Field
class OmgWtForm(Form):
field_order = (
'viz_type', 'granularity', 'since', 'group_by', 'limit')
@ -44,11 +45,14 @@ def form_factory(datasource, form_args=None):
groupby = SelectMultipleField(
'Group by', choices=[
(s, s) for s in datasource.groupby_column_names])
granularity = SelectField(
'Time Granularity', choices=[(g, g) for g in grain])
since = SelectField(
'Since', choices=[(s, s) for s in utils.since_l.keys()],
default="all")
#granularity = SelectField(
# 'Time Granularity', choices=[(g, g) for g in grain])
#since = SelectField(
# 'Since', choices=[(s, s) for s in utils.since_l.keys()],
# default="all")
granularity = TextField('Time Granularity', default="one day")
since = TextField('Since', default="one day ago")
until = TextField('Until', default="now")
limit = SelectField(
'Limit', choices=[(s, s) for s in limits])
for i in range(10):

View File

@ -66,19 +66,28 @@ class BaseViz(object):
ds = self.datasource
args = self.form_data
groupby = args.getlist("groupby") or []
granularity = args.get("granularity")
granularity = args.get("granularity", "1 day")
granularity = utils.parse_human_timedelta(granularity).total_seconds() * 1000
aggregations = {
m.metric_name: m.json_obj
for m in ds.metrics if m.metric_name == self.metric
}
limit = int(
args.get("limit", config.ROW_LIMIT)) or config.ROW_LIMIT
since = args.get("since", "all")
from_dttm = (datetime.now() - utils.since_l[since]).isoformat()
since = args.get("since", "1 year ago")
from_dttm = utils.parse_human_datetime(since)
if from_dttm > datetime.now():
from_dttm = datetime.now() - (from_dttm-datetime.now())
from_dttm = from_dttm.isoformat()
until = args.get("until", "now")
to_dttm = utils.parse_human_datetime(until).isoformat()
if from_dttm >= to_dttm:
flash("The date range doesn't seem right.", "danger")
from_dttm = to_dttm # Making them identicial to not raise
d = {
'datasource': ds.datasource_name,
'granularity': granularity or 'all',
'intervals': from_dttm + '/' + datetime.now().isoformat(),
'granularity': {"type": "duration", "duration": granularity},
'intervals': from_dttm + '/' + to_dttm,
'dimensions': groupby,
'aggregations': aggregations,
'limit_spec': {

View File

@ -104,7 +104,7 @@ IMG_UPLOAD_URL = '/static/uploads/'
# Theme configuration
# these are located on static/appbuilder/css/themes
# you can create your own and easily use them placing them on the same dir structure to override
APP_THEME = "bootstrap-theme.css" # default bootstrap
#APP_THEME = "bootstrap-theme.css" # default bootstrap
#APP_THEME = "cerulean.css"
#APP_THEME = "amelia.css"
#APP_THEME = "cosmo.css"