• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

docinfosci / canvasxpress-python / 5f9854e2-3ace-4db8-8620-070e901a70eb

04 Apr 2025 03:41AM UTC coverage: 80.946% (+6.1%) from 74.814%
5f9854e2-3ace-4db8-8620-070e901a70eb

push

circleci

web-flow
JS local sources; Better Jupyter; Top level configs; R/JS attribute mapping

1.  Support local / custom CanvasXpress JS and CSS sources.
2.  Support top-level configuration declarations.
3.  Support mapping R/JS configuration key names to Pythonic editions.  For example, renderTo --> render_to.
4.  Improved Jupyter Notebook detection and pre-loading of CanvasXpress JS and CSS.

93 of 117 new or added lines in 10 files covered. (79.49%)

7 existing lines in 3 files now uncovered.

1933 of 2388 relevant lines covered (80.95%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/canvasxpress/render/rstudio.py
1
import uuid
×
2
from pathlib import Path
×
3
from typing import Any, Union, List
×
4

5
import htmlmin
×
6
from IPython.display import HTML
×
7
from bs4 import BeautifulSoup
×
8

9
from canvasxpress.canvas import CanvasXpress
×
10
from canvasxpress.render.base import CXRenderable
×
11

12
_cx_intermixed_header = """
×
13
<html>
14
    <meta charset="UTF-8">
15
    <link 
16
        href='@css_url@' 
17
        rel='stylesheet' 
18
        type='text/css'
19
    />
20
    <script 
21
        src='@js_url@' 
22
        type='text/javascript'>
23
    </script>
24
</html>
25
"""
26

27
_cx_js_intermixed_template = """
×
28
<script type="text/javascript">
29
    @code@
30
</script>
31
"""
32

33
_cx_html_intermixed_template = """
×
34
<html>
35
    @canvasxpress_license@
36
    @canvases@
37
    @js_functions@
38
</html>
39
"""
40

41

42
class CXHtml(CXRenderable):
×
43
    """
44
    CXHtml is a `CXRenderable` that renders `CanvasXpress` objects into
45
    HTML Snippets suitable for use in RStudio
46
    """
47

48
    def __init__(self, *cx: Union[List[CanvasXpress], CanvasXpress, None]):
×
49
        """
50
        Initializes a new `CXHtml` object.
51
        :praram cx: `Union[List[CanvasXpress], CanvasXpress, None], ...`
52
            The `CanvasXpress` object(s) to be tracked.  See the `canvas`
53
            property, except that on initialization cx can be `None`.
54
            Multiple CanvasXpress objects are supported provided that
55
            they have distinct `render_to` targets.
56
        """
57
        super().__init__(*cx)
×
58

59
    def display_canvasxpress_header(self):
×
NEW
60
        css_url = CanvasXpress.css_library_url()
×
NEW
61
        js_url = CanvasXpress.js_library_url()
×
62

63
        header_html_text = _cx_intermixed_header.replace("@css_url@", css_url).replace(
×
64
            "@js_url@", js_url
65
        )
66

67
        return header_html_text
×
68

69
    def display_debug_code(self, code: str):
×
70
        minified_code = htmlmin.Minifier().minify(code)
×
71
        pretty_code = BeautifulSoup(minified_code, "html.parser").prettify()
×
72
        return pretty_code
×
73

74
    def get_chart_display_code(self, columns: int) -> str:
×
75
        render_targets = list()
×
76

77
        if self.canvas is None:
×
78
            pass
×
79

80
        elif isinstance(self.canvas, CanvasXpress):
×
81
            render_targets.append(self.canvas)
×
82

83
        else:
84
            render_targets.extend(self.canvas)
×
85

86
        used_render_targets = list()
×
87
        for target in render_targets:
×
88
            original_render_target = target.render_to
×
89
            if original_render_target in used_render_targets:
×
90
                target.render_to = (
×
91
                    original_render_target + "_" + str(uuid.uuid4()).replace("-", "_")
92
                )
93

94
            used_render_targets.append(target.render_to)
×
95

96
        render_targets.reverse()
×
97

98
        html_parts = [target.render_to_html_parts() for target in render_targets]
×
99

100
        canvases = [part["cx_canvas"] for part in html_parts]
×
101
        if len(canvases) < columns:
×
102
            columns = len(canvases)
×
103

104
        functions = [part["cx_js"] for part in html_parts]
×
105

106
        cx_license = ""
×
107
        for part in html_parts:
×
108
            if part.get("cx_license"):
×
109
                cx_license = part["cx_license"]
×
110
                break
×
111

112
        canvas_table = "<table>"
×
113
        column_index = 0
×
114
        for chart_index in range(len(canvases)):
×
115
            if column_index == 0:
×
116
                canvas_table += "<tr>"
×
117

118
            canvas_table += '<td style="padding: 5px"><div>'
×
119
            canvas_table += canvases[chart_index]
×
120
            canvas_table += "</div></td>"
×
121

122
            column_index += 1
×
123
            if column_index == columns:
×
124
                column_index = 0
×
125
                canvas_table += "</tr>"
×
126

127
        canvas_table += "</table>"
×
128

129
        js_functions = "\n".join(
×
130
            [_cx_js_intermixed_template.replace("@code@", fx) for fx in functions]
131
        )
132
        html_text = (
×
133
            _cx_html_intermixed_template.replace("@canvases@", canvas_table)
134
            .replace("@canvasxpress_license@", cx_license)
135
            .replace("@js_functions@", js_functions)
136
        )
137

138
        return html_text
×
139

140
    def display_charts(self, code: str, output_file: str):
×
141
        try:
×
142
            if output_file is not None:
×
143
                file_path_candidate = str(output_file)
×
144
                file_path = Path(file_path_candidate)
×
145
                if file_path.is_dir():
×
146
                    file_path = file_path.joinpath(f"cx_{str(uuid.uuid4())}.html")
×
147

148
                with open(str(file_path), "w") as render_file:
×
149
                    render_file.write(code)
×
150

151
        except Exception as e:
×
152
            raise RuntimeError(f"Cannot create output file: {e}")
×
153

154
        return code
×
155

156
    def render(self, **kwargs: Any):
×
157
        """
158
        Renders the associated CanvasXpress object appropriate for display in
159
        a RStudio environment.  Charts cannot have the same name, so render_to
160
        will be updated with a uuid for each conflicting chart.
161
        :param kwargs: `Any`
162
            * Supports `columns` for any positive `int` of `1` or greater, with a
163
              default value of `1`.  Values less that `1` are ignored.  `columns`
164
              indicates how many charts should be rendered horizontally if more
165
              than one chart is being tracked.
166
            * Supports `output_file` as a string for a path at which the output
167
              should be saved.  If a file exists at the specified path then
168
              it will be overwritten.
169
            * Supports `debug` for displaying the output source.  True indicates
170
              that the HTML code shall be displayed prior to the parsed output.
171
              Default is False.
172
        """
173
        debug_output_arg = kwargs.get("debug")
×
174
        debug_output = bool(debug_output_arg) if debug_output_arg is not None else False
×
175

176
        columns_arg = int(kwargs.get("columns", 1))
×
177
        columns = columns_arg if columns_arg > 0 else 1
×
178

179
        output_file_arg = kwargs.get("output_file")
×
180
        output_file = (
×
181
            output_file_arg
182
            if output_file_arg is not None and isinstance(output_file_arg, str)
183
            else None
184
        )
185

186
        code = self.get_chart_display_code(columns)
×
187

188
        try:
×
189
            header = self.display_canvasxpress_header()
×
190
            body = self.display_charts(code, output_file)
×
191
            if debug_output:
×
192
                self.display_debug_code(code)
×
193

194
            return HTML(header + body)
×
195

196
        except Exception as e:
×
197
            raise RuntimeError(f"Cannot create output cell: {e}")
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc