Skip to content

Commit 84e849f

Browse files
authored
Merge pull request #47 from highcharts-for-python/develop
PR for v.1.4.4
2 parents fd3f3e8 + ef1517d commit 84e849f

File tree

4 files changed

+234
-6
lines changed

4 files changed

+234
-6
lines changed

CHANGES.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
Release 1.4.4
2+
=========================================
3+
4+
* **BUGFIX**: Added ``FlagData`` and ``FlagDataCollection`` classes.
5+
6+
---------------------
7+
18
Release 1.4.3
29
=========================================
310

highcharts_stock/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '1.4.3'
1+
__version__ = '1.4.4'
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
from typing import Optional, List, Dict
2+
3+
from validator_collection import validators, checkers
4+
5+
from highcharts_stock.options.series.data.single_point import SingleXData
6+
from highcharts_stock.options.series.data.collections import DataPointCollection
7+
from highcharts_stock import constants, errors
8+
9+
10+
class FlagData(SingleXData):
11+
"""Data point that features a single ``x`` point with a ``title``."""
12+
13+
def __init__(self, **kwargs):
14+
self._title = None
15+
16+
self.title = kwargs.get('title', None)
17+
18+
super().__init__(**kwargs)
19+
20+
@property
21+
def title(self) -> Optional[str]:
22+
"""The short text to be shown on the flag.
23+
24+
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
25+
"""
26+
return self._title
27+
28+
@title.setter
29+
def title(self, value):
30+
if not value:
31+
self._title = None
32+
else:
33+
self._title = validators.string(value, coerce_value = True)
34+
35+
@classmethod
36+
def from_list(cls, value):
37+
if not value:
38+
return []
39+
elif checkers.is_string(value):
40+
try:
41+
value = validators.json(value)
42+
except (ValueError, TypeError):
43+
pass
44+
elif not checkers.is_iterable(value):
45+
value = [value]
46+
47+
collection = []
48+
for item in value:
49+
if checkers.is_type(item, 'FlagData'):
50+
as_obj = item
51+
elif checkers.is_dict(item):
52+
as_obj = cls.from_dict(item)
53+
elif item is None or isinstance(item, constants.EnforcedNullType):
54+
as_obj = cls(x = None)
55+
elif checkers.is_numeric(item):
56+
as_obj = cls(x = item)
57+
elif checkers.is_iterable(item):
58+
if len(item) == 2:
59+
as_obj = cls(x = item[0], title = item[1])
60+
elif len(item) == 1:
61+
as_obj = cls(x = item[0])
62+
else:
63+
raise errors.HighchartsValueError(f'data expects either a 1D or 2D '
64+
f'collection. Collection received '
65+
f'had {len(item)} dimensions.')
66+
else:
67+
raise errors.HighchartsValueError(f'each data point supplied must either '
68+
f'be a Flag Data Point or be '
69+
f'coercable to one. Could not coerce: '
70+
f'{item}')
71+
collection.append(as_obj)
72+
73+
return collection
74+
75+
@classmethod
76+
def from_ndarray(cls, value):
77+
"""Creates a collection of data points from a `NumPy <https://numpy.org>`__
78+
:class:`ndarray <numpy:ndarray>` instance.
79+
80+
:returns: A collection of data point values.
81+
:rtype: :class:`DataPointCollection <highcharts_core.options.series.data.collections.DataPointCollection>`
82+
"""
83+
return FlagDataCollection.from_ndarray(value)
84+
85+
@classmethod
86+
def _get_supported_dimensions(cls) -> List[int]:
87+
"""Returns a list of the supported dimensions for the data point.
88+
89+
:rtype: :class:`list <python:list>` of :class:`int <python:int>`
90+
"""
91+
return [1, 2]
92+
93+
@classmethod
94+
def _get_props_from_array(cls, length = None) -> List[str]:
95+
"""Returns a list of the property names that can be set using the
96+
:meth:`.from_array() <highcharts_core.options.series.data.base.DataBase.from_array>`
97+
method.
98+
99+
:param length: The length of the array, which may determine the properties to
100+
parse. Defaults to :obj:`None <python:None>`, which returns the full list of
101+
properties.
102+
:type length: :class:`int <python:int>` or :obj:`None <python:None>`
103+
104+
:rtype: :class:`list <python:list>` of :class:`str <python:str>`
105+
"""
106+
prop_list = {
107+
None: ['x', 'title'],
108+
1: ['x'],
109+
2: ['x', 'title']
110+
}
111+
return cls._get_props_from_array_helper(prop_list, length)
112+
113+
def to_array(self, force_object = False) -> List | Dict:
114+
"""Generate the array representation of the data point (the inversion
115+
of
116+
:meth:`.from_array() <highcharts_core.options.series.data.base.DataBase.from_array>`).
117+
118+
.. warning::
119+
120+
If the data point *cannot* be serialized to a JavaScript array,
121+
this method will instead return the untrimmed :class:`dict <python:dict>`
122+
representation of the data point as a fallback.
123+
124+
:param force_object: if ``True``, forces the return of the instance's
125+
untrimmed :class:`dict <python:dict>` representation. Defaults to ``False``.
126+
:type force_object: :class:`bool <python:bool>`
127+
128+
:returns: The array representation of the data point.
129+
:rtype: :class:`list <python:list>` of values or :class:`dict <python:dict>`
130+
"""
131+
if self.requires_js_object or force_object:
132+
return self._to_untrimmed_dict()
133+
134+
if self.x is not None:
135+
x = self.x
136+
else:
137+
x = constants.EnforcedNull
138+
139+
if self.title is not None:
140+
title = self.title
141+
else:
142+
title = constants.EnforcedNull
143+
144+
if self.title is None:
145+
return [x]
146+
147+
return [x, title]
148+
149+
@classmethod
150+
def _get_kwargs_from_dict(cls, as_dict):
151+
"""Convenience method which returns the keyword arguments used to initialize the
152+
class from a Highcharts Javascript-compatible :class:`dict <python:dict>` object.
153+
154+
:param as_dict: The HighCharts JS compatible :class:`dict <python:dict>`
155+
representation of the object.
156+
:type as_dict: :class:`dict <python:dict>`
157+
158+
:returns: The keyword arguments that would be used to initialize an instance.
159+
:rtype: :class:`dict <python:dict>`
160+
161+
"""
162+
kwargs = {
163+
'accessibility': as_dict.get('accessibility', None),
164+
'class_name': as_dict.get('className', None),
165+
'color': as_dict.get('color', None),
166+
'color_index': as_dict.get('colorIndex', None),
167+
'custom': as_dict.get('custom', None),
168+
'description': as_dict.get('description', None),
169+
'events': as_dict.get('events', None),
170+
'id': as_dict.get('id', None),
171+
'label_rank': as_dict.get('labelrank',
172+
None) or as_dict.get('labelRank',
173+
None),
174+
'name': as_dict.get('name', None),
175+
'selected': as_dict.get('selected', None),
176+
177+
'data_labels': as_dict.get('dataLabels', None),
178+
'drag_drop': as_dict.get('dragDrop', None),
179+
'drilldown': as_dict.get('drilldown', None),
180+
181+
'x': as_dict.get('x', None),
182+
183+
'title': as_dict.get('title', None),
184+
}
185+
186+
return kwargs
187+
188+
def _to_untrimmed_dict(self, in_cls = None) -> dict:
189+
untrimmed = {
190+
'title': self.title,
191+
'x': self.x,
192+
193+
'dataLabels': self.data_labels,
194+
'dragDrop': self.drag_drop,
195+
'drilldown': self.drilldown,
196+
197+
'accessibility': self.accessibility,
198+
'className': self.class_name,
199+
'color': self.color,
200+
'colorIndex': self.color_index,
201+
'custom': self.custom,
202+
'description': self.description,
203+
'events': self.events,
204+
'id': self.id,
205+
'labelrank': self.label_rank,
206+
'name': self.name,
207+
'selected': self.selected,
208+
209+
}
210+
211+
return untrimmed
212+
213+
214+
class FlagDataCollection(DataPointCollection):
215+
@classmethod
216+
def _get_data_point_class(cls):
217+
"""The Python class to use as the underlying data point within the Collection.
218+
219+
:rtype: class object
220+
"""
221+
return FlagData

highcharts_stock/options/series/flags.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
from highcharts_stock.options.series.base import NavigatorIndicatorSeries
66
from highcharts_stock.options.plot_options.flags import FlagsOptions
7-
from highcharts_stock.options.series.data.single_point import (SingleXData,
8-
SingleXDataCollection)
7+
from highcharts_stock.options.series.data.flags import (FlagData,
8+
FlagDataCollection)
99

1010

1111
class FlagsSeries(NavigatorIndicatorSeries, FlagsOptions):
@@ -26,7 +26,7 @@ def __init__(self, **kwargs):
2626
super().__init__(**kwargs)
2727

2828
@property
29-
def data(self) -> Optional[List[SingleXData] | SingleXDataCollection]:
29+
def data(self) -> Optional[List[FlagData] | FlagDataCollection]:
3030
"""Collection of data that represents the series. Defaults to
3131
:obj:`None <python:None>`.
3232
@@ -74,7 +74,7 @@ def _data_collection_class(cls):
7474
:rtype: :class:`DataPointCollection <highcharts_core.options.series.data.collections.DataPointCollection>`
7575
descendent
7676
"""
77-
return SingleXDataCollection
77+
return FlagDataCollection
7878

7979
@classmethod
8080
def _data_point_class(cls):
@@ -83,7 +83,7 @@ def _data_point_class(cls):
8383
:rtype: :class:`DataBase <highcharts_core.options.series.data.base.DataBase>`
8484
descendent
8585
"""
86-
return SingleXData
86+
return FlagData
8787

8888
@classmethod
8989
def _get_kwargs_from_dict(cls, as_dict):

0 commit comments

Comments
 (0)