1#!/usr/bin/env python3
  2
  3"""Implementation of the BitHistogram widget."""
  4
  5import collections
  6from datetime import timedelta
  7
  8from django.template.loader import render_to_string
  9
 10import chart.alg.settings
 11from chart.reports.widget import Widget
 12from chart.plotviewer.hardcopy import make_bit_histogram
 13from chart.common.xml import to_html
 14from chart.common import traits
 15from chart.plotviewer.plot_utils import DataPoint
 16from chart.common.path import Path
 17
 18# Force orbital stats if the report duration is over this
 19ORBITAL_STATS_DURATION = timedelta(days=3)
 20
 21DEFAULT_WIDTH = 1000
 22DEFAULT_HEIGHT = 600
 23
 24
 25class HistogramWidget(Widget):
 26    """Render a histogram for a list of data points.
 27    """
 28
 29    name = 'bit-histogram'
 30
 31    thumbnail = 'widgets/bithistogram.png'
 32
 33    options = collections.OrderedDict([
 34            ('title', {'type': 'string',
 35                       'optional': True}),
 36            ('filename', {'type': 'string',
 37                          'optional': True}),
 38            ('field', {
 39                    'type': 'field',
 40                    'optional': True,
 41                    'description': (
 42                        'Timeseries data point to plot. Either a datapoint or an '
 43                        'event+property must be specified')}),
 44            ('width', {'type': 'uint',
 45                       'default': DEFAULT_WIDTH,
 46                       'unit': 'pixels'}),
 47            ('height', {'type': 'uint',
 48                       'default': DEFAULT_HEIGHT,
 49                       'unit': 'pixels'}),
 50            ('label-fontsize', {'type': 'uint',
 51                                'unit': 'pt',
 52                                'default': 10,
 53                                'description': 'Font size for axis labels'}),
 54            ])
 55
 56    document_options = collections.OrderedDict([
 57            ('sid', {'type': 'sid'}),
 58            ('sensing_start', {'type': 'datetime'}),
 59            ('sensing_stop', {'type': 'datetime'})])
 60
 61    def __str__(self):
 62        """String representation of this widget, appears in log file."""
 63
 64        if hasattr(self, 'title') and self.title is not None:
 65            # Note, this function can get called in an exception handler during widget
 66            # construction so don't assume anything is available
 67            return 'BitHistogram({title})'.format(title=self.config.get('title', ''))
 68
 69        else:
 70            return 'BitHistogram'
 71
 72    def pre_html(self, document):
 73        """Insert ourselves into the List of Figures widget."""
 74
 75        c = self.config
 76        # document.figures.append(c['title'])
 77
 78        # Build an automatic title if the user didn't supply one
 79        if 'title' not in c:
 80            c['title'] = 'Bit histogram of {field}'.format(field=c['field'].name)
 81
 82        # Make sure we appear in the LoF widget
 83        document.figures.append(c['title'])
 84
 85    def html(self, document):
 86        """Render ourselves."""
 87
 88        c = self.config
 89        dc = document.config
 90
 91        # If the user didn't supply a filename we deduce one
 92        if 'filename' not in c:
 93            # `anon_counts` gives the number of anonymous images already created
 94            # for this report
 95            if 'bithistogram' not in document.anon_counts:
 96                document.anon_counts['bithistogram'] = 1
 97
 98            else:
 99                document.anon_counts['bithistogram'] += 1
100
101            filename = Path('BITHISTOGRAM_{cc}.png'.format(cc=document.anon_counts['bithistogram']))
102
103        else:
104            filename = Path(c['filename'])
105
106        filename = document.theme.mod_filename(document, filename)
107
108        dp = DataPoint(sid=dc['sid'],
109                       table=c['field'].table,
110                       field=c['field'])
111
112        fig = make_bit_histogram(dc['sid'],
113                                 dc['sensing_start'],
114                                 dc['sensing_stop'],
115                                 dp,
116                                 c['width'],
117                                 c['height'],
118                                 c['title'],
119                                 c['label-fontsize'])
120        fig.savefig(str(filename))
121
122        url = ''
123
124        # Render graph detail page
125        summary_filename = Path(str(filename) + '.html')
126        with summary_filename.open('w') as summary:
127            summary.write(
128                render_to_string(
129                    'widgets/bit-histogram.html',
130                    {'css': chart.alg.settings.REPORT_CSS_BASENAME,
131                     'src': filename,
132                     'datapoint_info': [c['field']],
133                     'config': self.make_html_config(),
134                     'url': url,
135                     'elem': to_html(self.elem),
136                     # why does the next line throw a unicode error here without escaping?
137                     'title': traits.to_htmlstr(c['title'])}))
138
139        document.append_figure(filename=filename,
140                               zoom_filename=filename,
141                               title=c['title'],
142                               width=c['width'],
143                               height=c['height'],
144                               widget=self)