コード例 #1
0
ファイル: ourdygraph.py プロジェクト: CINF/cinfdata
class Plot():
    """ This class is used to generate the dygraph content.

    NOTE: With the version of dygraph used at the time of writing
    version 2 (medio 2012) it was not possible to produce parametric
    plots (for as function of temperature type plots) nor was it
    possible to flip the x axis be reversing the x-axis limits. For
    the latter there is a snippet of code in a comment in the end of
    the file if it is ever made possible."""

    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        # Colors object, will be filled in at new_plot
        self.c = None
        self.reduction = 1

    def new_plot(self, data, plot_info, measurement_count):
        """ Produce all the plot output by calls to the different
        subsection methods
        """
        self.c = Color(data, self.ggs)
        self.measurement_count = sum(measurement_count)
        self._header(self.out, data, plot_info)
        self._data(self.out, data, plot_info)
        self._options(self.out, data, plot_info)
        self._end(self.out, data, plot_info)
        
    def _header(self, out, data, plot_info):
        """ Form the header """
        # Get max points from settings of any, default 10000
        #max_points = 100000
        max_points = 50000
        if self.ggs.get('dygraph_settings') is not None:
            if self.ggs['dygraph_settings'].get('max_points') is not None:
                max_points = int(self.ggs['dygraph_settings']['max_points'])
        # Calculate the data point reduction factor
        self.reduction = (self.measurement_count / max_points) + 1

        if self.reduction > 1:
            mouse_over = "This plot, which was supposed to contain {0} data "\
                "points, has been reduced to only plot every {1} point, to "\
                "reduce the total data size. The current data point limit is "\
                "{2} and it can be changed in your graphsettings.xml"
            mouse_over = mouse_over.format(self.measurement_count,
                                           self.reduction, max_points)
            warning = '<b title=\\"{0}\\">Reduced data set (hover for '\
                'details)</b>'.format(mouse_over)
            out.write('document.getElementById("warning_div").innerHTML='\
                          '\"{0}\";'.format(warning))

        # Write dygraph header
        out.write('g = new Dygraph(\n' +
                  self.tab + 'document.getElementById("graphdiv"),\n') 

    def _data(self, out, data, plot_info):
        """ Determine the type of the plot and call the appropriate
        _data_*** function
        """
        # Generate a random filename for the data
        filename = '../figures/{0}.csv'.format(uuid.uuid4())
        out.write('{0}"{1}",\n'.format(self.tab, filename))
        file_ = open(filename, 'w')
        if self.ggs['default_xscale'] == 'dat':
            self._data_dateplot(file_, data, plot_info)
        else:
            self._data_xyplot(file_, data, plot_info)
        file_.close()

    def _data_dateplot(self, out, data, plot_info):
        """Generate the CSV data for a dateplot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert date in column 0 and y in appropriate column
                    this_line[0] = str(time.strftime(
                            '%Y-%m-%d %H:%M:%S', time.localtime(int(item[0]))
                            ))
                    this_line[n+1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _data_xyplot(self, out, data, plot_info):
        """Generate the CSV data for a XY plot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert x in column 0 and y in appropriate column
                    this_line[0] = str(item[0])
                    this_line[n+1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _options(self, out, data, plot_info):
        """ Form all the options and ask _output_options to print them in a
        javascript friendly way
        """

        def _q(string):
            """ _q for _quote: Utility function to add quotes to strings """
            return '\'{0}\''.format(string)

        # Form labels string
        labels = [self.ggs['xlabel'] if self.ggs.has_key('xlabel') else '']
        for dat in data['left']:
            labels.append(dat['lgs']['legend'])
        r_ylabels=[]
        for dat in data['right']:
            labels.append(dat['lgs']['legend'])
            r_ylabels.append(dat['lgs']['legend'])
        # Overwrite labels if there is no data
        if self.measurement_count == 0:
            labels = ['NO DATA X', 'NO DATA Y']

        # Initiate options variable. A group of options are contained in a list
        # and the options are given as a key value pair in in a dictionary
        # containing only one item
        options = [{'labels': str(labels)}]

        # Add second yaxis configuration
        two_line_y_axis_label = False
        if self.right_yaxis:
            first_label = r_ylabels[0]
            # Add first data set to secondary axis
            # Ex: 'Containment (r)': {axis: {}}
            y2options = [{'logscale': str(self.o['right_logscale']).lower()}]
            if self.o['right_yscale_bounding'] is not None:
                y2options.append(
                    {'valueRange': str(list(self.o['right_yscale_bounding']))}
                    )
            options.append({_q(first_label): [{'axis': y2options}]})
            # Add remaining datasets to secondary axis
            for label in r_ylabels[1:]:
                # Ex: 'IG Buffer (r)': {axis: 'Containment (r)'}
                a = {_q(label): [{'axis': _q(first_label)}]}
                options.append(a)

            # Add the right y label
            if plot_info.has_key('right_ylabel'):
                if plot_info['y_right_label_addition'] == '':
                    options.append({'y2label': _q(plot_info['right_ylabel'])})
                else:
                    two_line_y_axis_label = True
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['right_ylabel'], plot_info['y_right_label_addition'])
                    options.append({'y2label':_q(label)})

        # General options
        options += [{'logscale': str(self.o['left_logscale']).lower()},
                    {'connectSeparatedPoints': 'true'},
                    {'legend': _q('always')},
                    #{'lineWidth': '0'}
                    ]
     
        # Add title
        if plot_info.has_key('title'):
            if self.measurement_count == 0:
                options.append({'title': _q('NO DATA')})
            else:
                options.append({'title': _q(plot_info['title'])})

        # Add the left y label
        if plot_info.has_key('left_ylabel'):
            if plot_info['y_left_label_addition'] == '':
                options.append({'ylabel': _q(plot_info['left_ylabel'])})
            else:
                two_line_y_axis_label = True
                label = '<font size="3">{0}<br />{1}</font>'.format(
                    plot_info['left_ylabel'], plot_info['y_left_label_addition'])
                options.append({'ylabel':_q(label)})

        # Set the proper space for y axis labels
        if two_line_y_axis_label:
            options.append({'yAxisLabelWidth': '100'})
        else:
            options.append({'yAxisLabelWidth': '80'})

        # Determine the labels and add them
        if plot_info.has_key('xlabel'):
            if self.ggs['default_xscale'] != 'dat':
                if plot_info['xlabel_addition'] == '':
                    options.append({'xlabel': _q(plot_info['xlabel'])})
                else:
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['xlabel'], plot_info['xlabel_addition'])
                    options.append({'xlabel':_q(label)})
                    options.append({'xLabelHeight': '30'})

        colors = [self.c.get_color_hex() for dat in data['left'] + data['right']]
        options.append({'colors':str(colors)})
        
        # Zoom, left y-scale
        if self.o['left_yscale_bounding'] is not None:
            options.append({'valueRange':
                                str(list(self.o['left_yscale_bounding']))})
        # X-scale
        if self.o['xscale_bounding'] is not None and\
                self.o['xscale_bounding'][1] > self.o['xscale_bounding'][0]:
            xzoomstring = '[{0}, {1}]'.format(self.o['xscale_bounding'][0],
                                              self.o['xscale_bounding'][1])
            options.append({'dateWindow': xzoomstring})

        grids = [{'drawXGrid': 'true'}, {'drawYGrid': 'false'}]
        # Add modifications from settings file
        if self.ggs.has_key('dygraph_settings'):
            # roller
            if self.ggs['dygraph_settings'].has_key('roll_period'):
                period = self.ggs['dygraph_settings']['roll_period']
                options += [{'showRoller': 'true'}, {'rollPeriod': period}]
            # grids
            if self.ggs['dygraph_settings'].has_key('xgrid'):
                grids[0]['drawXGrid'] = self.ggs['dygraph_settings']['xgrid']
            if self.ggs['dygraph_settings'].has_key('ygrid'):
                grids[1]['drawYGrid'] = self.ggs['dygraph_settings']['ygrid']
            # high light series
            if self.ggs['dygraph_settings'].has_key('series_highlight'):
                if self.ggs['dygraph_settings']['series_highlight'] == 'true':
                    options.append(
                        {'highlightSeriesOpts': [
                                {'strokeWidth': '2'},
                                {'strokeBorderWidth': '1'},
                                {'highlightCircleSize': '5'}
                                ]
                         })
            if self.ggs['dygraph_settings'].has_key('labels_side'):
                if self.ggs['dygraph_settings']['labels_side'] == 'true':
                    sep_new_lines = 'true'
                    if self.ggs['dygraph_settings'].has_key('labels_newline'):
                        sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                    options += [{'labelsDiv': 'document.getElementById("labels")'},
                                {'labelsSeparateLines': sep_new_lines}]
            elif self.ggs['dygraph_settings'].has_key('labels_newline'):
                sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                options += [{'labelsSeparateLines': sep_new_lines}]

        # Disable grids
        options += grids


        self._output_options(out, None, options, 1, last=True)

    def _output_options(self, out, name, options, level, last=False):
        """ Oh boy! I wish I had documented this when I wrote it! KN """
        if name is None:
            out.write(self.tab * level + '{\n')
        else:
            out.write(self.tab * level + name + ': {\n')

        for n, (key, value) in enumerate([op.items()[0] for op in options]):
            if isinstance(value, list):
                if n == len(value)-1:
                    self._output_options(out, key, value, level+1, last=True)
                else:
                    self._output_options(out, key, value, level+1, last=False)
            else:
                if n == len(options)-1:
                    out.write(self.tab*(level+1) + key + ': ' + value + '\n')
                else:
                    out.write(self.tab*(level+1) + key + ': ' + value + ',\n')
                
        if last:
            out.write(self.tab * level + '}\n')
        else:
            out.write(self.tab * level + '},\n')

    def _end(self, out, data, plot_info):
        """ Output last line """
        out.write(');')
コード例 #2
0
class Plot():
    """ This class is used to generate the dygraph content.

    NOTE: With the version of dygraph used at the time of writing
    version 2 (medio 2012) it was not possible to produce parametric
    plots (for as function of temperature type plots) nor was it
    possible to flip the x axis be reversing the x-axis limits. For
    the latter there is a snippet of code in a comment in the end of
    the file if it is ever made possible."""
    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs  # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        # object to give first good color, and then random colors
        self.c = Color()
        self.reduction = 1

    def new_plot(self, data, plot_info, measurement_count):
        """ Produce all the plot output by calls to the different
        subsection methods
        """
        self.measurement_count = sum(measurement_count)
        self._header(self.out, data, plot_info)
        self._data(self.out, data, plot_info)
        self._options(self.out, data, plot_info)
        self._end(self.out, data, plot_info)

    def _header(self, out, data, plot_info):
        """ Form the header """
        # Get max points from settings of any, default 10000
        #max_points = 100000
        max_points = 50000
        if self.ggs.get('dygraph_settings') is not None:
            if self.ggs['dygraph_settings'].get('max_points') is not None:
                max_points = int(self.ggs['dygraph_settings']['max_points'])
        # Calculate the data point reduction factor
        self.reduction = (self.measurement_count / max_points) + 1

        if self.reduction > 1:
            mouse_over = "This plot, which was supposed to contain {0} data "\
                "points, has been reduced to only plot every {1} point, to "\
                "reduce the total data size. The current data point limit is "\
                "{2} and it can be changed in your graphsettings.xml"
            mouse_over = mouse_over.format(self.measurement_count,
                                           self.reduction, max_points)
            warning = '<b title=\\"{0}\\">Reduced data set (hover for '\
                'details)</b>'.format(mouse_over)
            out.write('document.getElementById("warning_div").innerHTML='\
                          '\"{0}\";'.format(warning))

        # Write dygraph header
        out.write('g = new Dygraph(\n' + self.tab +
                  'document.getElementById("graphdiv"),\n')

    def _data(self, out, data, plot_info):
        """ Determine the type of the plot and call the appropriate
        _data_*** function
        """
        # Generate a random filename for the data
        filename = '../figures/{0}.csv'.format(uuid.uuid4())
        out.write('{0}"{1}",\n'.format(self.tab, filename))
        file_ = open(filename, 'w')
        if self.ggs['default_xscale'] == 'dat':
            self._data_dateplot(file_, data, plot_info)
        else:
            self._data_xyplot(file_, data, plot_info)
        file_.close()

    def _data_dateplot(self, out, data, plot_info):
        """Generate the CSV data for a dateplot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert date in column 0 and y in appropriate column
                    this_line[0] = str(
                        time.strftime('%Y-%m-%d %H:%M:%S',
                                      time.localtime(int(item[0]))))
                    this_line[n + 1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _data_xyplot(self, out, data, plot_info):
        """Generate the CSV data for a XY plot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert x in column 0 and y in appropriate column
                    this_line[0] = str(item[0])
                    this_line[n + 1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _options(self, out, data, plot_info):
        """ Form all the options and ask _output_options to print them in a
        javascript friendly way
        """
        def _q(string):
            """ _q for _quote: Utility function to add quotes to strings """
            return '\'{0}\''.format(string)

        # Form labels string
        labels = [self.ggs['xlabel'] if self.ggs.has_key('xlabel') else '']
        for dat in data['left']:
            labels.append(dat['lgs']['legend'])
        r_ylabels = []
        for dat in data['right']:
            labels.append(dat['lgs']['legend'])
            r_ylabels.append(dat['lgs']['legend'])
        # Overwrite labels if there is no data
        if self.measurement_count == 0:
            labels = ['NO DATA X', 'NO DATA Y']

        # Initiate options variable. A group of options are contained in a list
        # and the options are given as a key value pair in in a dictionary
        # containing only one item
        options = [{'labels': str(labels)}]

        # Add second yaxis configuration
        two_line_y_axis_label = False
        if self.right_yaxis:
            first_label = r_ylabels[0]
            # Add first data set to secondary axis
            # Ex: 'Containment (r)': {axis: {}}
            y2options = [{'logscale': str(self.o['right_logscale']).lower()}]
            if self.o['right_yscale_bounding'] is not None:
                y2options.append(
                    {'valueRange': str(list(self.o['right_yscale_bounding']))})
            options.append({_q(first_label): [{'axis': y2options}]})
            # Add remaining datasets to secondary axis
            for label in r_ylabels[1:]:
                # Ex: 'IG Buffer (r)': {axis: 'Containment (r)'}
                a = {_q(label): [{'axis': _q(first_label)}]}
                options.append(a)

            # Add the right y label
            if plot_info.has_key('right_ylabel'):
                if plot_info['y_right_label_addition'] == '':
                    options.append({'y2label': _q(plot_info['right_ylabel'])})
                else:
                    two_line_y_axis_label = True
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['right_ylabel'],
                        plot_info['y_right_label_addition'])
                    options.append({'y2label': _q(label)})

        # General options
        options += [
            {
                'logscale': str(self.o['left_logscale']).lower()
            },
            {
                'connectSeparatedPoints': 'true'
            },
            {
                'legend': _q('always')
            },
            #{'lineWidth': '0'}
        ]

        # Add title
        if plot_info.has_key('title'):
            if self.measurement_count == 0:
                options.append({'title': _q('NO DATA')})
            else:
                options.append({'title': _q(plot_info['title'])})

        # Add the left y label
        if plot_info.has_key('left_ylabel'):
            if plot_info['y_left_label_addition'] == '':
                options.append({'ylabel': _q(plot_info['left_ylabel'])})
            else:
                two_line_y_axis_label = True
                label = '<font size="3">{0}<br />{1}</font>'.format(
                    plot_info['left_ylabel'],
                    plot_info['y_left_label_addition'])
                options.append({'ylabel': _q(label)})

        # Set the proper space for y axis labels
        if two_line_y_axis_label:
            options.append({'yAxisLabelWidth': '100'})
        else:
            options.append({'yAxisLabelWidth': '80'})

        # Determine the labels and add them
        if plot_info.has_key('xlabel'):
            if self.ggs['default_xscale'] != 'dat':
                if plot_info['xlabel_addition'] == '':
                    options.append({'xlabel': _q(plot_info['xlabel'])})
                else:
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['xlabel'], plot_info['xlabel_addition'])
                    options.append({'xlabel': _q(label)})
                    options.append({'xLabelHeight': '30'})

        colors = [
            self.c.get_color_hex() for dat in data['left'] + data['right']
        ]
        options.append({'colors': str(colors)})

        # Zoom, left y-scale
        if self.o['left_yscale_bounding'] is not None:
            options.append(
                {'valueRange': str(list(self.o['left_yscale_bounding']))})
        # X-scale
        if self.o['xscale_bounding'] is not None and\
                self.o['xscale_bounding'][1] > self.o['xscale_bounding'][0]:
            xzoomstring = '[{0}, {1}]'.format(self.o['xscale_bounding'][0],
                                              self.o['xscale_bounding'][1])
            options.append({'dateWindow': xzoomstring})

        grids = [{'drawXGrid': 'true'}, {'drawYGrid': 'false'}]
        # Add modifications from settings file
        if self.ggs.has_key('dygraph_settings'):
            # roller
            if self.ggs['dygraph_settings'].has_key('roll_period'):
                period = self.ggs['dygraph_settings']['roll_period']
                options += [{'showRoller': 'true'}, {'rollPeriod': period}]
            # grids
            if self.ggs['dygraph_settings'].has_key('xgrid'):
                grids[0]['drawXGrid'] = self.ggs['dygraph_settings']['xgrid']
            if self.ggs['dygraph_settings'].has_key('ygrid'):
                grids[1]['drawYGrid'] = self.ggs['dygraph_settings']['ygrid']
            # high light series
            if self.ggs['dygraph_settings'].has_key('series_highlight'):
                if self.ggs['dygraph_settings']['series_highlight'] == 'true':
                    options.append({
                        'highlightSeriesOpts': [{
                            'strokeWidth': '2'
                        }, {
                            'strokeBorderWidth': '1'
                        }, {
                            'highlightCircleSize': '5'
                        }]
                    })
            if self.ggs['dygraph_settings'].has_key('labels_side'):
                if self.ggs['dygraph_settings']['labels_side'] == 'true':
                    sep_new_lines = 'true'
                    if self.ggs['dygraph_settings'].has_key('labels_newline'):
                        sep_new_lines = self.ggs['dygraph_settings'][
                            'labels_newline']
                    options += [{
                        'labelsDiv':
                        'document.getElementById("labels")'
                    }, {
                        'labelsSeparateLines': sep_new_lines
                    }]
            elif self.ggs['dygraph_settings'].has_key('labels_newline'):
                sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                options += [{'labelsSeparateLines': sep_new_lines}]

        # Disable grids
        options += grids

        self._output_options(out, None, options, 1, last=True)

    def _output_options(self, out, name, options, level, last=False):
        """ Oh boy! I wish I had documented this when I wrote it! KN """
        if name is None:
            out.write(self.tab * level + '{\n')
        else:
            out.write(self.tab * level + name + ': {\n')

        for n, (key, value) in enumerate([op.items()[0] for op in options]):
            if isinstance(value, list):
                if n == len(value) - 1:
                    self._output_options(out, key, value, level + 1, last=True)
                else:
                    self._output_options(out,
                                         key,
                                         value,
                                         level + 1,
                                         last=False)
            else:
                if n == len(options) - 1:
                    out.write(self.tab * (level + 1) + key + ': ' + value +
                              '\n')
                else:
                    out.write(self.tab * (level + 1) + key + ': ' + value +
                              ',\n')

        if last:
            out.write(self.tab * level + '}\n')
        else:
            out.write(self.tab * level + '},\n')

    def _end(self, out, data, plot_info):
        """ Output last line """
        out.write(');')
コード例 #3
0
class Plot():
    """ This class is used to generate the dygraph content.

    NOTE: With the version of dygraph used at the time of writing
    version 2 (medio 2012) it was not possible to produce parametric
    plots (for as function of temperature type plots) nor was it
    possible to flip the x axis be reversing the x-axis limits. For
    the latter there is a snippet of code in a comment in the end of
    the file if it is ever made possible."""

    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        self.measurement_count = None
        # Colors object, will be filled in at new_plot
        self.c = None
        self.reduction = 1

    def new_plot(self, data, plot_info, measurement_count):
        """ Produce all the plot output by calls to the different
        subsection methods
        """
        self.c = Color(data, self.ggs)
        self.measurement_count = sum(measurement_count)
        self._header()
        self._data(data)
        self._options(data, plot_info)
        self._end()
        
    def _header(self):
        """ Form the header """
        # Get max points from settings of any, default 10000
        #max_points = 100000
        max_points = 50000
        if self.ggs.get('dygraph_settings') is not None:
            if self.ggs['dygraph_settings'].get('max_points') is not None:
                max_points = int(self.ggs['dygraph_settings']['max_points'])
        # Calculate the data point reduction factor
        self.reduction = (self.measurement_count / max_points) + 1

        if self.reduction > 1:
            mouse_over = "This plot, which was supposed to contain {0} data "\
                "points, has been reduced to only plot every {1} point, to "\
                "reduce the total data size. The current data point limit is "\
                "{2} and it can be changed in your graphsettings.xml"
            mouse_over = mouse_over.format(self.measurement_count,
                                           self.reduction, max_points)
            warning = '<b title=\\"{0}\\">Reduced data set (hover for '\
                'details)</b>'.format(mouse_over)
            self.out.write('document.getElementById("warning_div").innerHTML='\
                               '\"{0}\";'.format(warning))

        # Write dygraph header
        self.out.write('g = new Dygraph(\n' +
                       self.tab + 'document.getElementById("graphdiv"),\n') 

    def _data(self, data):
        """ Determine the type of the plot and call the appropriate
        _data_*** function
        """
        # Generate a random filename for the data
        filename = '../figures/{0}.csv'.format(uuid.uuid4())
        self.out.write('{0}"{1}",\n'.format(self.tab, filename))
        file_ = open(filename, 'w')
        if self.ggs['default_xscale'] == 'dat':
            self._data_dateplot(file_, data)
        else:
            self._data_xyplot(file_, data)
        file_.close()

    def _data_dateplot(self, out, data):
        """Generate the CSV data for a dateplot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert date in column 0 and y in appropriate column
                    this_line[0] = str(time.strftime(
                            '%Y-%m-%d %H:%M:%S', time.localtime(int(item[0]))
                            ))
                    this_line[n+1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _data_xyplot(self, out, data):
        """Generate the CSV data for a XY plot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert x in column 0 and y in appropriate column
                    this_line[0] = str(item[0])
                    this_line[n+1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _options(self, data, plot_info):
        """Form all the options and output as JSON"""
        # Form labels string
        labels = [self.ggs['xlabel'] if self.ggs.has_key('xlabel') else '']
        for dat in data['left'] + data['right']:
            labels.append(dat['lgs']['legend'])
        # Overwrite labels if there is no data
        if self.measurement_count == 0:
            labels = ['NO DATA X', 'NO DATA Y']

        # Initiate options variable.
        options = {
            'labels': labels,
            'connectSeparatedPoints': True,
            'legend': 'always',
            }

        # FIXME WORK-AROUND: There is a bug in the new dygraphs, so
        # that if logscale is to be used on any of the axis, it must
        # be set to true as a top level option as well as in the axis
        # specific options:
        # https://github.com/danvk/dygraphs/issues/867
        if self.o['left_logscale'] or self.o['right_logscale']:
            options['logscale'] = True

        # Form the series specific options. Looks like:
        #"series": {
        #    "M4": {
        #        "color": "#63650b", 
        #        "axis": "y"
        #    }, 
        #    "Gas flow 1 [He] (r)": {
        #        "color": "#3b3914", 
        #        "axis": "y2"
        #    }, 
        #}
        series = {}
        for side, axis in (('left', 'y'), ('right', 'y2')):
            for dat in data[side]:
                series[dat['lgs']['legend']] = {
                    'axis': axis,
                    'color': self.c.get_color_hex(),
                }
        options['series'] = series

        # Add axes options
        self._options_axes(options, plot_info)

        # Add title, and axis labels
        self._options_title_axes_labels(options, plot_info)

        # X-scale
        if self.o['xscale_bounding'] is not None and\
                self.o['xscale_bounding'][1] > self.o['xscale_bounding'][0]:
            options['dateWindow'] = self.o['xscale_bounding']

        replacements = self._options_from_graphsettings(options)

        # Generate options, replace function placeholder and write out
        options_text = json.dumps(options, indent=4) + '\n'
        for placeholder, replacement in replacements.items():
            options_text = options_text.replace(placeholder, replacement)
        self.out.write(options_text)


    def _options_axes(self, options, plot_info):
        """Form the axes options, will modify options variable"""
        axes = {
            'x': {'drawGrid': True},
            'y': {
                'logscale': self.o['left_logscale'],
                'drawGrid': False,
                },
            }
        # Zoom, left y-scale
        if self.o['left_yscale_bounding'] is not None:
            axes['y']['valueRange'] = self.o['left_yscale_bounding']

        # Add second yaxis configuration
        if self.right_yaxis:
            axes['y2'] = {
                'logscale': self.o['right_logscale'],
                'drawGrid': False,
                }
            if self.o['right_yscale_bounding'] is not None:
                axes['y2']['valueRange'] = self.o['right_yscale_bounding']

            # Add the right y label
            if plot_info.has_key('right_ylabel'):
                if plot_info['y_right_label_addition'] == '':
                    axes['y2']['axisLabelWidth'] = 80
                    y2label = plot_info['right_ylabel']
                else:
                    axes['y2']['axisLabelWidth'] = 100
                    y2label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['right_ylabel'], 
                        plot_info['y_right_label_addition'],
                        )
                options['y2label'] = y2label

        options['axes'] = axes

    def _options_title_axes_labels(self, options, plot_info):
        """Add title and axis labels"""
        # Add title
        if plot_info.has_key('title'):
            if self.measurement_count == 0:
                options['title'] = 'NO DATA'
            else:
                options['title'] = plot_info['title']

        axes = options['axes']
        # Add the left y label
        if plot_info.has_key('left_ylabel'):
            if plot_info['y_left_label_addition'] == '':
                axes['y']['axisLabelWidth'] = 80
                ylabel = plot_info['left_ylabel']
            else:
                axes['y']['axisLabelWidth'] = 100
                ylabel = '<font size="3">{0}<br />{1}</font>'.format(
                    plot_info['left_ylabel'],
                    plot_info['y_left_label_addition'],
                    )
            options['ylabel'] = ylabel

        # Determine the labels and add them
        if plot_info.has_key('xlabel'):
            if self.ggs['default_xscale'] != 'dat':
                if plot_info['xlabel_addition'] == '':
                    options['xlabel'] = plot_info['xlabel']
                else:
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['xlabel'],
                        plot_info['xlabel_addition'],
                        )
                    options['xlabel'] = label
                    options.append({'xLabelHeight': '30'})

    def _options_from_graphsettings(self, options):
        """Add options from graphsettings"""
        replacements = {}
        # Add modifications from settings file
        if self.ggs.has_key('dygraph_settings'):
            # roller
            if self.ggs['dygraph_settings'].has_key('roll_period'):
                period = int(self.ggs['dygraph_settings']['roll_period'])
                options.update({'showRoller': 'true', 'rollPeriod': period})

            # grids
            for axis_name in 'xy':
                grid_settings_str = self.ggs['dygraph_settings']\
                    .get(axis_name + 'grid')
                if grid_settings_str is not None:
                    grid_settings = bool_str(grid_settings_str)
                    options['axes'][axis_name]['drawGrid'] = grid_settings

            # high light series
            if self.ggs['dygraph_settings'].has_key('series_highlight'):
                if self.ggs['dygraph_settings']['series_highlight'] == 'true':
                    options['highlightSeriesOpts'] = {
                        'strokeWidth': 2,
                        'strokeBorderWidth': 1,
                        'highlightCircleSize': 5,
                        }

            # Labels modifications
            labels_options = {}
            if self.ggs['dygraph_settings'].has_key('labels_side'):
                if self.ggs['dygraph_settings']['labels_side'] == 'true':
                    labels_options.update({
                            'labelsSeparateLines': True,
                            # func1 is just a placeholder that will
                            # be replaced by:
                            # document.getElementById("labels")
                            # in the serialized json, because json
                            # cannot serialize functions
                            'labelsDiv': '#func1#',
                            })
                    replacements['"#func1#"'] = \
                        'document.getElementById("labels")'

            sep_new_lines_str = self.ggs['dygraph_settings']\
                .get('labels_newline')
            if sep_new_lines_str is not None:
                labels_options['labelsSeparateLines'] = \
                    bool_str(sep_new_lines_str)
            options.update(labels_options)

        return replacements

    def _end(self):
        """ Output last line """
        self.out.write(');')
コード例 #4
0
class Plot():
    """ This class is used to generate the dygraph content.

    NOTE: With the version of dygraph used at the time of writing
    version 2 (medio 2012) it was not possible to produce parametric
    plots (for as function of temperature type plots) nor was it
    possible to flip the x axis be reversing the x-axis limits. For
    the latter there is a snippet of code in a comment in the end of
    the file if it is ever made possible."""

    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        # object to give first good color, and then random colors
        self.c = Color()

    def new_plot(self, data, plot_info, measurement_count):
        """ Produce all the plot output by calls to the different
        subsection methods
        """
        self.measurement_count = sum(measurement_count)
        self._header(self.out, data, plot_info)
        self._data(self.out, data, plot_info)
        self._options(self.out, data, plot_info)
        self._end(self.out, data, plot_info)
        
    def _header(self, out, data, plot_info):
        """ Form the header """
        out.write('g = new Dygraph(\n' +
                  self.tab + 'document.getElementById("graphdiv"),\n') 

    def _data(self, out, data, plot_info):
        """ Determine the type of the plot and call the appropriate
        _data_*** function
        """
        if self.ggs['default_xscale'] == 'dat':
            self._data_dateplot(out, data, plot_info)
        else:
            self._data_xyplot(out, data, plot_info)

    def _data_dateplot(self, out, data, plot_info):
        plot_n = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        last_line = '\n' + self.tab + '//DATA\n'
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_n + 1)
            for item in dat['data']:
                out.write(last_line)
                this_line[0] = str(time.strftime('%Y-%m-%d %H:%M:%S',
                                                 time.localtime(int(item[0]))))
                this_line[n+1] = str(item[1])
                last_line = self.tab + '"' + ','.join(this_line) + '\\n" +\n'
        
        if self.measurement_count == 0:
            out.write(last_line)
            this_line = [str(42), str(42)]
            last_line = self.tab + '"' + ','.join(this_line) + '\\n" +\n'
        
        out.write(last_line.rstrip(' +\n') + ',\n\n')

    def _data_xyplot(self, out, data, plot_info):
        plot_n = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        last_line = '\n' + self.tab + '//DATA\n'
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_n + 1)
            for item in dat['data']:
                out.write(last_line)
                this_line[0] = str(item[0])
                this_line[n+1] = str(item[1])
                last_line = self.tab + '"' + ','.join(this_line) + '\\n" +\n'

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write(last_line)
            this_line = [str(42), str(42)]
            last_line = self.tab + '"' + ','.join(this_line) + '\\n" +\n'
        
        out.write(last_line.rstrip(' +\n') + ',\n\n')

    def _options(self, out, data, plot_info):
        """ Form all the options and ask _output_options to print them in a
        javascript friendly way
        """

        def _q(string):
            """ _q for _quote: Utility function to add quotes to strings """
            return '\'{0}\''.format(string)

        # Form labels string
        labels = [self.ggs['xlabel'] if self.ggs.has_key('xlabel') else '']
        for dat in data['left']:
            labels.append(dat['lgs']['legend'])
        r_ylabels=[]
        for dat in data['right']:
            labels.append(dat['lgs']['legend'])
            r_ylabels.append(dat['lgs']['legend'])
        # Overwrite labels if there is no data
        if self.measurement_count == 0:
            labels = ['NO DATA X', 'NO DATA Y']

        # Initiate options variable. A group of options are contained in a list
        # and the options are given as a key value pair in in a dictionary
        # containing only one item
        options = [{'labels': str(labels)}]

        # Add second yaxis configuration
        two_line_y_axis_label = False
        if self.right_yaxis:
            first_label = r_ylabels[0]
            # Add first data set to secondary axis
            # Ex: 'Containment (r)': {axis: {}}
            y2options = [{'logscale': str(self.o['right_logscale']).lower()}]
            if self.o['right_yscale_bounding'] is not None:
                y2options.append(
                    {'valueRange': str(list(self.o['right_yscale_bounding']))}
                    )
            options.append({_q(first_label): [{'axis': y2options}]})
            # Add remaining datasets to secondary axis
            for label in r_ylabels[1:]:
                # Ex: 'IG Buffer (r)': {axis: 'Containment (r)'}
                a = {_q(label): [{'axis': _q(first_label)}]}
                options.append(a)

            # Add the right y label
            if plot_info.has_key('right_ylabel'):
                if plot_info['y_right_label_addition'] == '':
                    options.append({'y2label': _q(plot_info['right_ylabel'])})
                else:
                    two_line_y_axis_label = True
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['right_ylabel'], plot_info['y_right_label_addition'])
                    options.append({'y2label':_q(label)})

        # General options
        options += [{'logscale': str(self.o['left_logscale']).lower()},
                    {'connectSeparatedPoints': 'true'},
                    {'legend': _q('always')},
                    #{'lineWidth': '0'}
                    ]
     
        # Add title
        if plot_info.has_key('title'):
            if self.measurement_count == 0:
                options.append({'title': _q('NO DATA')})
            else:
                options.append({'title': _q(plot_info['title'])})

        # Add the left y label
        if plot_info.has_key('left_ylabel'):
            if plot_info['y_left_label_addition'] == '':
                options.append({'ylabel': _q(plot_info['left_ylabel'])})
            else:
                two_line_y_axis_label = True
                label = '<font size="3">{0}<br />{1}</font>'.format(
                    plot_info['left_ylabel'], plot_info['y_left_label_addition'])
                options.append({'ylabel':_q(label)})

        # Set the proper space for y axis labels
        if two_line_y_axis_label:
            options.append({'yAxisLabelWidth': '100'})
        else:
            options.append({'yAxisLabelWidth': '80'})

        # Determine the labels and add them
        if plot_info.has_key('xlabel'):
            if self.ggs['default_xscale'] != 'dat':
                if plot_info['xlabel_addition'] == '':
                    options.append({'xlabel': _q(plot_info['xlabel'])})
                else:
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['xlabel'], plot_info['xlabel_addition'])
                    options.append({'xlabel':_q(label)})
                    options.append({'xLabelHeight': '30'})

        colors = [self.c.get_color_hex() for dat in data['left'] + data['right']]
        options.append({'colors':str(colors)})
        
        # Zoom
        if self.o['left_yscale_bounding'] is not None:
            options.append({'valueRange':
                                str(list(self.o['left_yscale_bounding']))})

        grids = [{'drawXGrid': 'true'}, {'drawYGrid': 'false'}]
        # Add modifications from settings file
        if self.ggs.has_key('dygraph_settings'):
            # roller
            if self.ggs['dygraph_settings'].has_key('roll_period'):
                period = self.ggs['dygraph_settings']['roll_period']
                options += [{'showRoller': 'true'}, {'rollPeriod': period}]
            # grids
            if self.ggs['dygraph_settings'].has_key('xgrid'):
                grids[0]['drawXGrid'] = self.ggs['dygraph_settings']['xgrid']
            if self.ggs['dygraph_settings'].has_key('ygrid'):
                grids[1]['drawYGrid'] = self.ggs['dygraph_settings']['ygrid']
            # high light series
            if self.ggs['dygraph_settings'].has_key('series_highlight'):
                if self.ggs['dygraph_settings']['series_highlight'] == 'true':
                    options.append(
                        {'highlightSeriesOpts': [
                                {'strokeWidth': '2'},
                                {'strokeBorderWidth': '1'},
                                {'highlightCircleSize': '5'}
                                ]
                         })
            if self.ggs['dygraph_settings'].has_key('labels_side'):
                if self.ggs['dygraph_settings']['labels_side'] == 'true':
                    sep_new_lines = 'true'
                    if self.ggs['dygraph_settings'].has_key('labels_newline'):
                        sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                    options += [{'labelsDiv': 'document.getElementById("labels")'},
                                {'labelsSeparateLines': sep_new_lines}]
            elif self.ggs['dygraph_settings'].has_key('labels_newline'):
                sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                options += [{'labelsSeparateLines': sep_new_lines}]

        # Disable grids
        options += grids


        self._output_options(out, None, options, 1, last=True)

    def _output_options(self, out, name, options, level, last=False):
        """ Oh boy! I wish I had documented this when I wrote it! KN """
        if name is None:
            out.write(self.tab * level + '{\n')
        else:
            out.write(self.tab * level + name + ': {\n')

        for n, (key, value) in enumerate([op.items()[0] for op in options]):
            if isinstance(value, list):
                if n == len(value)-1:
                    self._output_options(out, key, value, level+1, last=True)
                else:
                    self._output_options(out, key, value, level+1, last=False)
            else:
                if n == len(options)-1:
                    out.write(self.tab*(level+1) + key + ': ' + value + '\n')
                else:
                    out.write(self.tab*(level+1) + key + ': ' + value + ',\n')
                
        if last:
            out.write(self.tab * level + '}\n')
        else:
            out.write(self.tab * level + '},\n')

    def _end(self, out, data, plot_info):
        """ Output last line """
        out.write(');')