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

sequana / sequana / 16568275773

28 Jul 2025 11:51AM UTC coverage: 69.115% (-0.6%) from 69.722%
16568275773

push

github

web-flow
Merge pull request #870 from cokelaer/dev

add threshold parameter in G4 module

1 of 2 new or added lines in 1 file covered. (50.0%)

725 existing lines in 17 files now uncovered.

14389 of 20819 relevant lines covered (69.11%)

2.07 hits per line

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

93.55
/sequana/utils/datatables_js.py
1
#
2
#  This file is part of Sequana software
3
#
4
#  Copyright (c) 2016 - Sequana Development Team
5
#
6
#  File author(s):
7
#      Dimitri Desvillechabrol <dimitri.desvillechabrol@pasteur.fr>,
8
#          <d.desvillechabrol@gmail.com>
9
#
10
#  Distributed under the terms of the 3-clause BSD license.
11
#  The full license is in the LICENSE file, distributed with this software.
12
#
13
#  website: https://github.com/sequana/sequana
14
#  documentation: http://sequana.readthedocs.io
15
#
16
##############################################################################
17
""" Utilities to create a Jquery DataTable for your HTML file.
3✔
18

19

20
.. autosummary::
21

22
    DataTableFunction
23
    DataTable
24

25

26
"""
27
from collections import OrderedDict
3✔
28

29
import colorlog
3✔
30

31
logger = colorlog.getLogger(__name__)
3✔
32

33
from sequana.lazy import pandas as pd
3✔
34

35

36
class DataTableFunction(object):
3✔
37
    """Class that contains Jquery DataTables function and options.
38

39
    Example:
40

41
    ::
42

43
        import pandas as pd
44
        from sequana.utils.datatables_js import DataTableFunction
45

46
        df = pandas.read_csv('data.csv')
47
        datatable_js = DataTableFunction(df, 'data')
48
        datatable_js.datatable_options = {'pageLength': 15,
49
                                          'dom': 'Bfrtip',
50
                                          'buttons': ['copy', 'csv']}
51
        js = datatable_js.create_javascript_function()
52
        html_datatables = [DataTable(df, "data_{0}".format(i), datatable_js)
53
                           for i, df in enumerate(df_list)]
54

55
    Here, the datatable_options dictionary is used to fine tune the appearance
56
    of the table.
57

58
    .. note::  DataTables add a number of elements around the table to control
59
        the table or show additional information about it. There are controlled
60
        by the order in the document (**DOM**) defined as a string made of
61
        letters, each of them having a precise meaning. The order of the letter
62
        is important. For instance if **B** is first, the buttons are put before
63
        the table. If **B** is at the end, it is shown below the table.
64
        Here are some of the valid letters and their meaning:
65

66
        - **B**: add the Buttons (copy/csv)
67
        - **i**: add *showing 1 to N of M entries*
68
        - **f**: add a search bar (**f** filtering)
69
        - **r**: processing display element
70
        - **t**: the table itself
71
        - **p**: pagination control
72

73
        Each option can be specified multiple times (with the exception of the
74
        table itself).
75

76
    .. note:: other useful options are:
77

78
        - pageLength: 15
79
        - scrollX: "true"
80
        - paging: 15
81
        - buttons: ['copy', 'csv']
82

83
        Note that buttons can also be excel, pdf, print, ...
84

85

86
    All options of datatable:
87
        https://datatables.net/reference/option/
88
    """
89

90
    def __init__(self, df, html_id, index=False):
3✔
91
        """.. rubric:: contructor
92

93
        :param df: data frame.
94
        :param str html_id: the ID used in the HTML file.
95
        """
96
        self.index = index
3✔
97
        # Mars 2022. added 'id' in front of the id to avoid issue with is
98
        # starting with a number, which is not allowed in HTML
99
        self._html_id = f"id_{html_id}"
3✔
100
        self._datatable_options = dict()
3✔
101
        self._datatable_columns = self._set_datatable_columns(df)
3✔
102

103
    @property
3✔
104
    def html_id(self):
3✔
105
        """Get the html_id, which cannot be set by the user after the
106
        instanciation of the class.
107
        """
108
        return self._html_id
3✔
109

110
    @property
3✔
111
    def datatable_options(self):
3✔
112
        """Get, set or delete the DataTable options. Setter takes a dict as
113
        parameter with the desired options and updates the current dictionary.
114

115
        Example::
116

117
            datatable = DataTableFunction("tab")
118
            datatable.datatable_options = {'dom': 'Bfrtip',
119
                                           'buttons': ['copy', 'csv']}
120

121
        source: https://datatables.net/reference/option/
122
        """
123
        return self._datatable_options
3✔
124

125
    @datatable_options.setter
3✔
126
    def datatable_options(self, d):
3✔
127
        try:
3✔
128
            d["buttons"] = self._add_export_visible(d["buttons"])
3✔
129
        except KeyError:
3✔
130
            pass
3✔
131
        self._datatable_options.update(d)
3✔
132

133
    def _add_export_visible(self, buttons):
3✔
134
        """Add option to disable the exporting of hidden columns"""
135
        try:
3✔
136
            for b in buttons:
3✔
137
                b.update({"exportOptions": {"columns": ":visible"}})
3✔
UNCOV
138
                b.update({"filename": "data"})
×
139
        except AttributeError:
3✔
140
            buttons = [{"extend": b, "filename": "data", "exportOptions": {"columns": ":visible"}} for b in buttons]
3✔
141
        return buttons
3✔
142

143
    @datatable_options.deleter
3✔
144
    def datatable_options(self):
3✔
UNCOV
145
        self._datatable_options = dict()
×
146

147
    @property
3✔
148
    def datatable_columns(self):
3✔
149
        """Get datatable_columns dictionary. It is automatically set from the
150
        dataframe you want to plot.
151
        """
152
        return self._datatable_columns
3✔
153

154
    def _set_datatable_columns(self, df):
3✔
155
        """Fill :attr:`DataTableFunction.datatable_columns` with header of
156
        :param:`DataTableFunction.df`.
157
        """
158
        if isinstance(df, pd.Series):  # pragma: no cover
159
            return {}
160

161
        if self.index is True:
3✔
162
            columns = [""] + list(df.columns)
3✔
163
        else:
164
            columns = list(df.columns)
3✔
165
        column_dict = OrderedDict((name, dict()) for name in columns)
3✔
166
        return column_dict
3✔
167

168
    def create_javascript_function(self):
3✔
169
        """Return javascript to create the DataTable."""
170

171
        js_function = """
3✔
172
<script async type="text/javascript">
173
    function parseCsv_{0}(csv, id) {{
174
        Papa.parse(csv, {{
175
            comments: '#',
176
            delimiter: ',',
177
            header: true,
178
            dynamicTyping: true,
179
            error: function(reason) {{
180
                console.log(reason);
181
            }},
182
            complete: function(results) {{
183
                {1}
184
            }}
185
        }});
186
    }};
187
</script>
188
"""
189
        return js_function.format(self.html_id, self._create_datatable_option())
3✔
190

191
    def _create_datatable_option(self):
3✔
192
        """Return DataTable options."""
193
        self.datatable_options["columns"] = self._create_columns_option()
3✔
194
        js = self._dict_to_string(self.datatable_options)
3✔
195
        js = "$(id).DataTable({{{0},data: results.data}});".format(js)
3✔
196
        return js
3✔
197

198
    def _create_columns_option(self):
3✔
199
        """Return string well formated with all columns options."""
200
        js = [self._coloption_2_str(key, value) for key, value in self.datatable_columns.items()]
3✔
201
        return "[{0}]".format(",\n".join(js))
3✔
202

203
    def _coloption_2_str(self, name, options):
3✔
204
        s = "data:'{0}'".format(name)
3✔
205
        if options:
3✔
206
            s = "{0},\n{1}".format(s, self._dict_to_string(options))
3✔
207
        return "{{{0}}}".format(s)
3✔
208

209
    def _dict_to_string(self, d):
3✔
210
        """Convert dict to string for CanvasJS.
211

212
        Example:
213

214
        ::
215
            dico = {'key1': value1, 'key2': value2, 'key3': value3}
216
            print(CanvasJS._dict_to_string(dico))
217

218
            "key1:value1,key2:value2,key3:value3"
219
        """
220
        s = ["{0}:{1}".format(key, self._check_type(value)) for key, value in d.items()]
3✔
221
        return ",\n".join(s)
3✔
222

223
    def _check_type(self, value):
3✔
224
        """Check value type to fill javascript sections. String must be
225
        surrounded by quotes and not boolean or integer.
226

227
        Javascript variable must not be surrounded by quotes. Custom variables
228
        start with 'data_'.
229
        """
230
        try:
3✔
231
            if not value.startswith(("true", "false", "function", "{", "[")):
3✔
232
                return "'{0}'".format(value)
3✔
233
        except AttributeError:
3✔
234
            return value
3✔
235
        return value
3✔
236

237
    def set_links_to_column(self, link_col, target_col, new_page=True):
3✔
238
        """Hide a column with urls and connect it with a column.
239

240
        :param str link_col: column with your URLs.
241
        :param str target_col: column to connect.
242
        """
243
        # hide the link column
244
        try:  # pragma: no cover
245
            self.datatable_columns[link_col]["visible"] = "false"
246
        except KeyError:  # pragma: no cover
247
            keys = self.datatable_columns.keys()
248
            logger.warning(f"KeyError: Column name '{target_col}' does not exist. Use one of {keys}")
249

250
        # function to add link
251
        if new_page is True:
3✔
252
            fct = """function(data, type, row, meta){{
3✔
253
                return '<a href="'+row.{0}+'" target="_blank">'+data+'</a>';
254
            }}
255
            """.format(
256
                link_col
257
            )
258
        else:  # pragma: no cover
259
            fct = """function(data, type, row, meta){{
260
                return '<a href="'+row.{0}+'">'+data+'</a>';
261
            }}
262
            """.format(
263
                link_col
264
            )
265
        try:  # pragma: no cover
266
            self.datatable_columns[target_col]["render"] = fct
267
        except KeyError:  # pragma: no cover
268
            logger.warning("KeyError: Column name '{0}' does not exist.".format(target_col))
269

270
    def set_tooltips_to_column(self, tooltips_col, target_col):
3✔
271
        """Hide a column with tooltips and connect it with a column.
272

273
        :param str tooltips_col: column with your tooltips.
274
        :param str target_col: column to connect.
275
        """
276
        # hide tooltips
277
        try:
3✔
278
            self.datatable_columns[tooltips_col]["visible"] = "false"
3✔
279
        except KeyError:
×
UNCOV
280
            logger.warning("KeyError: Column name '{0}' does not exist.".format(target_col))
×
281
        # function to add tooltips
282
        fct = """function(data, type, row, meta){{
3✔
283
            return '<a href="#" data-toggle="tooltip" title="'+row.{0}+'">'+data+'</a>';
284
        }}
285
        """.format(
286
            tooltips_col
287
        )
288
        try:
3✔
289
            self.datatable_columns[target_col]["render"] = fct
3✔
290
        except KeyError:
×
291
            logger.warning("KeyError: Column name '{0}' does not exist.".format(target_col))
×
UNCOV
292
            pass
×
293

294

295
class DataTable(object):
3✔
296
    """Class that contains html table which used a javascript function.
297

298
    You  must add in your HTML file the JS function
299
    (:meth:`DataTable.create_javascript_function`) and the HTML code
300
    (:meth:`DataTable.create_datatable`).
301

302
    Example:
303

304
    ::
305

306
        df = pandas.read_csv('data.csv')
307
        datatable = DataTable(df, 'data')
308
        datatable.datatable.datatable_options = {'pageLength': 15,
309
                                                 'dom': 'Bfrtip',
310
                                                 'buttons': ['copy', 'csv']}
311
        js = datatable.create_javascript_function()
312
        html = datatable.create_datatable()
313

314
        # Second CSV file with same format
315
        df2 = pandas.read_csv('data2.csv')
316
        datatable2 = DataTable(df2, 'data2', datatable.datatable)
317
        html2 = datatable.create_datatable()
318

319
    The reason to include the JS manually is that you may include many HTML
320
    table but need to include the JS only once.
321

322
    """
323

324
    def __init__(self, df, html_id, datatable=None, index=False):
3✔
325
        """.. rubric:: contructor
326

327
        :param df: data frame.
328
        :param str html_id: the unique ID used in the HTML file.
329
        :param DataTableFunction datatable: javascript function to create the
330
            Jquery Datatables. If None, a :class:`DataTableFunction` is
331
            generated from the df.
332
        :param bool index: indicates whether the index dataframe should be shown
333
        """
334
        self.index = index
3✔
335
        self._df = df
3✔
336
        # Mars 2022. added 'id' in front of the id to avoid issue with is
337
        # starting with a number, which is not allowed in HTML
338
        self._html_id = f"id_{html_id}"
3✔
339
        if datatable:
3✔
340
            self.datatable = datatable
3✔
341
        else:
342
            self.datatable = DataTableFunction(df, html_id, index=index)
3✔
343

344
    def __len__(self):
3✔
UNCOV
345
        return len(self.df)
×
346

347
    @property
3✔
348
    def df(self):
3✔
349
        return self._df
3✔
350

351
    @property
3✔
352
    def html_id(self):
3✔
353
        return self._html_id
3✔
354

355
    def create_datatable(self, style="width:100%", **kwargs):
3✔
356
        """Return string well formated to include in a HTML page.
357

358
        :param str style: CSS option of your table.
359
        :param dict kwargs: parameters of :meth:`pandas.DataFrame.to_csv`.
360
        """
361

362
        html = """
3✔
363
<script async type="text/javascript">
364
    $(document).ready(function() {{
365
        var {0} = document.getElementById('csv_{0}').innerText;
366
        parseCsv_{1}({0}, '#table_{0}');
367
        {0} = null;
368
    }});
369
</script>
370
        """.format(
371
            self.html_id, self.datatable.html_id
372
        )
373
        html += self._create_hidden_csv(**kwargs)
3✔
374
        html += self._create_html_table(style)
3✔
375
        return html
3✔
376

377
    def _create_hidden_csv(self, **kwargs):
3✔
378
        """Return the HTML code and the CSV code for your hidden CSV section.
379

380
        :param **dict kwargs: parameters of :meth:`pandas.DataFrame.to_csv`.
381
        """
382
        csv = self._df.to_csv(index=self.index, **kwargs)
3✔
383
        html = '<pre id="csv_{0}">{1}</pre>'.format(self.html_id, csv.strip())
3✔
384
        css = "<style>#csv_{0}{{display:none}}</style>".format(self.html_id)
3✔
385
        return "{0}\n{1}\n".format(css, html)
3✔
386

387
    def _create_html_table(self, style):
3✔
388
        """Just for set some option and header.
389

390
        :param str style: css option of your table.
391
        """
392
        # set table id
393
        if style:
3✔
394
            style = 'style="{0}"'.format(style)
3✔
395
        html_table = '<table id="table_{0}" class="display table text-center" {1}>'.format(self.html_id, style)
3✔
396
        # create table's header
397
        th = "<th>{0}</th>"
3✔
398
        if self.index is True:
3✔
399
            header = [th.format("")]
3✔
400
            header += [th.format(name) for name in self.df]
3✔
401
        else:
402
            header = [th.format(name) for name in self.df]
3✔
403
        header = "<thead><tr>{0}</tr></thead>".format("\n".join(header))
3✔
404
        html_table = """
3✔
405
    {0}
406
        {1}
407
    </table>
408
        """.format(
409
            html_table, header
410
        )
411
        return html_table
3✔
412

413
    def create_javascript_function(self):
3✔
414
        """Generate the javascript function to create the DataTable in a HTML
415
        page.
416
        """
417
        return self.datatable.create_javascript_function()
3✔
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

© 2026 Coveralls, Inc