def _chart_cell(args, cell): source = args['data'] ipy = IPython.get_ipython() chart_options = _utils.parse_config(cell, ipy.user_ns) if chart_options is None: chart_options = {} fields = args['fields'] if args['fields'] else '*' _HTML_TEMPLATE = u""" <div class="bqgc" id="%s"> </div> <script> require(['extensions/charting', 'element!%s', 'style!/static/extensions/charting.css'], function(charts, dom) { charts.render(dom, {chartStyle:'%s', dataName:'%s', fields:'%s'}, %s, %s); } ); </script> """ div_id = _html.Html.next_id() chart_type = args['chart'] count = 25 if chart_type == 'paged_table' else -1 data, _ = _utils.get_data(source, fields, 0, count) return IPython.core.display.HTML( _HTML_TEMPLATE % (div_id, div_id, chart_type, _utils.get_data_source_index(source), fields, json.dumps(chart_options, cls=gcp._util.JSONEncoder), json.dumps(data, cls=gcp._util.JSONEncoder)))
def _chart_cell(args, cell): source = args['data'] ipy = IPython.get_ipython() chart_options = _utils.parse_config(cell, ipy.user_ns) if chart_options is None: chart_options = {} fields = args['fields'] if args['fields'] else '*' _HTML_TEMPLATE = """ <div class="bqgc" id="%s"> </div> <script> require(['extensions/charting', 'element!%s', 'style!/static/extensions/charting.css'], function(charts, dom) { charts.render(dom, {chartStyle:'%s', dataName:'%s', fields:'%s'}, %s, %s); } ); </script> """ div_id = _html.Html.next_id() chart_type = args['chart'] count = 25 if chart_type == 'paged_table' else -1 data, _ = _utils.get_data(source, fields, 0, count) return IPython.core.display.HTML( _HTML_TEMPLATE % (div_id, div_id, chart_type, _utils.get_data_source_index(source), fields, json.dumps(chart_options, cls=gcp._util.JSONEncoder), json.dumps(data, ensure_ascii=False, cls=gcp._util.JSONEncoder)))
def _table_viewer(table, rows_per_page=25, fields=None): """ Return a table viewer. This includes a static rendering of the first page of the table, that gets replaced by the charting code in environments where Javascript is executable and BQ is available. Args: table: the table to view. rows_per_page: how many rows to display at one time. fields: an array of field names to display; default is None which uses the full schema. Returns: A string containing the HTML for the table viewer. """ if not table.exists(): raise Exception('Table %s does not exist' % str(table)) _HTML_TEMPLATE = u""" <div class="bqtv" id="{div_id}">{static_table}</div> <br />{meta_data}<br /> <script> require(['extensions/charting', 'element!{div_id}', 'style!/static/extensions/charting.css'], function(charts, dom) {{ charts.render(dom, {{ chartStyle:"{chart_style}", dataName:"{data_name}", fields:"{fields}", totalRows:{total_rows}, rowsPerPage:{rows_per_page}, }}, {{}}, {data}); }} ); </script> """ if fields is None: fields = _utils.get_field_list(fields, table.schema) div_id = _html.Html.next_id() meta_count = ('rows: %d' % table.length) if table.length >= 0 else '' meta_name = str(table) if table.job is None else ('job: %s' % table.job.id) if table.job: if table.job.cache_hit: meta_cost = 'cached' else: bytes = gcp.bigquery._query_stats.QueryStats._size_formatter(table.job.bytes_processed) meta_cost = '%s processed' % bytes meta_time = 'time: %.1fs' % table.job.total_time else: meta_cost = '' meta_time = '' data, total_count = _utils.get_data(table, fields, 0, rows_per_page) if total_count < 0: # The table doesn't have a length metadata property but may still be small if we fetched less # rows than we asked for. fetched_count = len(data['rows']) if fetched_count < rows_per_page: total_count = fetched_count chart = 'table' if 0 <= total_count <= rows_per_page else 'paged_table' meta_entries = [meta_count, meta_time, meta_cost, meta_name] meta_data = '(%s)' % (', '.join([entry for entry in meta_entries if len(entry)])) return _HTML_TEMPLATE.format(div_id=div_id, static_table=_html.HtmlBuilder.render_chart_data(data), meta_data=meta_data, chart_style=chart, data_name=_utils.get_data_source_index(str(table)), fields=','.join(fields), total_rows=total_count, rows_per_page=rows_per_page, data=json.dumps(data, cls=gcp._util.JSONEncoder))
def _table_viewer(table, rows_per_page=25, fields=None): """ Return a table viewer. Args: table: the table to view. rows_per_page: how many rows to display at one time. fields: an array of field names to display; default is None which uses the full schema. Returns: A string containing the HTML for the table viewer. """ if not table.exists(): raise Exception('%s does not exist' % str(table)) _HTML_TEMPLATE = """ <div class="bqtv" id="%s"></div> <br />%s<br /> <script> require(['extensions/charting', 'element!%s', 'style!/static/extensions/charting.css'], function(charts, dom) { charts.render(dom, { chartStyle:"%s", dataName:"%s", fields:"%s", totalRows:%d, rowsPerPage:%d, }, {}, %s); } ); </script> """ if fields is None: fields = _utils.get_field_list(fields, table.schema) div_id = _html.Html.next_id() meta_count = ('rows: %d' % table.length) if table.length >= 0 else '' meta_name = str(table) if table.job is None else ('job: %s' % table.job.id) if table.job: if table.job.cache_hit: meta_cost = 'cached' else: bytes = gcp.bigquery._query_stats.QueryStats._size_formatter( table.job.bytes_processed) meta_cost = '%s processed' % bytes meta_time = 'time: %.1fs' % table.job.total_time else: meta_cost = '' meta_time = '' data, total_count = _utils.get_data(table, fields, 0, rows_per_page) if total_count < 0: # The table doesn't have a length metadata property but may still be small if we fetched less # rows than we asked for. fetched_count = len(data['rows']) if fetched_count < rows_per_page: total_count = fetched_count chart = 'table' if 0 <= total_count <= rows_per_page else 'paged_table' meta_entries = [meta_count, meta_time, meta_cost, meta_name] meta_data = '(%s)' % (', '.join( [entry for entry in meta_entries if len(entry)])) return _HTML_TEMPLATE %\ (div_id, meta_data, div_id, chart, _utils.get_data_source_index(str(table)), ','.join(fields), total_count, rows_per_page, json.dumps(data, cls=gcp._util.JSONEncoder))
def _chart_cell(args, cell): source = args['data'] ipy = IPython.get_ipython() chart_options = _utils.parse_config(cell, ipy.user_ns) if chart_options is None: chart_options = {} elif not isinstance(chart_options, dict): raise Exception("Could not parse chart options") fields = args['fields'] if args['fields'] else '*' div_id = _html.Html.next_id() env = {} controls_html = '' controls_ids = [] if 'variables' in chart_options: variables = chart_options['variables'] del chart_options['variables'] # Just to make sure GCharts doesn't see them. try: item = _utils.get_notebook_item(source) _, defaults = gcp.data.SqlModule.get_sql_statement_with_environment(item, '') except Exception: defaults = {} for varname, control in variables.items(): label = control.get('label', varname) control_id = div_id + '__' + varname controls_ids.append(control_id) value = control.get('value', defaults.get(varname, None)) # The user should usually specify the type but we will default to 'textbox' for strings # and 'set' for lists. if isinstance(value, basestring): type = 'textbox' elif isinstance(value, list): type = 'set' else: type = None type = control.get('type', type) if type == 'picker': choices = control.get('choices', value) if not isinstance(choices, list) or len(choices) == 0: raise Exception('picker control must specify a nonempty set of choices') if value is None: value = choices[0] choices_html = '' for i, choice in enumerate(choices): choices_html += "<option value=\"%s\" %s>%s</option>" % \ (choice, ("selected=\"selected\"" if choice == value else ''), choice) control_html = "{label}<select disabled id=\"{id}\">{choices}</select>"\ .format(label=label, id=control_id, choices=choices_html) elif type == 'set': # Multi-picker; implemented as checkboxes. # TODO(gram): consider using "name" property of the control to group checkboxes. That # way we can save the code of constructing and parsing control Ids with sequential # numbers in it. Multiple checkboxes can share the same name. choices = control.get('choices', value) if not isinstance(choices, list) or len(choices) == 0: raise Exception('set control must specify a nonempty set of choices') if value is None: value = choices choices_html = '' controls_ids[-1] = '%s:%d' % (control_id, len(choices)) # replace ID to include count. for i, choice in enumerate(choices): checked = choice in value choice_id = '%s:%d' % (control_id, i) # TODO(gram): we may want a 'Submit/Refresh button as we may not want to rerun # query on each checkbox change. choices_html += """ <div> <label> <input type="checkbox" id="{id}" value="{choice}" {checked} disabled> {choice} </label> </div> """.format(id=choice_id, choice=choice, checked="checked" if checked else '') control_html = "{label}<div>{choices}</div>".format(label=label, choices=choices_html) elif type == 'checkbox': control_html = """ <label> <input type="checkbox" id="{id}" {checked} disabled> {label} </label> """.format(label=label, id=control_id, checked="checked" if value else '') elif type == 'slider': min = control.get('min', None) max = control.get('max', None) if min is None or max is None: raise Exception('slider control must specify a min and max value') if max <= min: raise Exception('slider control must specify a min value less than max value') step = control.get('step', 1 if isinstance(min, int) and isinstance(max, int) else (max - min) / 10.0) if value is None: value = min control_html = """ {label} <input type="text" class="gchart-slider_value" id="{id}_value" value="{value}" disabled/> <input type="range" class="gchart-slider" id="{id}" min="{min}" max="{max}" step="{step}" value="{value}" disabled/> """.format(label=label, id=control_id, value=value, min=min, max=max, step=step) elif type == 'textbox': if value is None: value = '' control_html = "{label}<input type=\"text\" value=\"{value}\" id=\"{id}\" disabled/>"\ .format(label=label, value=value, id=control_id) else: raise Exception( 'Unknown control type %s (expected picker, slider, checkbox, textbox or set)' % type) env[varname] = value controls_html += "<div class=\"gchart-control\">{control}</div>\n"\ .format(control=control_html) controls_html = "<div class=\"gchart-controls\">{controls}</div>".format(controls=controls_html) _HTML_TEMPLATE = """ <div class="bqgc-container"> {controls} <div class="bqgc{extra_class}" id="{id}"> </div> </div> <script> require(['extensions/charting', 'element!{id}', 'style!/static/extensions/charting.css'], function(charts, dom) {{ charts.render(dom, {{chartStyle:'{chart_type}', dataName:'{source}', fields:'{fields}'}}, {options}, {data}, {control_ids}); }} ); </script> """ chart_type = args['chart'] count = 25 if chart_type == 'paged_table' else -1 data, _ = _utils.get_data(source, fields, env, 0, count) # TODO(gram): check if we need to augment env with user_ns return IPython.core.display.HTML( _HTML_TEMPLATE.format(controls=controls_html, id=div_id, chart_type=chart_type, extra_class=" bqgc-controlled" if len(controls_html) else '', source=_utils.get_data_source_index(source), fields=fields, options=json.dumps(chart_options, cls=gcp._util.JSONEncoder), data=json.dumps(data, cls=gcp._util.JSONEncoder), control_ids=str(controls_ids)))
def _table_viewer(table, rows_per_page=25, fields=None): """ Return a table viewer. Args: table: the table to view. rows_per_page: how many rows to display at one time. fields: an array of field names to display; default is None which uses the full schema. Returns: A string containing the HTML for the table viewer. """ if not table.exists(): raise Exception('%s does not exist' % str(table)) _HTML_TEMPLATE = """ <div class="bqtv" id="%s"></div> <br />%s<br /> <script> require(['extensions/charting', 'element!%s', 'style!/static/extensions/charting.css'], function(charts, dom) { charts.render(dom, { chartStyle:"%s", dataName:"%s", fields:"%s", totalRows:%d, rowsPerPage:%d, }, {}, %s); } ); </script> """ if fields is None: fields = _utils.get_field_list(fields, table.schema) div_id = _html.Html.next_id() meta_count = ('rows: %d' % table.length) if table.length >= 0 else '' meta_name = str(table) if table.job is None else ('job: %s' % table.job.id) if table.job: if table.job.cache_hit: meta_cost = 'cached' else: bytes = gcp.bigquery._query_stats.QueryStats._size_formatter(table.job.bytes_processed) meta_cost = '%s processed' % bytes meta_time = 'time: %.1fs' % table.job.total_time else: meta_cost = '' meta_time = '' data, total_count = _utils.get_data(table, fields, 0, rows_per_page) if total_count < 0: # The table doesn't have a length metadata property but may still be small if we fetched less # rows than we asked for. fetched_count = len(data['rows']) if fetched_count < rows_per_page: total_count = fetched_count chart = 'table' if 0 <= total_count <= rows_per_page else 'paged_table' meta_entries = [meta_count, meta_time, meta_cost, meta_name] meta_data = '(%s)' % (', '.join([entry for entry in meta_entries if len(entry)])) return _HTML_TEMPLATE %\ (div_id, meta_data, div_id, chart, _utils.get_data_source_index(str(table)), ','.join(fields), total_count, rows_per_page, json.dumps(data, cls=gcp._util.JSONEncoder))
def _table_viewer(table, rows_per_page=25, fields=None): """ Return a table viewer. This includes a static rendering of the first page of the table, that gets replaced by the charting code in environments where Javascript is executable and BQ is available. Args: table: the table to view. rows_per_page: how many rows to display at one time. fields: an array of field names to display; default is None which uses the full schema. Returns: A string containing the HTML for the table viewer. """ if not table.exists(): raise Exception('%s does not exist' % str(table)) _HTML_TEMPLATE = """ <div class="bqtv" id="{div_id}">{static_table}</div> <br />{meta_data}<br /> <script> require(['extensions/charting', 'element!{div_id}', 'style!/static/extensions/charting.css'], function(charts, dom) {{ charts.render(dom, {{ chartStyle:"{chart_style}", dataName:"{data_name}", fields:"{fields}", totalRows:{total_rows}, rowsPerPage:{rows_per_page}, }}, {{}}, {data}); }} ); </script> """ if fields is None: fields = _utils.get_field_list(fields, table.schema) div_id = _html.Html.next_id() meta_count = ('rows: %d' % table.length) if table.length >= 0 else '' meta_name = str(table) if table.job is None else ('job: %s' % table.job.id) if table.job: if table.job.cache_hit: meta_cost = 'cached' else: bytes = gcp.bigquery._query_stats.QueryStats._size_formatter( table.job.bytes_processed) meta_cost = '%s processed' % bytes meta_time = 'time: %.1fs' % table.job.total_time else: meta_cost = '' meta_time = '' data, total_count = _utils.get_data(table, fields, 0, rows_per_page) if total_count < 0: # The table doesn't have a length metadata property but may still be small if we fetched less # rows than we asked for. fetched_count = len(data['rows']) if fetched_count < rows_per_page: total_count = fetched_count chart = 'table' if 0 <= total_count <= rows_per_page else 'paged_table' meta_entries = [meta_count, meta_time, meta_cost, meta_name] meta_data = '(%s)' % (', '.join( [entry for entry in meta_entries if len(entry)])) return _HTML_TEMPLATE.format( div_id=div_id, static_table=_html.HtmlBuilder.render_chart_data(data), meta_data=meta_data, chart_style=chart, data_name=_utils.get_data_source_index(str(table)), fields=','.join(fields), total_rows=total_count, rows_per_page=rows_per_page, data=json.dumps(data, ensure_ascii=False, cls=gcp._util.JSONEncoder))