def process(cls, widget, job, data): class ColInfo: def __init__(self, col, dataindex, axis): self.col = col self.dataindex = dataindex self.axis = axis all_cols = job.get_columns() # The category "key" column -- this is the column shown along the # bottom of the bar widget keycols = [c for c in all_cols if c.name in widget.options.keycols] # columns of '*' is a special case, just use all # defined columns other than time if widget.options.columns == '*' or widget.options.dynamic: cols = [c for c in all_cols if not c.iskey] else: # The value columns - one set of bars for each cols = [c for c in all_cols if c.name in widget.options.columns] axes = Axes(widget.options.axes) # Array of data series definitions yui3 style series = [] # Array of axis definitions yui3 style catname = '-'.join([k.name for k in keycols]) w_axes = { catname: { "keys": [catname], "position": "bottom", "styles": { "label": { "rotation": -60 } } } } # Map of column info by column name colmap = {} # Add keycols to the colmap for i, c in enumerate(all_cols): if c not in keycols: continue ci = ColInfo(c, i, axes.getaxis(c.name)) colmap[c.name] = ci for i, c in enumerate(all_cols): # Rest of this is for data cols only if c not in cols: continue ci = ColInfo(c, i, axes.getaxis(c.name)) colmap[c.name] = ci series.append({ "xKey": '-'.join([k.name for k in keycols]), "xDisplayName": ','.join([k.label for k in keycols]), "yKey": c.name, "yDisplayName": c.label, "styles": { "line": { "weight": 1 }, "marker": { "height": 6, "width": 20 } } }) # The rest compute axis min/max for datavalues, so skip keys if c.iskey: continue axis_name = 'axis' + str(ci.axis) if axis_name not in w_axes: w_axes[axis_name] = { "type": "numeric", "position": ("left" if (ci.axis == 0) else "right"), "keys": [] } w_axes[axis_name]['keys'].append(c.name) # Array of actual data yui3 style. Each row is a dict of key->value rows = [] # min/max values by axis 0/1 minval = {} maxval = {} stacked = False # XXXCJ for rawrow in data: row = {} rowmin = {} rowmax = {} # collect key values keyvals = [] for c in colmap.values(): if not c.col.iskey: continue keyvals.append(rawrow[c.dataindex]) row[catname] = ','.join(str(k) for k in keyvals) # collect the data values for c in colmap.values(): if c.col.iskey: continue # Set the value val = rawrow[c.dataindex] row[c.col.name] = val a = c.axis if c.axis not in rowmin: rowmin[a] = val rowmax[a] = val else: rowmin[a] = ((rowmin[a] + val) if stacked else min(rowmin[a], val)) rowmax[a] = ((rowmax[a] + val) if stacked else max(rowmax[a], val)) for a in rowmin.keys(): minval[a] = rowmin[a] if (a not in minval) else min( minval[a], rowmin[a]) maxval[a] = rowmax[a] if (a not in maxval) else max( maxval[a], rowmax[a]) rows.append(row) # Build up axes for c in colmap.values(): if c.col.iskey: continue axis_name = 'axis' + str(c.axis) if minval and maxval: n = NiceScale(minval[c.axis], maxval[c.axis]) w_axes[axis_name]['minimum'] = "%.10f" % n.nicemin w_axes[axis_name]['maximum'] = "%.10f" % n.nicemax w_axes[axis_name]['tickExponent'] = math.log10(n.tickspacing) w_axes[axis_name]['styles'] = { 'majorUnit': { 'count': n.numticks } } else: # empty data which would result in keyError above w_axes[axis_name]['minimum'] = "0" w_axes[axis_name]['maximum'] = "1" w_axes[axis_name]['tickExponent'] = 1 w_axes[axis_name]['styles'] = {'majorUnit': {'count': 1}} data = { "chartTitle": widget.title.format(**job.actual_criteria), "type": widget.options.charttype, "categoryKey": catname, "dataProvider": rows, "seriesCollection": series, "axes": w_axes, "legend": { "position": "bottom", "fontSize": "8pt", "styles": { "gap": 0 } } } return data
def process(cls, widget, job, data): class ColInfo: def __init__(self, col, dataindex, axis): self.col = col self.dataindex = dataindex self.axis = axis all_cols = job.get_columns() # The category "key" column -- this is the column shown along the # bottom of the bar widget keycols = [c for c in all_cols if c.name in widget.options.keycols] # columns of '*' is a special case, just use all # defined columns other than time if widget.options.columns == '*' or widget.options.dynamic: cols = [c for c in all_cols if not c.iskey] else: # The value columns - one set of bars for each cols = [c for c in all_cols if c.name in widget.options.columns] axes = Axes(widget.options.axes) # Array of data series definitions yui3 style series = [] # Array of axis definitions yui3 style catname = '-'.join([k.name for k in keycols]) w_axes = {catname: {"keys": [catname], "position": "bottom", "styles": {"label": {"rotation": -60}}}} # Map of column info by column name colmap = {} # Add keycols to the colmap for i, c in enumerate(all_cols): if c not in keycols: continue ci = ColInfo(c, i, axes.getaxis(c.name)) colmap[c.name] = ci for i, c in enumerate(all_cols): # Rest of this is for data cols only if c not in cols: continue ci = ColInfo(c, i, axes.getaxis(c.name)) colmap[c.name] = ci series.append({"xKey": '-'.join([k.name for k in keycols]), "xDisplayName": ','.join([k.label for k in keycols]), "yKey": c.name, "yDisplayName": c.label, "styles": {"line": {"weight": 1}, "marker": {"height": 6, "width": 20}}}) # The rest compute axis min/max for datavalues, so skip keys if c.iskey: continue axis_name = 'axis' + str(ci.axis) if axis_name not in w_axes: w_axes[axis_name] = {"type": "numeric", "position": ("left" if (ci.axis == 0) else "right"), "keys": []} w_axes[axis_name]['keys'].append(c.name) # Array of actual data yui3 style. Each row is a dict of key->value rows = [] # min/max values by axis 0/1 minval = {} maxval = {} stacked = False # XXXCJ for rawrow in data: row = {} rowmin = {} rowmax = {} # collect key values keyvals = [] for c in colmap.values(): if not c.col.iskey: continue keyvals.append(rawrow[c.dataindex]) row[catname] = ','.join(str(k) for k in keyvals) # collect the data values for c in colmap.values(): if c.col.iskey: continue # Set the value val = rawrow[c.dataindex] row[c.col.name] = val a = c.axis if c.axis not in rowmin: rowmin[a] = val rowmax[a] = val else: rowmin[a] = ((rowmin[a] + val) if stacked else min(rowmin[a], val)) rowmax[a] = ((rowmax[a] + val) if stacked else max(rowmax[a], val)) for a in rowmin.keys(): minval[a] = rowmin[a] if (a not in minval) else min(minval[a], rowmin[a]) maxval[a] = rowmax[a] if (a not in maxval) else max(maxval[a], rowmax[a]) rows.append(row) # Build up axes for c in colmap.values(): if c.col.iskey: continue axis_name = 'axis' + str(c.axis) if minval and maxval: n = NiceScale(minval[c.axis], maxval[c.axis]) w_axes[axis_name]['minimum'] = "%.10f" % n.nicemin w_axes[axis_name]['maximum'] = "%.10f" % n.nicemax w_axes[axis_name]['tickExponent'] = math.log10(n.tickspacing) w_axes[axis_name]['styles'] = { 'majorUnit': {'count': n.numticks} } else: # empty data which would result in keyError above w_axes[axis_name]['minimum'] = "0" w_axes[axis_name]['maximum'] = "1" w_axes[axis_name]['tickExponent'] = 1 w_axes[axis_name]['styles'] = {'majorUnit': {'count': 1}} data = { "chartTitle": widget.title.format(**job.actual_criteria), "type": widget.options.charttype, "categoryKey": catname, "dataProvider": rows, "seriesCollection": series, "axes": w_axes, "legend": {"position": "bottom", "fontSize": "8pt", "styles": {"gap": 0}} } return data
def process(cls, widget, job, data): class ColInfo: def __init__(self, col, dataindex, axis, istime=False, isdate=False): self.col = col self.key = cleankey(col.name) self.dataindex = dataindex self.axis = axis self.istime = istime self.isdate = isdate t_cols = job.get_columns() colinfo = {} # map by widget key # columns of None is a special case, just use all # defined columns other than time if widget.options.columns is None: valuecolnames = [ col.name for col in t_cols if not col.istime() and not col.isdate() ] else: valuecolnames = widget.options.columns # Column keys are the 'cleaned' column names w_keys = [cleankey(n) for n in valuecolnames] # Retrieve the desired value columns # ...and the indices for the value values # (as the 'data' has *all* columns) time_colinfo = None for i, c in enumerate(t_cols): if c.istime(): ci = ColInfo(c, i, -1, istime=True) time_colinfo = ci elif c.isdate(): ci = ColInfo(c, i, -1, isdate=True) time_colinfo = ci elif c.name in valuecolnames: if c.isnumeric(): ci = ColInfo(c, i, -1, istime=False, isdate=False) else: raise KeyError( "Cannot graph non-numeric data in timeseries widget: " "column {0}".format(c.name)) colinfo[ci.key] = ci if widget.options.altaxis: altaxis = widget.options.altaxis axes_def = { '0': { 'position': 'left', 'columns': [col for col in valuecolnames if col not in altaxis] }, '1': { 'position': 'right', 'columns': [col for col in valuecolnames if col in altaxis] } } else: axes_def = {'0': {'position': 'left', 'columns': valuecolnames}} w_series = [] axes = Axes(axes_def) # Setup the time axis w_axes = { "time": { "keys": ["time"], "position": "bottom", "type": "time", "styles": { "label": { "fontSize": "8pt", "rotation": "-45" } } } } # Create a better time format depending on t0/t1 t_dataindex = time_colinfo.dataindex t0 = data[0][t_dataindex] t1 = data[-1][t_dataindex] if not hasattr(t0, 'utcfromtimestamp'): t0 = timeutils.sec_string_to_datetime(t0) t1 = timeutils.sec_string_to_datetime(t1) total_seconds = timeutils.timedelta_total_seconds(t1 - t0) if total_seconds < 2: w_axes['time']['formatter'] = 'formatTimeMs' elif total_seconds < 120: w_axes['time']['labelFormat'] = '%k:%M:%S' elif total_seconds < (24 * 60 * 60): w_axes['time']['labelFormat'] = '%k:%M' elif time_colinfo.isdate: w_axes['time']['formatter'] = 'formatDate' else: w_axes['time']['labelFormat'] = '%D %k:%M' # Setup the other axes, checking the axis for each column for w_key in w_keys: # Need to interate the valuecolnames array to preserve order ci = colinfo[w_key] w_series.append({ "xKey": "time", "xDisplayName": "Time", "yKey": ci.key, "yDisplayName": ci.col.label, "styles": { "line": { "weight": 1 }, "marker": { "height": 3, "width": 3 } } }) ci.axis = axes.getaxis(ci.col.name) axis_name = 'axis' + str(ci.axis) if axis_name not in w_axes: w_axes[axis_name] = { "type": "numeric", "position": axes.position(ci.axis), "keys": [] } w_axes[axis_name]['keys'].append(ci.key) # Output row data rows = [] # min/max values by axis 0/1 minval = {} maxval = {} stacked = widget.options.stacked # Iterate through all rows if input data for rawrow in data: t = rawrow[t_dataindex] try: t = timeutils.datetime_to_microseconds(t) / 1000 except AttributeError: t = t * 1000 row = {'time': t} rowmin = {} rowmax = {} for ci in colinfo.values(): if ci.istime or ci.isdate: continue a = ci.axis val = rawrow[ci.dataindex] row[ci.key] = val if val != '' else None # If stacked and there is only one value, use that # value as the rowmin. If stacked and there is more than # one value for the axis, use a floor of 0 to give proper # context. if a not in rowmin: rowmin[a] = val if val != '' else 0 rowmax[a] = val if val != '' else 0 else: rowmin[a] = (0 if stacked else min(rowmin[a], val)) rowmax[a] = ((rowmax[a] + val) if stacked else max(rowmax[a], val)) for a in rowmin.keys(): minval[a] = rowmin[a] if (a not in minval) else min( minval[a], rowmin[a]) maxval[a] = rowmax[a] if (a not in maxval) else max( maxval[a], rowmax[a]) rows.append(row) # Setup the scale values for the axes for ci in colinfo.values(): if ci.istime or ci.isdate: continue axis_name = 'axis' + str(ci.axis) if minval and maxval: n = NiceScale(minval[ci.axis], maxval[ci.axis]) w_axes[axis_name]['minimum'] = "%.10f" % n.nicemin w_axes[axis_name]['maximum'] = "%.10f" % n.nicemax w_axes[axis_name]['tickExponent'] = math.log10(n.tickspacing) w_axes[axis_name]['styles'] = { 'majorUnit': { 'count': n.numticks } } else: # empty data which would result in keyError above w_axes[axis_name]['minimum'] = "0" w_axes[axis_name]['maximum'] = "1" w_axes[axis_name]['tickExponent'] = 1 w_axes[axis_name]['styles'] = {'majorUnit': {'count': 1}} if ci.col.units == ci.col.UNITS_PCT: w_axes[axis_name]['formatter'] = 'formatPct' else: w_axes[axis_name]['formatter'] = 'formatMetric' if stacked: charttype = "area" elif widget.options.bar: charttype = "column" else: charttype = "combo" data = { "chartTitle": widget.title.format(**job.actual_criteria), "type": charttype, "stacked": stacked, "dataProvider": rows, "seriesCollection": w_series, "axes": w_axes, "legend": { "position": "bottom", "fontSize": "8pt", "styles": { "gap": 0 } }, "interactionType": "planar" } # logger.debug("data:\n\n%s\n" % data) return data
def process(cls, widget, job, data): class ColInfo: def __init__(self, col, dataindex, axis, istime=False, isdate=False): self.col = col self.key = cleankey(col.name) self.dataindex = dataindex self.axis = axis self.istime = istime self.isdate = isdate t_cols = job.get_columns() colinfo = {} # map by widget key # columns of None is a special case, just use all # defined columns other than time if widget.options.columns is None: valuecolnames = [col.name for col in t_cols if not col.istime() and not col.isdate()] else: valuecolnames = widget.options.columns # Column keys are the 'cleaned' column names w_keys = [cleankey(n) for n in valuecolnames] # Retrieve the desired value columns # ...and the indices for the value values # (as the 'data' has *all* columns) time_colinfo = None for i, c in enumerate(t_cols): if c.istime(): ci = ColInfo(c, i, -1, istime=True) time_colinfo = ci elif c.isdate(): ci = ColInfo(c, i, -1, isdate=True) time_colinfo = ci elif c.name in valuecolnames: if c.isnumeric(): ci = ColInfo(c, i, -1, istime=False, isdate=False) else: raise KeyError( "Cannot graph non-numeric data in timeseries widget: " "column {0}".format(c.name)) colinfo[ci.key] = ci if widget.options.altaxis: altaxis = widget.options.altaxis axes_def = {'0': {'position': 'left', 'columns': [col for col in valuecolnames if col not in altaxis]}, '1': {'position': 'right', 'columns': [col for col in valuecolnames if col in altaxis]} } else: axes_def = {'0': {'position': 'left', 'columns': valuecolnames}} w_series = [] axes = Axes(axes_def) # Setup the time axis w_axes = {"time": {"keys": ["time"], "position": "bottom", "type": "time", "styles": {"label": {"fontSize": "8pt", "rotation": "-45"}}}} # Create a better time format depending on t0/t1 t_dataindex = time_colinfo.dataindex t0 = data[0][t_dataindex] t1 = data[-1][t_dataindex] if not hasattr(t0, 'utcfromtimestamp'): t0 = timeutils.sec_string_to_datetime(t0) t1 = timeutils.sec_string_to_datetime(t1) total_seconds = timeutils.timedelta_total_seconds(t1 - t0) if total_seconds < 2: w_axes['time']['formatter'] = 'formatTimeMs' elif total_seconds < 120: w_axes['time']['labelFormat'] = '%k:%M:%S' elif total_seconds < (24 * 60 * 60): w_axes['time']['labelFormat'] = '%k:%M' elif time_colinfo.isdate: w_axes['time']['formatter'] = 'formatDate' else: w_axes['time']['labelFormat'] = '%D %k:%M' # Setup the other axes, checking the axis for each column for w_key in w_keys: # Need to interate the valuecolnames array to preserve order ci = colinfo[w_key] w_series.append({"xKey": "time", "xDisplayName": "Time", "yKey": ci.key, "yDisplayName": ci.col.label, "styles": {"line": {"weight": 1}, "marker": {"height": 3, "width": 3}}}) ci.axis = axes.getaxis(ci.col.name) axis_name = 'axis' + str(ci.axis) if axis_name not in w_axes: w_axes[axis_name] = {"type": "numeric", "position": axes.position(ci.axis), "keys": [] } w_axes[axis_name]['keys'].append(ci.key) # Output row data rows = [] # min/max values by axis 0/1 minval = {} maxval = {} stacked = widget.options.stacked # Iterate through all rows if input data for rawrow in data: t = rawrow[t_dataindex] try: t = timeutils.datetime_to_microseconds(t) / 1000 except AttributeError: t = t * 1000 row = {'time': t} rowmin = {} rowmax = {} for ci in colinfo.values(): if ci.istime or ci.isdate: continue a = ci.axis val = rawrow[ci.dataindex] row[ci.key] = val if val != '' else None # If stacked and there is only one value, use that # value as the rowmin. If stacked and there is more than # one value for the axis, use a floor of 0 to give proper # context. if a not in rowmin: rowmin[a] = val if val != '' else 0 rowmax[a] = val if val != '' else 0 else: rowmin[a] = (0 if stacked else min(rowmin[a], val)) rowmax[a] = ((rowmax[a] + val) if stacked else max(rowmax[a], val)) for a in rowmin.keys(): minval[a] = rowmin[a] if (a not in minval) else min(minval[a], rowmin[a]) maxval[a] = rowmax[a] if (a not in maxval) else max(maxval[a], rowmax[a]) rows.append(row) # Setup the scale values for the axes for ci in colinfo.values(): if ci.istime or ci.isdate: continue axis_name = 'axis' + str(ci.axis) if minval and maxval: n = NiceScale(minval[ci.axis], maxval[ci.axis]) w_axes[axis_name]['minimum'] = "%.10f" % n.nicemin w_axes[axis_name]['maximum'] = "%.10f" % n.nicemax w_axes[axis_name]['tickExponent'] = math.log10(n.tickspacing) w_axes[axis_name]['styles'] = { 'majorUnit': {'count': n.numticks} } else: # empty data which would result in keyError above w_axes[axis_name]['minimum'] = "0" w_axes[axis_name]['maximum'] = "1" w_axes[axis_name]['tickExponent'] = 1 w_axes[axis_name]['styles'] = {'majorUnit': {'count': 1}} if ci.col.units == ci.col.UNITS_PCT: w_axes[axis_name]['formatter'] = 'formatPct' else: w_axes[axis_name]['formatter'] = 'formatMetric' if stacked: charttype = "area" elif widget.options.bar: charttype = "column" else: charttype = "combo" data = { "chartTitle": widget.title.format(**job.actual_criteria), "type": charttype, "stacked": stacked, "dataProvider": rows, "seriesCollection": w_series, "axes": w_axes, "legend": {"position": "bottom", "fontSize": "8pt", "styles": {"gap": 0}}, "interactionType": "planar" } # logger.debug("data:\n\n%s\n" % data) return data