ILinePlotGen.py revision 67527ddea39e858c957997356a16bd8e3819f359
1# $Copyright:
2# ----------------------------------------------------------------
3# This confidential and proprietary software may be used only as
4# authorised by a licensing agreement from ARM Limited
5#  (C) COPYRIGHT 2015 ARM Limited
6#       ALL RIGHTS RESERVED
7# The entire notice above must be reproduced on all authorised
8# copies and copies may only be made to the extent permitted
9# by a licensing agreement from ARM Limited.
10# ----------------------------------------------------------------
11# File:        ILinePlotGen.py
12# ----------------------------------------------------------------
13# $
14#
15"""This module is reponsible for creating a layout
16of plots as a 2D axes and handling corener cases
17and deleting empty plots
18"""
19
20from cr2.plotter import AttrConf
21import uuid
22import json
23import os
24from IPython.display import display, HTML
25
26
27if not AttrConf.PLOTTER_IPYTHON:
28    raise ImportError("No Ipython Environment found")
29
30# Install resources
31from cr2.plotter import Utils
32Utils.iplot_install("ILinePlot")
33
34
35class ILinePlotGen(object):
36
37    """Cols is the number of columns to draw
38       rows are calculated as 1D - 2D transformation
39       the same transformation is used to index the
40       axes array
41    """
42
43    def _add_graph_cell(self, fig_name):
44        """Add a HTML table cell to hold the plot"""
45
46        width = int(self._attr["width"] / self._cols)
47        div_js = """
48            <script>
49            var ilp_req = require.config( {
50
51                paths: {
52                    "dygraph-sync": "/static/plotter_scripts/ILinePlot/synchronizer",
53                    "dygraph": "/static/plotter_scripts/ILinePlot/dygraph-combined",
54                    "ILinePlot": "/static/plotter_scripts/ILinePlot/ILinePlot",
55                },
56
57                shim: {
58                    "dygraph-sync": ["dygraph"],
59                    "ILinePlot": {
60
61                        "deps": ["dygraph-sync", "dygraph" ],
62                        "exports":  "ILinePlot"
63                    }
64                }
65            });
66                ilp_req(["require", "ILinePlot"], function() {
67                ILinePlot.generate('""" + fig_name + """');
68            });
69            </script>
70        """
71
72        cell = '<td style="border-style: hidden;"><div class="ilineplot" id="{0}" style="width: \
73{1}px; height: {2}px;">{3}</div></td>'.format(fig_name,
74                                           width,
75                                           self._attr["height"], div_js)
76
77        self._html.append(cell)
78
79    def _add_legend_cell(self, fig_name):
80        """Add HTML table cell for the legend"""
81
82        width = int(self._attr["width"] / self._cols)
83        legend_div_name = fig_name + "_legend"
84        cell = '<td style="border-style: hidden;"><div style="text-align:right; \
85width: {0}px; height: auto;"; id="{1}"></div></td>'.format(width,
86                                                           legend_div_name)
87
88        self._html.append(cell)
89
90    def _begin_row(self):
91        """Add the opening tag for HTML row"""
92
93        self._html.append("<tr>")
94
95    def _end_row(self):
96        """Add the closing tag for the HTML row"""
97
98        self._html.append("</tr>")
99
100    def _generate_fig_name(self):
101        """Generate a unique figure name"""
102
103        fig_name = "fig_" + uuid.uuid4().hex
104        self._fig_map[self._fig_index] = fig_name
105        self._fig_index += 1
106        return fig_name
107
108    def _init_html(self):
109        """Initialize HTML code for the plots"""
110
111        width = self._attr["width"]
112        table = '<table style="width: {0}px; border-style: hidden;">'.format(
113            width)
114        self._html.append(table)
115
116        for _ in range(self._rows):
117            self._begin_row()
118
119            legend_figs = []
120            for _ in range(self._cols):
121                fig_name = self._generate_fig_name()
122                legend_figs.append(fig_name)
123                self._add_graph_cell(fig_name)
124
125            self._end_row()
126            self._begin_row()
127
128            for l_fig in legend_figs:
129                self._add_legend_cell(l_fig)
130
131            self._end_row()
132
133    def __init__(self, cols, num_plots, **kwargs):
134        """
135            Args:
136                cols (int): Number of plots in a single line
137                num_plots (int): Total Number of Plots
138        """
139
140        self._cols = cols
141        self._attr = kwargs
142        self._html = []
143        self.num_plots = num_plots
144        self._fig_map = {}
145        self._fig_index = 0
146
147        self._single_plot = False
148        if self.num_plots == 0:
149            raise RuntimeError("No plots for the given constraints")
150
151        if self.num_plots < self._cols:
152            self._cols = self.num_plots
153        self._rows = (self.num_plots / self._cols)
154
155        if self.num_plots % self._cols != 0:
156            self._rows += 1
157
158        self._attr["width"] = AttrConf.HTML_WIDTH
159        self._attr["height"] = AttrConf.HTML_HEIGHT
160        self._init_html()
161
162    def add_plot(self, plot_num, data_frame, title=""):
163        """Add a plot to for a corresponding index"""
164
165        fig_name = self._fig_map[plot_num]
166        fig_params = {}
167        fig_params["data"] = json.loads(data_frame.to_json())
168        fig_params["name"] = fig_name
169        fig_params["rangesel"] = False
170        fig_params["logscale"] = False
171        fig_params["title"] = title
172        fig_params["step_plot"] = self._attr["step_plot"]
173        fig_params["fill_graph"] = self._attr["fill"]
174
175        if "group" in self._attr:
176            fig_params["syncGroup"] = self._attr["group"]
177            if "sync_zoom" in self._attr:
178                fig_params["syncZoom"] = self._attr["sync_zoom"]
179            else:
180                fig_params["syncZoom"] = AttrConf.DEFAULT_SYNC_ZOOM
181
182        if "ylim" in self._attr:
183            fig_params["valueRange"] = self._attr["ylim"]
184
185        json_file = os.path.join(AttrConf.PLOTTER_STATIC_DATA_DIR, fig_name + ".json")
186        fh = open(json_file, "w")
187        json.dump(fig_params, fh)
188        fh.close()
189
190    def finish(self):
191        """Called when the Plotting is finished"""
192
193        figs = []
194
195        for fig_idx in self._fig_map.keys():
196            figs.append(self._fig_map[fig_idx])
197
198        display(HTML(self.html()))
199
200    def html(self):
201        """Return the raw HTML text"""
202
203        return "\n".join(self._html)
204