def report(self, r, **attr): """ Pivot table report page @param r: the S3Request instance @param attr: controller attributes for the request """ output = {} resource = self.resource get_config = resource.get_config show_filter_form = False if r.representation in ("html", "iframe"): filter_widgets = get_config("filter_widgets", None) if filter_widgets and not self.hide_filter: # Apply filter defaults (before rendering the data!) from s3filter import S3FilterForm show_filter_form = True S3FilterForm.apply_filter_defaults(r, resource) # Filter response = current.response s3_filter = response.s3.filter if s3_filter is not None: resource.add_filter(s3_filter) widget_id = "pivottable" # @todo: make configurable: maxrows = 10 maxcols = 10 # Extract the relevant GET vars report_vars = ("rows", "cols", "fact", "aggregate", "totals") get_vars = dict( (k, v) for k, v in r.get_vars.iteritems() if k in report_vars) # Fall back to report options defaults report_options = get_config("report_options", {}) defaults = report_options.get("defaults", {}) if not any(k in get_vars for k in ("rows", "cols", "fact")): get_vars = defaults get_vars["chart"] = r.get_vars.get("chart", defaults.get("chart", None)) get_vars["table"] = r.get_vars.get("table", defaults.get("table", None)) # Generate the pivot table if get_vars: rows = get_vars.get("rows", None) cols = get_vars.get("cols", None) layer = get_vars.get("fact", "id") if layer is not None: m = layer_pattern.match(layer) if m is None: # Backward-compatiblity: alternative "aggregate" option selector = layer if get_vars and "aggregate" in get_vars: method = get_vars["aggregate"] else: method = "count" else: selector, method = m.group(2), m.group(1) if not layer or not any([rows, cols]): pivottable = None else: prefix = resource.prefix_selector selector = prefix(selector) layer = (selector, method) get_vars["rows"] = prefix(rows) if rows else None get_vars["cols"] = prefix(cols) if cols else None get_vars["fact"] = "%s(%s)" % (method, selector) pivottable = resource.pivottable(rows, cols, [layer]) else: pivottable = None # Render as JSON-serializable dict if pivottable is not None: pivotdata = pivottable.json(maxrows=maxrows, maxcols=maxcols) else: pivotdata = None if r.representation in ("html", "iframe"): tablename = resource.tablename output["title"] = self.crud_string(tablename, "title_report") # Filter widgets if show_filter_form: advanced = False for widget in filter_widgets: if "hidden" in widget.opts and widget.opts.hidden: advanced = resource.get_config("report_advanced", True) break filter_formstyle = get_config("filter_formstyle", None) filter_form = S3FilterForm(filter_widgets, formstyle=filter_formstyle, advanced=advanced, submit=False, _class="filter-form", _id="%s-filter-form" % widget_id) fresource = current.s3db.resource(tablename) alias = resource.alias if r.component else None filter_widgets = filter_form.fields(fresource, r.get_vars, alias=alias) else: # Render as empty string to avoid the exception in the view filter_widgets = None # Generate the report form ajax_vars = Storage(r.get_vars) ajax_vars.update(get_vars) filter_url = r.url(method="", representation="", vars=ajax_vars.fromkeys( (k for k in ajax_vars if k not in report_vars))) ajaxurl = attr.get( "ajaxurl", r.url(method="report", representation="json", vars=ajax_vars)) output["form"] = S3ReportForm(resource) \ .html(pivotdata, get_vars = get_vars, filter_widgets = filter_widgets, ajaxurl = ajaxurl, filter_url = filter_url, widget_id = widget_id) # View response.view = self._view(r, "report.html") elif r.representation == "json": output = json.dumps(pivotdata, separators=SEPARATORS) else: r.error(501, current.ERROR.BAD_FORMAT) return output
def summary(self, r, **attr): """ Render the summary page @param r: the S3Request @param attr: controller attributes """ output = {} response = current.response resource = self.resource get_config = resource.get_config # Get Summary Page Configuration config = self._get_config(resource) # Page title crud_string = self.crud_string title = crud_string(self.tablename, "title_list") output["title"] = title # Tabs tablist = UL() sections = [] commons = [] # Active tab if "t" in r.get_vars: active_tab = int(r.get_vars["t"]) else: active_tab = 0 active_map = None show_filter_form = False filter_widgets = get_config("filter_widgets") if filter_widgets and not self.hide_filter: # Apply filter defaults (before rendering the data!) show_filter_form = True S3FilterForm.apply_filter_defaults(r, resource) # Render sections tab_idx = 0 widget_idx = 0 targets = [] pending = [] # Dynamic filtering (e.g. plot-click in report widget) attr["filter_form"] = form_id = "summary-filter-form" for section in config: common = section.get("common") # Section container section_id = section["name"] s = DIV(_class="section-container", _id=section_id) if not common: # Label label = section["label"] translate = section.get("translate", True) if isinstance(label, basestring) and translate: label = current.T(label) # Add tab tablist.append(LI(A(label, _href="#%s" % section_id))) if common or active_tab == tab_idx: visible = True else: visible = False # Widgets widgets = section.get("widgets", []) for widget in widgets: # Widget ID widget_id = "summary-%s" % widget_idx # Make sure widgets include the widget ID when # generating Ajax URLs: r.get_vars["w"] = r.vars["w"] = widget_id # Append to filter targets filterable = widget.get("filterable", True) if filterable: targets.append(widget_id) if not visible and widget.get("ajax_init"): pending.append(widget_id) # Apply method method = widget.get("method") if callable(method): content = method(r, widget_id=widget_id, visible=visible, **attr) else: handler = r.get_widget_handler(method) if handler is None: # Fall back to CRUD handler = resource.crud if handler is not None: if method == "datatable": # Assume that we have a FilterForm, so disable Quick Search dtargs = attr.get("dtargs", {}) dtargs["dt_searching"] = "false" attr["dtargs"] = dtargs content = handler(r, method=method, widget_id=widget_id, visible=visible, **attr) else: r.error(405, current.ERROR.BAD_METHOD) # Add content to section if isinstance(content, dict): if r.http == "POST" and content.get("success"): # Form successfully processed: behave like the # primary method handler and redirect to next next_url = content.get("next") if next_url: self.next = next_url return content for k, v in content.items(): if k not in ("tabs", "sections", "widget"): output[k] = v content = content.get("widget", "EMPTY") elif active_tab == tab_idx and isinstance(content, MAP): active_map = content s.append(DIV(content, _id="%s-container" % widget_id, _class="widget-container")) widget_idx += 1 if common: commons.append(s) else: sections.append(s) tab_idx += 1 # Remove widget ID r.get_vars.pop("w", None) # Add tabs + sections to output if len(sections) > 1: output["tabs"] = tablist # Hide tabbed sections initially to avoid visible artifacts # in slow page loads (S3.search.summary_tabs will un-hide the active one): for s in sections: s.add_class("hide") else: # Hide tabs if there's only one section (but then don't hide # the section!) output["tabs"] = "" output["sections"] = sections # Add common sections to output output["common"] = commons # Filter targets target = " ".join(targets) # Filter form filter_ajax = True if show_filter_form: # Where to retrieve filtered data from: if active_tab != 0: submit_url_vars = {"t": active_tab} else: submit_url_vars = {} filter_submit_url = attr.get("filter_submit_url") if not filter_submit_url: _vars = self._remove_filters(r.get_vars) _vars.update(submit_url_vars) filter_submit_url = r.url(vars=_vars) # Where to retrieve updated filter options from: filter_ajax_url = attr.get("filter_ajax_url", r.url(method="filter", vars={}, representation="options")) filter_clear = get_config("filter_clear", current.deployment_settings.get_ui_filter_clear()) filter_formstyle = get_config("filter_formstyle") filter_submit = get_config("filter_submit", True) filter_form = S3FilterForm(filter_widgets, clear=filter_clear, formstyle=filter_formstyle, submit=filter_submit, ajax=filter_ajax, url=filter_submit_url, ajaxurl=filter_ajax_url, _class="filter-form", _id=form_id) fresource = current.s3db.resource(resource.tablename) alias = resource.alias if r.component else None output["filter_form"] = filter_form.html(fresource, r.get_vars, target=target, alias=alias) else: # Render as empty string to avoid the exception in the view output["filter_form"] = "" # View response.view = self._view(r, "summary.html") if len(sections) > 1: # Provide a comma-separated list of initially hidden widgets # which are rendered empty and need a trigger to Ajax-load # their data layer (e.g. maps, reports): pending = ",".join(pending) if pending else "null" # Render the Sections as Tabs script = '''S3.search.summary_tabs("%s",%s,"%s")''' % \ (form_id, active_tab, pending) response.s3.jquery_ready.append(script) if active_map: # If there is a map on the active tab then we need to add # a callback to the Map JS Loader active_map.callback = '''S3.search.summary_maps("%s")''' % form_id return output
def report(self, r, **attr): """ Pivot table report page @param r: the S3Request instance @param attr: controller attributes for the request """ output = {} resource = self.resource get_config = resource.get_config show_filter_form = False if r.representation in ("html", "iframe"): filter_widgets = get_config("filter_widgets", None) if filter_widgets and not self.hide_filter: # Apply filter defaults (before rendering the data!) from s3filter import S3FilterForm show_filter_form = True S3FilterForm.apply_filter_defaults(r, resource) # Filter response = current.response s3_filter = response.s3.filter if s3_filter is not None: resource.add_filter(s3_filter) widget_id = "pivottable" # @todo: make configurable: maxrows = 10 maxcols = 10 # Extract the relevant GET vars report_vars = ("rows", "cols", "fact", "aggregate", "totals") get_vars = dict((k, v) for k, v in r.get_vars.iteritems() if k in report_vars) # Fall back to report options defaults report_options = get_config("report_options", {}) defaults = report_options.get("defaults", {}) if not any (k in get_vars for k in ("rows", "cols", "fact")): get_vars = defaults get_vars["chart"] = r.get_vars.get("chart", defaults.get("chart", None)) get_vars["table"] = r.get_vars.get("table", defaults.get("table", None)) # Generate the pivot table if get_vars: rows = get_vars.get("rows", None) cols = get_vars.get("cols", None) layer = get_vars.get("fact", "id") if layer is not None: m = layer_pattern.match(layer) if m is None: # Backward-compatiblity: alternative "aggregate" option selector = layer if get_vars and "aggregate" in get_vars: method = get_vars["aggregate"] else: method = "count" else: selector, method = m.group(2), m.group(1) if not layer or not any([rows, cols]): pivottable = None else: prefix = resource.prefix_selector selector = prefix(selector) layer = (selector, method) get_vars["rows"] = prefix(rows) if rows else None get_vars["cols"] = prefix(cols) if cols else None get_vars["fact"] = "%s(%s)" % (method, selector) pivottable = resource.pivottable(rows, cols, [layer]) else: pivottable = None # Render as JSON-serializable dict if pivottable is not None: pivotdata = pivottable.json(maxrows=maxrows, maxcols=maxcols) else: pivotdata = None if r.representation in ("html", "iframe"): tablename = resource.tablename output["title"] = self.crud_string(tablename, "title_report") # Filter widgets if show_filter_form: advanced = False for widget in filter_widgets: if "hidden" in widget.opts and widget.opts.hidden: advanced = resource.get_config("report_advanced", True) break filter_formstyle = get_config("filter_formstyle", None) filter_form = S3FilterForm(filter_widgets, formstyle=filter_formstyle, advanced=advanced, submit=False, _class="filter-form", _id="%s-filter-form" % widget_id) fresource = current.s3db.resource(tablename) alias = resource.alias if r.component else None filter_widgets = filter_form.fields(fresource, r.get_vars, alias=alias) else: # Render as empty string to avoid the exception in the view filter_widgets = None # Generate the report form ajax_vars = Storage(r.get_vars) ajax_vars.update(get_vars) filter_url = r.url(method="", representation="", vars=ajax_vars.fromkeys((k for k in ajax_vars if k not in report_vars))) ajaxurl = attr.get("ajaxurl", r.url(method="report", representation="json", vars=ajax_vars)) output["form"] = S3ReportForm(resource) \ .html(pivotdata, get_vars = get_vars, filter_widgets = filter_widgets, ajaxurl = ajaxurl, filter_url = filter_url, widget_id = widget_id) # View response.view = self._view(r, "report.html") elif r.representation == "json": output = json.dumps(pivotdata, separators=SEPARATORS) else: r.error(501, current.ERROR.BAD_FORMAT) return output
def summary(self, r, **attr): """ Render the summary page @param r: the S3Request @param attr: controller attributes """ output = {} response = current.response resource = self.resource get_config = resource.get_config # Get Summary Page Configuration config = self._get_config(resource) # Page title crud_string = self.crud_string title = crud_string(self.tablename, "title_list") output["title"] = title # Tabs tablist = UL() sections = [] commons = [] # Active tab if "t" in r.get_vars: active_tab = int(r.get_vars["t"]) else: active_tab = 0 active_map = None show_filter_form = False filter_widgets = get_config("filter_widgets") if filter_widgets and not self.hide_filter: # Apply filter defaults (before rendering the data!) show_filter_form = True S3FilterForm.apply_filter_defaults(r, resource) # Render sections tab_idx = 0 widget_idx = 0 targets = [] pending = [] # Dynamic filtering (e.g. plot-click in report widget) attr["filter_form"] = form_id = "summary-filter-form" for section in config: common = section.get("common") # Section container section_id = section["name"] s = DIV(_class="section-container", _id=section_id) if not common: # Label label = section["label"] translate = section.get("translate", True) if isinstance(label, basestring) and translate: label = current.T(label) # Add tab tablist.append(LI(A(label, _href="#%s" % section_id))) if common or active_tab == tab_idx: visible = True else: visible = False # Widgets widgets = section.get("widgets", []) for widget in widgets: # Widget ID widget_id = "summary-%s" % widget_idx # Make sure widgets include the widget ID when # generating Ajax URLs: r.get_vars["w"] = r.vars["w"] = widget_id # Append to filter targets filterable = widget.get("filterable", True) if filterable: targets.append(widget_id) if not visible and widget.get("ajax_init"): pending.append(widget_id) # Apply method method = widget.get("method") if callable(method): content = method(r, widget_id=widget_id, visible=visible, **attr) else: handler = r.get_widget_handler(method) if handler is None: # Fall back to CRUD handler = resource.crud if handler is not None: if method == "datatable": # Assume that we have a FilterForm, so disable Quick Search dtargs = attr.get("dtargs", {}) dtargs["dt_bFilter"] = "false" attr["dtargs"] = dtargs content = handler(r, method=method, widget_id=widget_id, visible=visible, **attr) else: r.error(405, current.ERROR.BAD_METHOD) # Add content to section if isinstance(content, dict): if r.http == "POST" and content.get("success"): # Form successfully processed: behave like the # primary method handler and redirect to next next_url = content.get("next") if next_url: self.next = next_url return content for k, v in content.items(): if k not in ("tabs", "sections", "widget"): output[k] = v content = content.get("widget", "EMPTY") elif active_tab == tab_idx and isinstance(content, MAP): active_map = content s.append( DIV(content, _id="%s-container" % widget_id, _class="widget-container")) widget_idx += 1 if common: commons.append(s) else: sections.append(s) tab_idx += 1 # Remove widget ID r.get_vars.pop("w", None) # Add tabs + sections to output if len(sections) > 1: output["tabs"] = tablist # Hide tabbed sections initially to avoid visible artifacts # in slow page loads (S3.search.summary_tabs will un-hide the active one): for s in sections: s.add_class("hide") else: # Hide tabs if there's only one section (but then don't hide # the section!) output["tabs"] = "" output["sections"] = sections # Add common sections to output output["common"] = commons # Filter targets target = " ".join(targets) # Filter form filter_ajax = True if show_filter_form: # Where to retrieve filtered data from: if active_tab != 0: submit_url_vars = {"t": active_tab} else: submit_url_vars = {} filter_submit_url = attr.get("filter_submit_url") if not filter_submit_url: _vars = self._remove_filters(r.get_vars) _vars.update(submit_url_vars) filter_submit_url = r.url(vars=_vars) # Where to retrieve updated filter options from: filter_ajax_url = attr.get( "filter_ajax_url", r.url(method="filter", vars={}, representation="options")) filter_formstyle = get_config("filter_formstyle") filter_submit = get_config("filter_submit", True) filter_form = S3FilterForm(filter_widgets, formstyle=filter_formstyle, submit=filter_submit, ajax=filter_ajax, url=filter_submit_url, ajaxurl=filter_ajax_url, _class="filter-form", _id=form_id) fresource = current.s3db.resource(resource.tablename) alias = resource.alias if r.component else None output["filter_form"] = filter_form.html(fresource, r.get_vars, target=target, alias=alias) else: # Render as empty string to avoid the exception in the view output["filter_form"] = "" # View response.view = self._view(r, "summary.html") if len(sections) > 1: # Provide a comma-separated list of initially hidden widgets # which are rendered empty and need a trigger to Ajax-load # their data layer (e.g. maps, reports): pending = ",".join(pending) if pending else "null" # Render the Sections as Tabs script = '''S3.search.summary_tabs("%s",%s,"%s")''' % \ (form_id, active_tab, pending) response.s3.jquery_ready.append(script) if active_map: # If there is a map on the active tab then we need to add # a callback to the Map JS Loader active_map.callback = '''S3.search.summary_maps("%s")''' % form_id return output
def timeplot(self, r, **attr): """ Time plot report page @param r: the S3Request instance @param attr: controller attributes for the request """ output = {} # Extract the relevant GET vars # @todo: option for grouping report_vars = ( "timestamp", "fact", "start", "end", "slots", "baseline", ) get_vars = dict( (k, v) for k, v in r.get_vars.iteritems() if k in report_vars) # Execute on component? resource = self.resource alias = r.get_vars.get("component") if alias and alias not in (resource.alias, "~"): if alias not in resource.components: hook = current.s3db.get_component(resource.tablename, alias) if hook: resource._attach(alias, hook) if alias in resource.components: resource = resource.components[alias] tablename = resource.tablename get_config = resource.get_config # Apply filter defaults (before rendering the data!) show_filter_form = False if r.representation in ("html", "iframe"): filter_widgets = get_config("filter_widgets", None) if filter_widgets and not self.hide_filter: from s3filter import S3FilterForm show_filter_form = True S3FilterForm.apply_filter_defaults(r, resource) # Fall back to report options defaults report_options = resource.get_config("timeplot_options", {}) defaults = report_options.get("defaults", {}) if not any(k in get_vars for k in report_vars): get_vars = defaults else: # Optional URL args always fall back to config: optional = ("timestamp", ) for opt in optional: if opt not in get_vars and opt in defaults: get_vars[opt] = defaults[opt] # Parse event timestamp option timestamp = get_vars.get("timestamp") try: event_start, event_end = self.resolve_timestamp( resource, timestamp) except (SyntaxError, AttributeError): r.error(400, sys.exc_info()[1]) # Parse fact option fact = get_vars.get("fact") try: method, rfields, arguments = self.resolve_fact(resource, fact) except (SyntaxError, AttributeError): r.error(400, sys.exc_info()[1]) # Create event frame start = get_vars.get("start") end = get_vars.get("end") slots = get_vars.get("slots") try: event_frame = self.create_event_frame( resource, event_start, event_end, start, end, slots, ) except (SyntaxError, ValueError): r.error(400, sys.exc_info()[1]) # Add event data baseline = get_vars.get("baseline") try: self.add_event_data( event_frame, resource, event_start, event_end, rfields, baseline=baseline, cumulative=method == "cumulate", ) except SyntaxError: pass # Iterate over the event frame to collect aggregates items = [] new_item = items.append for period in event_frame: item_start = period.start if item_start: item_start = item_start.isoformat() item_end = period.end if item_end: item_end = item_end.isoformat() value = period.aggregate( method=method, fields=[rfield.colname for rfield in rfields], arguments=arguments, event_type=tablename, ) new_item((item_start, item_end, value)) # Timeplot data data = { "items": items, "empty": event_frame.empty, "baseline": event_frame.baseline, } # Widget ID widget_id = "timeplot" # Render output if r.representation in ("html", "iframe"): # Page load output["title"] = self.crud_string(tablename, "title_report") # Filter widgets if show_filter_form: advanced = False for widget in filter_widgets: if "hidden" in widget.opts and widget.opts.hidden: advanced = get_config("report_advanced", True) break filter_formstyle = get_config("filter_formstyle", None) filter_form = S3FilterForm( filter_widgets, formstyle=filter_formstyle, advanced=advanced, submit=False, _class="filter-form", _id="%s-filter-form" % widget_id, ) fresource = current.s3db.resource(tablename) alias = resource.alias if resource.parent else None filter_widgets = filter_form.fields( fresource, r.get_vars, alias=alias, ) else: # Render as empty string to avoid the exception in the view filter_widgets = None ajax_vars = Storage(r.get_vars) ajax_vars.update(get_vars) filter_url = url = r.url(method="", representation="", vars=ajax_vars.fromkeys( (k for k in ajax_vars if k not in report_vars))) ajaxurl = attr.get( "ajaxurl", r.url( method="timeplot", representation="json", vars=ajax_vars, )) output["form"] = S3TimePlotForm(resource) \ .html(data, get_vars = get_vars, filter_widgets = filter_widgets, ajaxurl = ajaxurl, filter_url = filter_url, widget_id = widget_id, ) # View response = current.response response.view = self._view(r, "timeplot.html") elif r.representation == "json": # Ajax load output = json.dumps(data, separators=SEPARATORS) else: r.error(501, current.ERROR.BAD_FORMAT) return output
def organizer(self, r, **attr): """ Render the organizer view (HTML method) @param r: the S3Request instance @param attr: controller attributes @returns: dict of values for the view """ output = {} resource = self.resource get_config = resource.get_config # Parse resource configuration config = self.parse_config(resource) start = config["start"] end = config["end"] widget_id = "organizer" # Filter Defaults hide_filter = self.hide_filter filter_widgets = get_config("filter_widgets", None) show_filter_form = False default_filters = None if filter_widgets and not hide_filter: # Drop all filter widgets for start/end fields # (so they don't clash with the organizer's own filters) fw = [] prefix_selector = self.prefix_selector for filter_widget in filter_widgets: if not filter_widget: continue filter_field = filter_widget.field if isinstance(filter_field, basestring): filter_field = prefix_selector(resource, filter_field) if start and start.selector == filter_field or \ end and end.selector == filter_field: continue fw.append(filter_widget) filter_widgets = fw if filter_widgets: show_filter_form = True # Apply filter defaults (before rendering the data!) from s3filter import S3FilterForm default_filters = S3FilterForm.apply_filter_defaults( r, resource) # Filter Form if show_filter_form: get_vars = r.get_vars # Where to retrieve filtered data from filter_submit_url = attr.get("filter_submit_url") if not filter_submit_url: get_vars_ = self._remove_filters(get_vars) filter_submit_url = r.url(vars=get_vars_) # Where to retrieve updated filter options from: filter_ajax_url = attr.get("filter_ajax_url") if filter_ajax_url is None: filter_ajax_url = r.url( method="filter", vars={}, representation="options", ) filter_clear = get_config( "filter_clear", current.deployment_settings.get_ui_filter_clear()) filter_formstyle = get_config("filter_formstyle", None) filter_submit = get_config("filter_submit", True) filter_form = S3FilterForm(filter_widgets, clear=filter_clear, formstyle=filter_formstyle, submit=filter_submit, ajax=True, url=filter_submit_url, ajaxurl=filter_ajax_url, _class="filter-form", _id="%s-filter-form" % widget_id) fresource = current.s3db.resource( resource.tablename) # Use a clean resource alias = resource.alias if r.component else None output["list_filter_form"] = filter_form.html(fresource, get_vars, target=widget_id, alias=alias) else: # Render as empty string to avoid the exception in the view output["list_filter_form"] = "" # Page Title crud_string = self.crud_string if r.representation != "iframe": if r.component: title = crud_string(r.tablename, "title_display") else: title = crud_string(self.tablename, "title_list") output["title"] = title # Configure Resource permitted = self._permitted resource_config = {"ajaxURL": r.url(representation="json"), "useTime": config.get("use_time"), "baseURL": r.url(method=""), "labelCreate": s3_str(crud_string(self.tablename, "label_create")), "insertable": resource.get_config("insertable", True) and \ permitted("create"), "editable": resource.get_config("editable", True) and \ permitted("update"), "startEditable": start.field and start.field.writable, "durationEditable": end and end.field and end.field.writable, "deletable": resource.get_config("deletable", True) and \ permitted("delete"), # Forced reload on update, e.g. if onaccept changes # other data that are visible in the organizer "reloadOnUpdate": config.get("reload_on_update", False), } # Start and End Field resource_config["start"] = start.selector if start else None resource_config["end"] = end.selector if end else None # Description Labels labels = [] for rfield in config["description"]: label = rfield.label if label is not None: label = s3_str(label) labels.append((rfield.colname, label)) resource_config["columns"] = labels # Colors color = config.get("color") if color: resource_config["color"] = color.colname resource_config["colors"] = config.get("colors") # Generate form key formkey = uuid.uuid4().get_hex() # Store form key in session session = current.session keyname = "_formkey[%s]" % self.formname(r) session[keyname] = session.get(keyname, [])[-9:] + [formkey] # Instantiate Organizer Widget widget = S3OrganizerWidget([resource_config]) output["organizer"] = widget.html( widget_id=widget_id, formkey=formkey, ) # View current.response.view = self._view(r, "organize.html") return output
def timeplot(self, r, **attr): """ Time plot report page @param r: the S3Request instance @param attr: controller attributes for the request """ output = {} # Extract the relevant GET vars # @todo: option for grouping report_vars = ("timestamp", "fact", "start", "end", "slots", "baseline", ) get_vars = dict((k, v) for k, v in r.get_vars.iteritems() if k in report_vars) # Execute on component? resource = self.resource alias = r.get_vars.get("component") if alias and alias not in (resource.alias, "~"): if alias not in resource.components: hook = current.s3db.get_component(resource.tablename, alias) if hook: resource._attach(alias, hook) if alias in resource.components: resource = resource.components[alias] tablename = resource.tablename get_config = resource.get_config # Apply filter defaults (before rendering the data!) show_filter_form = False if r.representation in ("html", "iframe"): filter_widgets = get_config("filter_widgets", None) if filter_widgets and not self.hide_filter: from s3filter import S3FilterForm show_filter_form = True S3FilterForm.apply_filter_defaults(r, resource) # Fall back to report options defaults report_options = resource.get_config("timeplot_options", {}) defaults = report_options.get("defaults", {}) if not any(k in get_vars for k in report_vars): get_vars = defaults else: # Optional URL args always fall back to config: optional = ("timestamp",) for opt in optional: if opt not in get_vars and opt in defaults: get_vars[opt] = defaults[opt] # Parse event timestamp option timestamp = get_vars.get("timestamp") try: event_start, event_end = self.resolve_timestamp(resource, timestamp) except (SyntaxError, AttributeError): r.error(400, sys.exc_info()[1]) # Parse fact option fact = get_vars.get("fact") try: method, rfields, arguments = self.resolve_fact(resource, fact) except (SyntaxError, AttributeError): r.error(400, sys.exc_info()[1]) # Create event frame start = get_vars.get("start") end = get_vars.get("end") slots = get_vars.get("slots") try: event_frame = self.create_event_frame(resource, event_start, event_end, start, end, slots, ) except (SyntaxError, ValueError): r.error(400, sys.exc_info()[1]) # Add event data baseline = get_vars.get("baseline") try: self.add_event_data(event_frame, resource, event_start, event_end, rfields, baseline= baseline, cumulative = method == "cumulate", ) except SyntaxError: pass # Iterate over the event frame to collect aggregates items = [] new_item = items.append for period in event_frame: item_start = period.start if item_start: item_start = item_start.isoformat() item_end = period.end if item_end: item_end = item_end.isoformat() value = period.aggregate(method=method, fields=[rfield.colname for rfield in rfields], arguments=arguments, event_type=tablename, ) new_item((item_start, item_end, value)) # Timeplot data data = {"items": items, "empty": event_frame.empty, "baseline": event_frame.baseline, } # Widget ID widget_id = "timeplot" # Render output if r.representation in ("html", "iframe"): # Page load output["title"] = self.crud_string(tablename, "title_report") # Filter widgets if show_filter_form: advanced = False for widget in filter_widgets: if "hidden" in widget.opts and widget.opts.hidden: advanced = get_config("report_advanced", True) break filter_formstyle = get_config("filter_formstyle", None) filter_form = S3FilterForm(filter_widgets, formstyle=filter_formstyle, advanced=advanced, submit=False, _class="filter-form", _id="%s-filter-form" % widget_id, ) fresource = current.s3db.resource(tablename) alias = resource.alias if resource.parent else None filter_widgets = filter_form.fields(fresource, r.get_vars, alias=alias, ) else: # Render as empty string to avoid the exception in the view filter_widgets = None ajax_vars = Storage(r.get_vars) ajax_vars.update(get_vars) filter_url = url=r.url(method="", representation="", vars=ajax_vars.fromkeys((k for k in ajax_vars if k not in report_vars))) ajaxurl = attr.get("ajaxurl", r.url(method="timeplot", representation="json", vars=ajax_vars, )) output["form"] = S3TimePlotForm(resource) \ .html(data, get_vars = get_vars, filter_widgets = filter_widgets, ajaxurl = ajaxurl, filter_url = filter_url, widget_id = widget_id, ) # View response = current.response response.view = self._view(r, "timeplot.html") elif r.representation == "json": # Ajax load output = json.dumps(data, separators=SEPARATORS) else: r.error(501, current.ERROR.BAD_FORMAT) return output
def report(self, r, **attr): """ Report generator @param r: the S3Request instance @param attr: controller attributes """ T = current.T output = {} resource = self.resource tablename = resource.tablename get_config = resource.get_config representation = r.representation # Apply filter defaults before rendering the data show_filter_form = False if representation in ("html", "iframe"): filter_widgets = get_config("filter_widgets", None) if filter_widgets and not self.hide_filter: show_filter_form = True from s3filter import S3FilterForm S3FilterForm.apply_filter_defaults(r, resource) # Get the report configuration report_config = self.get_report_config() # Resolve selectors in the report configuration fields = self.resolve(report_config) # Get extraction method extract = report_config.get("extract") if not callable(extract): extract = self.extract selectors = [s for s in fields if fields[s] is not None] orderby = report_config.get("orderby_cols") # Extract the data items = extract(self.resource, selectors, orderby) # Group and aggregate groupby = report_config.get("groupby_cols") aggregate = report_config.get("aggregate_cols") gi = S3GroupedItems(items, groupby=groupby, aggregate=aggregate) # Report title title = report_config.get("title") if title is None: title = self.crud_string(tablename, "title_report") # Generate JSON data display_cols = report_config.get("display_cols") labels = report_config.get("labels") represent = report_config.get("groupby_represent") if representation == "pdf": as_dict = True else: as_dict = False data = gi.json(fields = display_cols, labels = labels, represent = represent, as_dict = as_dict, ) group_headers = report_config.get("group_headers", False) # Widget ID widget_id = "groupeditems" # Render output if representation in ("html", "iframe"): # Page load output["report_type"] = "groupeditems" output["widget_id"] = widget_id output["title"] = title # Filter form if show_filter_form: settings = current.deployment_settings # Filter form options filter_formstyle = get_config("filter_formstyle", None) filter_clear = get_config("filter_clear", settings.get_ui_filter_clear()) filter_submit = get_config("filter_submit", True) # Instantiate form filter_form = S3FilterForm(filter_widgets, formstyle=filter_formstyle, submit=filter_submit, clear=filter_clear, ajax=True, _class="filter-form", _id="%s-filter-form" % widget_id, ) # Render against unfiltered resource fresource = current.s3db.resource(tablename) alias = resource.alias if resource.parent else None output["filter_form"] = filter_form.html(fresource, r.get_vars, target = widget_id, alias = alias ) # Inject data items = INPUT(_type = "hidden", _id = "%s-data" % widget_id, _class = "gi-data", _value = data, ) output["items"] = items # Empty section output["empty"] = T("No data available") # Script options ajaxurl = attr.get("ajaxurl", r.url(method = "grouped", representation = "json", vars = r.get_vars, )) totals_label = report_config.get("totals_label", T("Total")) options = {"ajaxURL": ajaxurl, "totalsLabel": str(totals_label).upper(), "renderGroupHeaders": group_headers, } # Inject script self.inject_script(widget_id, options=options) # Export formats formats = DIV(DIV(_title = T("Export as PDF"), _class = "gi-export export_pdf", data = {"url": r.url(method = "grouped", representation = "pdf", vars = r.get_vars, ), }, ), _class="gi-export-formats", ) output["formats"] = formats # Detect and store theme-specific inner layout self._view(r, "grouped.html") # View response = current.response response.view = self._view(r, "report.html") elif representation == "json": # Ajax reload output = data elif representation == "pdf": # PDF Export totals_label = report_config.get("totals_label", T("Total")) pdf_header = report_config.get("pdf_header", DEFAULT) gi_table = S3GroupedItemsTable(resource, title = title, data = data, group_headers = group_headers, totals_label = totals_label, pdf_header = pdf_header, ) return gi_table.pdf(r) else: r.error(501, current.ERROR.BAD_FORMAT) return output
def summary(self, r, **attr): """ Render the summary page @param r: the S3Request @param attr: controller attributes """ output = {} resource = self.resource get_config = resource.get_config # Get Summary Page Configuration config = self._get_config(resource) # Page title crud_string = self.crud_string title = crud_string(self.tablename, "title_list") output["title"] = title # Tabs tablist = UL() sections = [] # Active tab if "t" in r.get_vars: active = r.get_vars["t"] else: active = None active_tab = "null" # Render sections section_idx = 0 widget_idx = 0 targets = [] for section in config: # Active tab? if active and active == str(section_idx): active_tab = section_idx # Label label = section["label"] translate = section.get("translate", True) if isinstance(label, basestring) and translate: self.label = current.T(label) else: self.label = label # Tab section_id = section["name"] tablist.append(LI(A(label, _href="#%s" % section_id))) # Section container s = DIV(_class="section-container", _id=section_id) # Widgets widgets = section.get("widgets", []) for widget in widgets: # Widget-ID widget_id = "summary-%s" % widget_idx # Make sure widgets include the widget ID when # generating Ajax-URLs: r.get_vars["w"] = r.vars["w"] = widget_id # Append to filter targets filterable = widget.get("filterable", True) if filterable: targets.append(widget_id) # Apply method method = widget.get("method", None) content = None if callable(method): content = method(r, widget_id=widget_id, **attr) else: handler = r.get_widget_handler(method) if handler is not None: content = handler(r, method=method, widget_id=widget_id, **attr) else: r.error(405, r.ERROR.BAD_METHOD) # Add content to section if isinstance(content, dict): for k, v in content.items(): if k not in ("tabs", "sections", "widget"): output[k] = v content = content.get("widget", "EMPTY") s.append( DIV(content, _id="%s-container" % widget_id, _class="widget-container")) widget_idx += 1 sections.append(s) section_idx += 1 # Add tabs + sections to output if len(sections) > 1: output["tabs"] = tablist else: # hide tabs if there's only one section output["tabs"] = "" output["sections"] = sections # Filter targets target = " ".join(targets) # Filter-form filter_ajax = True form_id = "summary-filter-form" filter_widgets = get_config("filter_widgets", None) hide_filter = attr.get("hide_filter", False) if filter_widgets and not hide_filter: # Where to retrieve filtered data from: if active_tab != "null": submit_url_vars = {"t": active_tab} else: submit_url_vars = {} filter_submit_url = attr.get("filter_submit_url", r.url(vars=submit_url_vars)) # Where to retrieve updated filter options from: filter_ajax_url = attr.get( "filter_ajax_url", r.url(method="filter", vars={}, representation="json")) filter_formstyle = get_config("filter_formstyle", None) filter_submit = get_config("filter_submit", True) filter_form = S3FilterForm(filter_widgets, formstyle=filter_formstyle, submit=filter_submit, ajax=filter_ajax, url=filter_submit_url, ajaxurl=filter_ajax_url, _class="filter-form", _id=form_id) fresource = current.s3db.resource(resource.tablename) alias = resource.alias if r.component else None output["filter_form"] = filter_form.html(fresource, r.get_vars, target=target, alias=alias) else: # Render as empty string to avoid the exception in the view output["filter_form"] = "" # View response = current.response response.view = self._view(r, "summary.html") # Script for tabs if len(sections) > 1: script = """ $('#summary-tabs').tabs({ active: %(active_tab)s, activate: function(event, ui) { if (ui.newTab.length) { S3.search.updateFilterSubmitURL('%(form_id)s', 't', $(ui.newTab).index()); } S3.search.updatePendingTargets('%(form_id)s'); } });""" % dict(form_id=form_id, active_tab=active_tab) response.s3.jquery_ready.append(script) return output