|
| 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 |
0 commit comments