Пример #1
0
def measure_graph(request, area_ident, filter='all'):
    """
    visualizes scores or measures in a graph

    identifier_list: [{'waterbody_slug': ...}, ...]
    start_end_dates: 2-tuple dates

    each row is an area
    """

    if filter == 'measure':
        measures = Measure.objects.filter(
            Q(pk=area_ident)|Q(parent__id=area_ident)).order_by('title')
    else:
        area = get_object_or_404(Area, ident=area_ident)

        if filter == 'focus':
            measures = [m for m in _sorted_measures(area)
                        if m.is_indicator == True]
        else:
            measures = _sorted_measures(area)

    start_date = iso8601.parse_date(request.GET.get('dt_start', '2008-1-1T00:00:00')).date()
    end_date = iso8601.parse_date(request.GET.get('dt_end', '2013-1-1T00:00:00')).date()
    width = int(request.GET.get('width', 380))
    height = int(request.GET.get('height', 170))
    legend_location = int(request.GET.get('legend-location', -1))
    wide_left_ticks = request.GET.get('wide_left_ticks', 'false') == 'true'
    format = request.GET.get('format', None)
    graph = DateGridGraph(width=width, height=height)

    _image_measures(graph, measures, start_date, end_date,
                    legend_location=legend_location,
                    wide_left_ticks=wide_left_ticks)

    graph.set_margins()

    if format == 'ps':                  
        return graph.render(
            response=HttpResponse(content_type='application/postscript'),
            format='ps')
    else:
        return graph.png_response(
            response=HttpResponse(content_type='image/png'))
Пример #2
0
    def get(self, request, *args, **kwargs):
        """
        Draw the EKR graph
        """

        dt_start, dt_end = self._dt_from_request()
        graph_items, graph_settings = self._graph_items_from_request()
        graph = DateGridGraph(
            width=int(graph_settings['width']),
            height=int(graph_settings['height']))

        # # Legend. Must do this before using graph location calculations
        # legend_handles = [
        #     Line2D([], [], color=value_to_html_color(0.8), lw=10),
        #     Line2D([], [], color=value_to_html_color(0.6), lw=10),
        #     Line2D([], [], color=value_to_html_color(0.4), lw=10),
        #     Line2D([], [], color=value_to_html_color(0.2), lw=10),
        #     Line2D([], [], color=value_to_html_color(0.0), lw=10),
        #     ]
        # legend_labels = [
        #     'Zeer goed', 'Goed', 'Matig', 'Ontoereikend', 'Slecht']
        # graph.legend(legend_handles, legend_labels, legend_location=6)

        yticklabels = []
        block_width = (date2num(dt_end) - date2num(dt_start)) / 50

        # Legend
        #graph.margin_right_extra += 90  # Room for legend. See also nens_graph.
        legend_handles = [
            Line2D([], [], color=COLOR_1, lw=10),
            Line2D([], [], color=COLOR_2, lw=10),
            Line2D([], [], color=COLOR_3, lw=10),
            Line2D([], [], color=COLOR_4, lw=10),
            Line2D([], [], color=COLOR_5, lw=10),
            ]
        legend_labels = [
            'slecht',
            'ontoereikend',
            'matig',
            'goed',
            'zeer goed',
            ]
        graph.legend(legend_handles, legend_labels, legend_location=7)

        for index, graph_item in enumerate(graph_items):
            if not graph_item.location:
                graph_item.location = graph_settings['location']

            # Find the corresponding Score.
            score = Score.from_graph_item(graph_item)
            if score.id is None:
                graph_item.label = '(%s)' % graph_item.label

            yticklabels.append(graph_item.label)

            # We want to draw a shadow past the end of the last
            # event. That's why we ignore dt_start.
            try:
                ts = graph_item.time_series(dt_end=dt_end, with_comments=True)
            except:
                logger.exception(
                    'HorizontalBarView crashed on graph_item.time_series of %s' %
                    graph_item)
                ts = {}
            if len(ts) != 1:
                logger.warn('Warning: drawing %d timeseries on a single bar '
                            'HorizontalBarView', len(ts))
            # We assume there is only one timeseries.
            for (loc, par, unit), single_ts in ts.items():
                dates, values, comments, flag_dates, flag_values, flag_comments = (
                    dates_values_comments(single_ts))
                if not dates:
                    logger.warning('Tried to draw empty timeseries %s %s',
                                   loc, par)
                    continue
                block_dates = []
                block_dates_shadow = []
                for date_index in range(len(dates) - 1):
                    dist_to_next = (date2num(dates[date_index + 1]) -
                                    date2num(dates[date_index]))
                    this_block_width = min(block_width, dist_to_next)

                    block_dates.append(
                        (date2num(dates[date_index]), this_block_width))
                    block_dates_shadow.append(
                        (date2num(dates[date_index]), dist_to_next))

                block_dates.append(
                    (date2num(dates[-1]), block_width))
                # Ignoring tzinfo, otherwise we can't compare.
                last_date = max(dt_start.replace(tzinfo=None), dates[-1])
                block_dates_shadow.append(
                    (date2num(last_date),
                     (date2num(dt_end) - date2num(dt_start))))

                a, b, c, d = score.borders
                block_colors = [comment_to_html_color(comment)
                                for comment in comments]

                # Block shadow
                graph.axes.broken_barh(
                    block_dates_shadow, (index - 0.2, 0.4),
                    facecolors=block_colors, edgecolors=block_colors,
                    alpha=0.2)
                # The 'real' block
                graph.axes.broken_barh(
                    block_dates, (index - 0.4, 0.8),
                    facecolors=block_colors, edgecolors='grey')

            # for goal in graph_item.goals.all():
            #     collected_goal_timestamps.update([goal.timestamp, ])

        # For each unique bar goal timestamp, generate a mini
        # graph. The graphs are ordered by timestamp.
        goal_timestamps = [
            datetime.datetime(2015, 1, 1, 0, 0),
            datetime.datetime(2027, 1, 1, 0, 0),
            ]
        subplot_numbers = [312, 313]
        for index, goal_timestamp in enumerate(goal_timestamps):
            axes_goal = graph.figure.add_subplot(subplot_numbers[index])
            axes_goal.set_yticks(range(len(yticklabels)))
            axes_goal.set_yticklabels('')
            axes_goal.set_xticks([0, ])
            axes_goal.set_xticklabels([goal_timestamp.year, ])
            for graph_item_index, graph_item in enumerate(graph_items):
                # TODO: make more efficient; score is retrieved twice
                # in this function.

                score = Score.from_graph_item(graph_item)
                #print 'score: %s' % score
                #print 'doel scores: %s' % str(score.targets)
                #a, b, c, d = score.borders
                goal = score.targets[index]
                if goal is not None:
                    axes_goal.broken_barh(
                        [(-0.5, 1)], (graph_item_index - 0.4, 0.8),
                        facecolors=comment_to_html_color(goal),
                        edgecolors='grey')
                # # 0 or 1 items
                # goals = graph_item.goals.filter(timestamp=goal_timestamp)
                # for goal in goals:
                #     axes_goal.broken_barh(
                #         [(-0.5, 1)], (graph_item_index - 0.4, 0.8),
                #         facecolors=value_to_html_color(goal.value),
                #         edgecolors='grey')
            axes_goal.set_xlim((-0.5, 0.5))
            axes_goal.set_ylim(-0.5, len(yticklabels) - 0.5)

            # Coordinates are related to the graph size - not graph 311
            bar_width_px = 12
            axes_x = float(graph.width -
                           (graph.MARGIN_RIGHT + graph.margin_right_extra) +
                           bar_width_px +
                           2 * bar_width_px * index
                           ) / graph.width
            axes_y = float(graph.MARGIN_BOTTOM +
                      graph.margin_bottom_extra) / graph.height
            axes_width = float(bar_width_px) / graph.width
            axes_height = float(graph.graph_height()) / graph.height
            axes_goal.set_position((axes_x, axes_y,
                                    axes_width, axes_height))

        graph.axes.set_yticks(range(len(yticklabels)))
        graph.axes.set_yticklabels(yticklabels)
        graph.axes.set_xlim(date2num((dt_start, dt_end)))
        graph.axes.set_ylim(-0.5, len(yticklabels) - 0.5)

        # Set the margins, including legend.
        graph.set_margins()

        return graph.png_response(
            response=HttpResponse(content_type='image/png'))
Пример #3
0
    def get(self, request, *args, **kwargs):
        """
        Input:
        - dt_start
        - dt_end
        - width
        - height

        - item={fews_norm_source_slug:.., location_id:..,
          parameter_id:.., module_id:.., type:.., arguments:..}
        - type can be 'line', 'stacked-bar', 'vertical-line',
          'horizontal-line', 'stacked-line' (see README).
        - items are processed in order.
        """
        default_colors = ['green', 'blue', 'yellow', 'magenta', ]

        dt_start, dt_end = self._dt_from_request()
        # Get all graph items from request.
        graph_items, graph_settings = self._graph_items_from_request()
        graph = DateGridGraph(
            width=int(graph_settings['width']),
            height=int(graph_settings['height']))
        if ('y-range-min' not in graph_settings and
            'y-range-max' not in graph_settings):
            graph.axes.set_ymargin(0.1)

        bar_width = GraphView.BAR_WIDTHS[PredefinedGraph.PERIOD_REVERSE[
            graph_settings['aggregation-period']]]
        graph.figure.suptitle(
            graph_settings.get('title', ''),
            x=0.5, y=1,
            horizontalalignment='center', verticalalignment='top')
        # Disappears somewhere, but not so important now...
        # graph.axes.set_xlabel(graph_settings.get('x-label', ''))
        # Can be overruled later by unit_as_y_label
        graph.axes.set_ylabel(graph_settings.get('y-label', ''))

        color_index = 0
        unit_from_graph = '(no unit)'
        ts_stacked_sum = {
            'bar-positive': 0,
            'bar-negative': 0,
            'line-cum': 0,
            'line': 0}
        matplotlib_legend_index = 0

        # To be filled using matplotlib_legend_index
        reversed_legend_items = []

        # Let's draw these graph items.
        for graph_item in graph_items:
            graph_type = graph_item.graph_type

            try:
                if graph_type == GraphItem.GRAPH_TYPE_LINE:
                    ts = cached_time_series_from_graph_item(
                        graph_item, dt_start, dt_end)
                    for (loc, par, unit), single_ts in ts.items():
                        if unit:
                            unit_from_graph = unit
                        matplotlib_legend_index += graph.line_from_single_ts(
                            single_ts, graph_item,
                            default_color=default_colors[color_index],
                            flags=graph_settings['flags'])

                        color_index = (color_index + 1) % len(default_colors)
                elif (graph_type ==
                      GraphItem.GRAPH_TYPE_STACKED_LINE_CUMULATIVE or
                      graph_type == GraphItem.GRAPH_TYPE_STACKED_LINE):
                    ts = cached_time_series_from_graph_item(
                        graph_item, dt_start, dt_end)
                    for (loc, par, unit), single_ts in ts.items():
                        if unit:
                            unit_from_graph = unit
                        if (graph_type == GraphItem.GRAPH_TYPE_STACKED_LINE):
                            current_ts = single_ts
                            stacked_key = 'line'
                        else:
                            current_ts = time_series_cumulative(
                                single_ts, graph_settings['reset-period'])
                            stacked_key = 'line-cum'
                        # cum_ts is bottom_ts + cumulative single_ts
                        # Make last observation carried forward
                        current_ts.is_locf = True
                        ts_stacked_sum[stacked_key] = (
                            current_ts + ts_stacked_sum[stacked_key])
                        added = graph.line_from_single_ts(
                            ts_stacked_sum[stacked_key], graph_item,
                            default_color=default_colors[color_index],
                            flags=False)
                        # Mark these items to be reversed in the legend.
                        reversed_legend_items.extend(
                            range(matplotlib_legend_index,
                                  matplotlib_legend_index + added))
                        matplotlib_legend_index += added
                        color_index = (color_index + 1) % len(default_colors)
                elif graph_type == GraphItem.GRAPH_TYPE_HORIZONTAL_LINE:
                    matplotlib_legend_index += graph.horizontal_line(
                        graph_item.value,
                        graph_item.layout_dict(),
                        default_color=default_colors[color_index])
                    color_index = (color_index + 1) % len(default_colors)
                elif graph_type == GraphItem.GRAPH_TYPE_VERTICAL_LINE:
                    matplotlib_legend_index += graph.vertical_line(
                        graph_item.value,
                        graph_item.layout_dict(),
                        default_color=default_colors[color_index])
                    color_index = (color_index + 1) % len(default_colors)
                elif graph_type == GraphItem.GRAPH_TYPE_STACKED_BAR:
                    ts = cached_time_series_aggregated(
                        graph_item, dt_start, dt_end,
                        aggregation=graph_settings['aggregation'],
                        aggregation_period=graph_settings[
                            'aggregation-period'])
                    if graph_item.value == 'negative':
                        stacked_key = 'bar-negative'
                        polarity = -1
                    else:
                        stacked_key = 'bar-positive'
                        polarity = 1
                    for (loc, par, option), single_ts in ts.items():
                        if option == TIME_SERIES_ALL:
                            # Make sure all timestamps are present.
                            ts_stacked_sum[stacked_key] += single_ts * 0
                            abs_single_ts = polarity * abs(single_ts)
                            added = graph.bar_from_single_ts(
                                abs_single_ts, graph_item, bar_width,
                                default_color=default_colors[color_index],
                                bottom_ts=ts_stacked_sum[stacked_key])
                            # Mark these items to be reversed in the legend.
                            if polarity == 1:
                                reversed_legend_items.extend(
                                    range(matplotlib_legend_index,
                                          matplotlib_legend_index + added))
                            matplotlib_legend_index += added
                            ts_stacked_sum[stacked_key] += abs_single_ts
                            color_index = (color_index + 1) % len(
                                default_colors)
                elif graph_type == GraphItem.GRAPH_TYPE_STACKED_BAR_SIGN:
                    ts = cached_time_series_aggregated(
                        graph_item, dt_start, dt_end,
                        aggregation=graph_settings['aggregation'],
                        aggregation_period=graph_settings[
                            'aggregation-period'])
                    if graph_item.value == 'negative':
                        stacked_key_positive = 'bar-negative'
                        stacked_key_negative = 'bar-positive'
                        polarity = {
                            TIME_SERIES_POSITIVE: -1,
                            TIME_SERIES_NEGATIVE: 1}
                    else:
                        stacked_key_positive = 'bar-positive'
                        stacked_key_negative = 'bar-negative'
                        polarity = {
                            TIME_SERIES_POSITIVE: 1,
                            TIME_SERIES_NEGATIVE: -1}
                    for (loc, par, option), single_ts in ts.items():
                        # Make sure all timestamps are present.
                        if option == TIME_SERIES_POSITIVE:
                            stacked_key = stacked_key_positive
                        elif option == TIME_SERIES_NEGATIVE:
                            stacked_key = stacked_key_negative
                        if (option == TIME_SERIES_POSITIVE or
                            option == TIME_SERIES_NEGATIVE):
                            ts_stacked_sum[stacked_key] += single_ts * 0
                            abs_single_ts = polarity[option] * abs(single_ts)
                            matplotlib_legend_index += graph.bar_from_single_ts(
                                abs_single_ts, graph_item, bar_width,
                                default_color=default_colors[color_index],
                                bottom_ts=ts_stacked_sum[stacked_key])
                            ts_stacked_sum[stacked_key] += abs_single_ts
                            color_index = (color_index + 1) % len(
                                default_colors)
            except:
                # You never know if there is a bug somewhere
                logger.exception("Unknown error while drawing graph item.")

        if graph_settings['legend-location'] >= 0:
            graph.legend(
                legend_location=graph_settings['legend-location'],
                reversed_legend_items=reversed_legend_items,
                remove_duplicates=True)
        if graph_settings.get('unit_as_y_label', False):
            graph.axes.set_ylabel(unit_from_graph)

        graph.axes.set_xlim(date2num((dt_start, dt_end)))

        # Set ylim
        y_min = graph_settings.get('y-range-min', graph.axes.get_ylim()[0])
        y_max = graph_settings.get('y-range-max', graph.axes.get_ylim()[1])
        graph.axes.set_ylim(y_min, y_max)

        # Now line?
        if graph_settings.get('now-line', False):
            today = datetime.datetime.now()
            graph.axes.axvline(today, color='orange', lw=2, ls='--')

        # Set the margins, including legend.
        graph.set_margins()

        response_format = graph_settings['format']
        if response_format == 'csv':
            response = HttpResponse(mimetype='text/csv')
            response['Content-Disposition'] = (
                'attachment; filename="%s.csv"' %
                graph_settings.get('title', 'export'))
            graph.timeseries_csv(response)
            return response
        elif response_format == 'bmp':
            return graph.render(
                response=HttpResponse(content_type='image/bmp'),
                format='bmp')
        elif response_format == 'emf':
            return graph.render(
                response=HttpResponse(content_type='image/x-emf'),
                format='emf')
        elif response_format == 'eps':
            return graph.render(
                response=HttpResponse(content_type='image/x-eps'),
                format='eps')
        # elif response_format == 'jpg':
        #     return graph.render(
        #         response=HttpResponse(content_type='image/jpg'),
        #         format='jpg')
        elif response_format == 'pdf':
            return graph.render(
                response=HttpResponse(content_type='application/pdf'),
                format='pdf')
        # elif response_format == 'png2':
        #     return graph.render(
        #         response=HttpResponse(content_type='image/png'),
        #         format='png2')
        elif response_format == 'ps':
            return graph.render(
                response=HttpResponse(content_type='application/postscript'),
                format='ps')
        elif response_format == 'raw':
            return graph.render(
                response=HttpResponse(content_type='test/plain'),
                format='raw')
        elif response_format == 'rgb':
            return graph.render(
                response=HttpResponse(content_type='image/x-rgb'),
                format='rgb')
        elif response_format == 'rgba':
            return graph.render(
                response=HttpResponse(content_type='image/x-rgba'),
                format='rgba')
        elif response_format == 'svg':
            return graph.render(
                response=HttpResponse(content_type='image/svg+xml'),
                format='svg')
        elif response_format == 'svgz':
            return graph.render(
                response=HttpResponse(content_type='image/svg+xml'),
                format='svgz')
        elif response_format == 'html':
            # Display html table
            return render_to_response(
                'lizard_graph/table.html',
                {'data': graph.timeseries_as_list()})
        elif response_format == 'xls':
            xls = data_as_xls(data=graph.timeseries_as_list())
            response = HttpResponse(xls.read(), mimetype='application/xls')
            response['Content-Disposition'] = ('attachment; '
                                               'filename="export.xls"')
            return response
        elif response_format == 'png_attach':
            response = HttpResponse(mimetype='image/png')
            response['Content-Disposition'] = (
                'attachment; filename="%s.png"' %
                graph_settings.get('title', 'export'))
            graph.render(response=response)
            return response
        else:
            return graph.render(
                response=HttpResponse(content_type='image/png'))