Skip to content

Commit 959a3d6

Browse files
committed
Added Chart.from_array() support.
1 parent d7f9056 commit 959a3d6

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

highcharts_gantt/chart.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,111 @@ def add_series(self, *series):
424424

425425
self.options.series = updated_series
426426

427+
@classmethod
428+
def from_array(cls,
429+
value,
430+
series_type = 'line',
431+
series_kwargs = None,
432+
options_kwargs = None,
433+
chart_kwargs = None,
434+
is_gantt_chart = False):
435+
"""Create a :class:`Chart <highcharts_core.chart.Chart>` instance with
436+
one series populated from the array contained in ``value``.
437+
438+
.. seealso::
439+
440+
The specific structure of the expected array is highly dependent on the type of data
441+
point that the series needs, which itself is dependent on the series type itself.
442+
443+
Please review the detailed :ref:`series documentation <series_documentation>` for
444+
series type-specific details of relevant array structures.
445+
446+
:param value: The array to use to populate the series data.
447+
:type value: iterable
448+
449+
:param series_type: Indicates the series type that should be created from the array
450+
data. Defaults to ``'line'``.
451+
:type series_type: :class:`str <python:str>`
452+
453+
:param series_kwargs: An optional :class:`dict <python:dict>` containing keyword
454+
arguments that should be used when instantiating the series instance. Defaults
455+
to :obj:`None <python:None>`.
456+
457+
.. warning::
458+
459+
If ``series_kwargs`` contains a ``data`` key, its value will be *overwritten*.
460+
The ``data`` value will be created from ``df`` instead.
461+
462+
:type series_kwargs: :class:`dict <python:dict>`
463+
464+
:param options_kwargs: An optional :class:`dict <python:dict>` containing keyword
465+
arguments that should be used when instantiating the :class:`HighchartsOptions`
466+
instance. Defaults to :obj:`None <python:None>`.
467+
468+
.. warning::
469+
470+
If ``options_kwargs`` contains a ``series`` key, the ``series`` value will be
471+
*overwritten*. The ``series`` value will be created from the data in ``df``.
472+
473+
:type options_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>`
474+
475+
:param chart_kwargs: An optional :class:`dict <python:dict>` containing keyword
476+
arguments that should be used when instantiating the :class:`Chart` instance.
477+
Defaults to :obj:`None <python:None>`.
478+
479+
.. warning::
480+
481+
If ``chart_kwargs`` contains an ``options`` key, ``options`` will be
482+
*overwritten*. The ``options`` value will be created from the
483+
``options_kwargs`` and the data in ``df`` instead.
484+
485+
:type chart_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>`
486+
487+
:param is_gantt_chart: if ``True``, enforces the use of
488+
:class:`HighchartsGanttOptions <highcharts_stock.options.HighchartsGanttOptions>`.
489+
If ``False``, applies
490+
:class:`HighchartsOptions <highcharts_stock.options.HighchartsOptions>`.
491+
Defaults to ``False``.
492+
493+
.. note::
494+
495+
The value given to this argument will override any values specified in
496+
``chart_kwargs``.
497+
498+
:type is_gantt_chart: :class:`bool <python:bool>`
499+
500+
:returns: A :class:`Chart <highcharts_core.chart.Chart>` instance with its
501+
data populated from the data in ``value``.
502+
:rtype: :class:`Chart <highcharts_core.chart.Chart>`
503+
504+
"""
505+
series_type = validators.string(series_type, allow_empty = False)
506+
series_type = series_type.lower()
507+
if series_type not in SERIES_CLASSES:
508+
raise errors.HighchartsValueError(f'series_type expects a valid Highcharts '
509+
f'series type. Received: {series_type}')
510+
511+
series_kwargs = validators.dict(series_kwargs, allow_empty = True) or {}
512+
options_kwargs = validators.dict(options_kwargs, allow_empty = True) or {}
513+
chart_kwargs = validators.dict(chart_kwargs, allow_empty = True) or {}
514+
515+
series_cls = SERIES_CLASSES.get(series_type, None)
516+
517+
series = series_cls.from_array(value, series_kwargs = series_kwargs)
518+
519+
options_kwargs['series'] = [series]
520+
chart_kwargs['is_gantt_chart'] = is_gantt_chart
521+
522+
if is_gantt_chart:
523+
options = HighchartsGanttOptions(**options_kwargs)
524+
else:
525+
options = HighchartsOptions(**options_kwargs)
526+
527+
instance = cls(**chart_kwargs)
528+
instance.options = options
529+
530+
return instance
531+
427532
@classmethod
428533
def from_series(cls, *series, kwargs = None):
429534
"""Creates a new :class:`Chart <highcharts_core.chart.Chart>` instance populated

tests/test_chart.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
import os
44
import pytest
5+
try:
6+
import numpy as np
7+
HAS_NUMPY = True
8+
except ImportError:
9+
HAS_NUMPY = False
510

611
from json.decoder import JSONDecodeError
712

@@ -522,3 +527,65 @@ def test_from_csv(input_files, filename, property_map, kwargs, expected_series,
522527
result = cls.from_csv(input_file,
523528
property_column_map = property_map,
524529
**kwargs)
530+
531+
532+
@pytest.mark.parametrize('value, expected_shape, has_ndarray, has_data_points, error', [
533+
(np.asarray([
534+
[0.0, 15.0],
535+
[10.0, -50.0],
536+
[20.0, -56.5],
537+
[30.0, -46.5],
538+
[40.0, -22.1],
539+
[50.0, -2.5],
540+
[60.0, -27.7],
541+
[70.0, -55.7],
542+
[80.0, -76.5]
543+
]) if HAS_NUMPY else [
544+
[0.0, 15.0],
545+
[10.0, -50.0],
546+
[20.0, -56.5],
547+
[30.0, -46.5],
548+
[40.0, -22.1],
549+
[50.0, -2.5],
550+
[60.0, -27.7],
551+
[70.0, -55.7],
552+
[80.0, -76.5]
553+
], (9, 2), True, False, None),
554+
([
555+
{
556+
'id': 'some-value'
557+
},
558+
{
559+
'id': 'some other value'
560+
},
561+
], (2, 2), False, True, None),
562+
563+
('Not an Array', None, True, False, ValueError),
564+
])
565+
def test_from_array(value, expected_shape, has_ndarray, has_data_points, error):
566+
if has_ndarray is False and has_data_points is False:
567+
raise AssertionError('Test is invalid. has_ndarray or has_data_points must be '
568+
'True. Both were supplied as False.')
569+
if not error:
570+
result = cls.from_array(value)
571+
assert result is not None
572+
assert result.options.series is not None
573+
assert len(result.options.series) == 1
574+
assert result.options.series[0].data is not None
575+
576+
if has_ndarray:
577+
data = result.options.series[0].data
578+
if HAS_NUMPY:
579+
assert data.ndarray is not None
580+
assert data.ndarray.shape == expected_shape
581+
else:
582+
assert data.array is not None or data.data_points is not None
583+
if data.array:
584+
assert len(data.array) == expected_shape[0]
585+
else:
586+
assert len(data.data_points) == expected_shape[0]
587+
if has_data_points:
588+
assert len(result.options.series[0].data) == expected_shape[0]
589+
else:
590+
with pytest.raises(error):
591+
result = cls.from_array(value)

0 commit comments

Comments
 (0)