Exemple #1
0
    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
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
    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
Exemple #9
0
    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