From 8b175638cdb5d3f4105d82e7fae43cbf4d05e61c Mon Sep 17 00:00:00 2001 From: Maxime Date: Fri, 17 Jul 2015 06:46:00 +0000 Subject: [PATCH] Humanized time boundaries and granularity --- app.db | Bin 59392 -> 59392 bytes app/models.py | 4 +- app/templates/panoramix/datasource.html | 67 +++++++++++++++++++----- app/utils.py | 28 +++++++--- app/views.py | 14 +++-- app/viz.py | 19 +++++-- config.py | 2 +- 7 files changed, 102 insertions(+), 32 deletions(-) diff --git a/app.db b/app.db index 16e9d6aa8fde4fa9ef0664766bf9449ae7d16be6..da7d7cfbfaa8fb8e36a7f07d80fb9bee03c99942 100644 GIT binary patch delta 288 zcmZp;z}#?wd4e=!_e2?I#_o*?%a}RlGcYAFD>BaC*f^1quhD^7fI(1Hx>1nR(2;TS zI@Ww4a|HuKD+3EFLjyfaV`CFz6Q;>}Y->TnW>$t4Rz~J}MwSM~2Bw^w-?B+=Wl?8f zRNvSr%gEar#>wO8$RNwX!P})ddH;Q}$x(ax1;N^YI!!J0%nU4zO)Vx@?e$kP)J{pP zR4dNROHQ@bHPo(5O)OF?%`3^wvCYdbS4%I-FD*!_w4FTRg4pJ-dj-{4rZX^3-`Lp8 z$lH>}53)oVXi10j5$F*f@!iuhE`afI(1Hx>1nR(2;ZU zTGo6)GX*1KD?@WDV*@<{OLHR&)5&^lYlXm~CMH&*2U&XYKVDOG&I$ zE6&VIPPI+U$x%x$$}cTQsP&gz|%|PzGAj wp*VTTQ&o^#jjT+8Zne-eFtao^GMs$tX{casW?pGYD%1u;pk -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; } {% endblock %} @@ -24,8 +20,13 @@

{{ form.viz_type.label }}: {{ form.viz_type(class_="form-control select2") }}
{{ form.metric.label }}: {{ form.metric(class_="form-control select2") }}
-
{{ form.granularity.label }}: {{ form.granularity(class_="form-control select2") }}
-
{{ form.since.label }}: {{ form.since(class_="form-control select2") }}
+
{{ form.granularity.label }}: {{ form.granularity(class_="form-control select2_free_granularity") }}
+
+
+
{{ form.since.label }}: {{ form.since(class_="form-control select2_free_since") }}
+
{{ form.until.label }}: {{ form.until(class_="form-control select2_free_until") }}
+
+
{{ form.groupby.label }}: {{ form.groupby(class_="form-control select2") }}
{{ form.limit.label }}: {{ form.limit(class_="form-control select2") }}

@@ -33,9 +34,7 @@

Filters

{% for i in range(10) %}
-
{{ form['flt_col_' ~ i](class_="form-control select2 inc") }} -
{{ form['flt_op_' ~ i](class_="form-control select2 input-sm inc") }} {{ form['flt_eq_' ~ i](class_="form-control inc") }} @@ -83,7 +82,51 @@

Latest Segment Metadata

{% endblock %} diff --git a/app/utils.py b/app/utils.py index 48e91e7709bbc..59ced72a13839 100644 --- a/app/utils.py +++ b/app/utils.py @@ -2,13 +2,6 @@ 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 diff --git a/app/views.py b/app/views.py index a24d3f828775a..02ac4b59adb0c 100644 --- a/app/views.py +++ b/app/views.py @@ -9,6 +9,7 @@ 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 @@ class QueryForm(OmgWtForm): 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): diff --git a/app/viz.py b/app/viz.py index 543c831b66da5..b8d4ec3f1a449 100644 --- a/app/viz.py +++ b/app/viz.py @@ -66,19 +66,28 @@ def query_obj(self): 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': { diff --git a/config.py b/config.py index 65e596a920b5e..2470c5ab7c572 100644 --- a/config.py +++ b/config.py @@ -104,7 +104,7 @@ # 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"