diff --git a/CHANGES.rst b/CHANGES.rst
index 3f3c99ec..450ff187 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,16 @@
+Release 1.3.0
+=========================================
+
+* **ENHANCEMENT:** Modified the way that data points are serialized to JavaScript literal objects. Now, they are serialized to a JavaScript array if their configured properties are those that Highcharts (JS) supports in JavaScript array notation. Otherwise, the code falls back to serialize the data point as a JavaScript object literal. This change is intended to improve performance and reduce the size of the serialized data. (#77)
+* **ENHANCEMENT:** Added ``__repr__()`` method for Highcharts Core for Python classes (#76).
+* **ENHANCEMENT:** Added ``__str__()`` method with special handling for difficult-to-read classes (#76).
+* **ENHANCEMENT:** Added ``Chart.get_script_tags()`` to retrieve Javascript ``'
+ for x in self.get_required_modules(include_extension = True)]
+
+ if as_str:
+ return '\n'.join(scripts)
+
+ return scripts
+
+ def get_required_modules(self,
+ include_extension = False) -> List[str]:
"""Return the list of URLs from which the Highcharts JavaScript modules
needed to render the chart can be retrieved.
:param include_extension: if ``True``, will return script names with the
``'.js'`` extension included. Defaults to ``False``.
:type include_extension: :class:`bool `
-
- :rtype: :class:`list `
+
+ :rtype: :class:`list ` of :class:`str `
"""
initial_scripts = ['highcharts']
scripts = self._process_required_modules(initial_scripts, include_extension)
@@ -189,8 +254,9 @@ def callback(self, value):
@property
def module_url(self) -> str:
"""The URL from which Highcharts modules should be downloaded when
- generating the ```` tags. Defaults to
- ``'https://code.highcharts.com/'``.
+ generating the ```` tags. Will default to the
+ ``HIGHCHARTS_MODULE_URL`` environment variable if available, and
+ otherwise defaults to ``'https://code.highcharts.com/'``.
.. tip::
diff --git a/highcharts_core/constants.py b/highcharts_core/constants.py
index 37c0e525..0e340a4c 100644
--- a/highcharts_core/constants.py
+++ b/highcharts_core/constants.py
@@ -28,6 +28,9 @@
class EnforcedNullType:
def __eq__(self, other):
return isinstance(other, self.__class__)
+
+ def __repr__(self):
+ return "EnforcedNullType()"
EnforcedNull = EnforcedNullType()
diff --git a/highcharts_core/js_literal_functions.py b/highcharts_core/js_literal_functions.py
index 914fba01..51e647c2 100644
--- a/highcharts_core/js_literal_functions.py
+++ b/highcharts_core/js_literal_functions.py
@@ -25,7 +25,14 @@ def serialize_to_js_literal(item, encoding = 'utf-8') -> Optional[str]:
:rtype: :class:`str ` or :obj:`None `
"""
if checkers.is_iterable(item, forbid_literals = (str, bytes, dict, UserDict)):
- return [serialize_to_js_literal(x, encoding = encoding) for x in item]
+ requires_js_objects = all([getattr(x, 'requires_js_object', True)
+ for x in item])
+ if requires_js_objects:
+ return [serialize_to_js_literal(x, encoding = encoding)
+ for x in item]
+ else:
+ return [serialize_to_js_literal(x.to_array(), encoding = encoding)
+ for x in item]
elif hasattr(item, 'to_js_literal'):
return item.to_js_literal(encoding = encoding)
elif isinstance(item, constants.EnforcedNullType) or item == 'null':
diff --git a/highcharts_core/metaclasses.py b/highcharts_core/metaclasses.py
index a2187fdb..61ea9537 100644
--- a/highcharts_core/metaclasses.py
+++ b/highcharts_core/metaclasses.py
@@ -41,6 +41,23 @@ def __eq__(self, other):
return self_js_literal == other_js_literal
+ def __repr__(self):
+ """Generate an unambiguous and complete :class:`str ` representation
+ of the object.
+
+ :returns: An unambiguous and complete :class:`str ` representation
+ of the object (which may have varying degrees of readability).
+ :rtype: :class:`str `
+ """
+ as_dict = self.to_dict()
+
+ kwargs = {utility_functions.to_snake_case(key): as_dict[key]
+ for key in as_dict}
+ kwargs_as_str = ', '.join([f'{key} = {repr(kwargs[key])}'
+ for key in kwargs])
+
+ return f'{self.__class__.__name__}({kwargs_as_str})'
+
@property
def _dot_path(self) -> Optional[str]:
"""The dot-notation path to the options key for the current class.
@@ -115,15 +132,16 @@ def get_required_modules(self, include_extension = False) -> List[str]:
``'.js'`` extension included. Defaults to ``False``.
:type include_extension: :class:`bool `
- :rtype: :class:`list `
+ :rtype: :class:`list ` of :class:`str `
"""
initial_scripts = constants.MODULE_REQUIREMENTS.get(self._dot_path, [])
- prelim_scripts = self._process_required_modules(initial_scripts, include_extension = include_extension)
+ prelim_scripts = self._process_required_modules(initial_scripts,
+ include_extension = include_extension)
scripts = []
has_all_indicators = False
for script in prelim_scripts:
- if script.endswith('indicators-all.js'):
+ if script.endswith('indicators-all.js') or script.endswith('indicators-all'):
has_all_indicators = True
for script in prelim_scripts:
@@ -255,7 +273,10 @@ def trim_dict(untrimmed: dict,
as_dict[key] = trimmed_value
# Enforced null
elif isinstance(value, constants.EnforcedNullType):
- as_dict[key] = 'null'
+ if to_json:
+ as_dict[key] = None
+ else:
+ as_dict[key] = value
# dict -> object
elif isinstance(value, dict):
trimmed_value = HighchartsMeta.trim_dict(value,
@@ -270,12 +291,26 @@ def trim_dict(untrimmed: dict,
context = context)
if trimmed_value:
as_dict[key] = trimmed_value
- # Pandas Timestamp
- elif checkers.is_type(value, 'Timestamp'):
- as_dict[key] = value.timestamp()
+ # Datetime or Datetime-like
+ elif checkers.is_datetime(value):
+ trimmed_value = value
+ if to_json:
+ if not value.tzinfo:
+ trimmed_value = value.replace(tzinfo = datetime.timezone.utc)
+ as_dict[key] = trimmed_value.timestamp() * 1000
+ elif hasattr(trimmed_value, 'to_pydatetime'):
+ as_dict[key] = trimmed_value.to_pydatetime()
+ else:
+ as_dict[key] = trimmed_value
+ # Date or Time
+ elif checkers.is_date(value) or checkers.is_time(value):
+ if to_json:
+ as_dict[key] = value.isoformat()
+ else:
+ as_dict[key] = value
# other truthy -> str / number
elif value:
- trimmed_value = HighchartsMeta.trim_iterable(value,
+ trimmed_value = HighchartsMeta.trim_iterable(value,
to_json = to_json,
context = context)
if trimmed_value:
@@ -422,7 +457,7 @@ def to_json(self,
context = self.__class__.__name__)
for key in as_dict:
- if as_dict[key] == constants.EnforcedNull or as_dict[key] == 'null':
+ if as_dict[key] == constants.EnforcedNull or as_dict[key] is None:
as_dict[key] = None
try:
as_json = json.dumps(as_dict, encoding = encoding)
diff --git a/highcharts_core/options/__init__.py b/highcharts_core/options/__init__.py
index e3fae433..166ec06c 100644
--- a/highcharts_core/options/__init__.py
+++ b/highcharts_core/options/__init__.py
@@ -96,6 +96,36 @@ def __init__(self, **kwargs):
self.series = kwargs.get('series', None)
+ def __str__(self):
+ """Return a human-readable :class:`str ` representation of the chart
+ configuration.
+
+ .. warning::
+
+ To ensure that the result is human-readable, the result will be rendered
+ *without* its ``plot_options`` and ``series`` sub-properties.
+
+ .. tip::
+
+ If you would like a *complete* and *unambiguous* :class:`str `
+ representation, then you can:
+
+ * use the :meth:`__repr__() ` method,
+ * call ``repr(my_options)``, or
+ * serialize the chart to JSON using ``my_options.to_json()``.
+
+ :returns: A :class:`str ` representation of the chart configuration.
+ :rtype: :class:`str `
+ """
+ as_dict = self.to_dict()
+
+ kwargs = {utility_functions.to_snake_case(key): as_dict[key]
+ for key in as_dict if key not in ['series', 'plotOptions']}
+ kwargs_as_str = ', '.join([f'{key} = {repr(kwargs[key])}'
+ for key in kwargs])
+
+ return f'{self.__class__.__name__}({kwargs_as_str})'
+
@property
def accessibility(self) -> Optional[Accessibility]:
"""Options for configuring accessibility for the chart.
diff --git a/highcharts_core/options/series/base.py b/highcharts_core/options/series/base.py
index f3fe48bf..38740c65 100644
--- a/highcharts_core/options/series/base.py
+++ b/highcharts_core/options/series/base.py
@@ -46,6 +46,37 @@ def __init__(self, **kwargs):
super().__init__(**kwargs)
+ def __str__(self):
+ """Return a human-readable :class:`str ` representation of the series.
+
+ .. warning::
+
+ To ensure that the result is human-readable, the string representation
+ will be generated *without* its
+ :meth:`.data `
+ property.
+
+ .. tip::
+
+ If you would like a *complete* and *unambiguous* :class:`str `
+ representation, then you can:
+
+ * use the :meth:`__repr__() ` method,
+ * call ``repr(my_series)``, or
+ * serialize the series to JSON using ``my_series.to_json()``.
+
+ :returns: A :class:`str ` representation of the chart.
+ :rtype: :class:`str `
+ """
+ as_dict = self.to_dict()
+
+ kwargs = {utility_functions.to_snake_case(key): as_dict[key]
+ for key in as_dict if key != 'data'}
+ kwargs_as_str = ', '.join([f'{key} = {repr(kwargs[key])}'
+ for key in kwargs])
+
+ return f'{self.__class__.__name__}({kwargs_as_str})'
+
@property
def _dot_path(self) -> Optional[str]:
"""The dot-notation path to the options key for the current class.
diff --git a/highcharts_core/options/series/data/arcdiagram.py b/highcharts_core/options/series/data/arcdiagram.py
index 3a889e9a..064902d3 100644
--- a/highcharts_core/options/series/data/arcdiagram.py
+++ b/highcharts_core/options/series/data/arcdiagram.py
@@ -1,10 +1,10 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
from collections import UserDict
from validator_collection import validators, checkers
-from highcharts_core import utility_functions, constants
+from highcharts_core import utility_functions, constants, errors
from highcharts_core.options.series.data.base import DataBase
from highcharts_core.utility_classes.gradients import Gradient
from highcharts_core.utility_classes.patterns import Pattern
@@ -126,6 +126,17 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['from_',
+ 'to',
+ 'weight']
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/options/series/data/bar.py b/highcharts_core/options/series/data/bar.py
index 86263e71..8c5b7bb3 100644
--- a/highcharts_core/options/series/data/bar.py
+++ b/highcharts_core/options/series/data/bar.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
import datetime
@@ -365,6 +365,7 @@ def from_array(cls, value):
as_obj = cls.from_dict(as_dict)
if checkers.is_string(as_obj.x):
as_obj.name = as_obj.x
+ as_obj.x = None
else:
raise errors.HighchartsValueError(f'each data point supplied must either '
f'be a WindBarb Data Point or be '
@@ -375,6 +376,63 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x', 'value', 'direction', 'y', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.x is None and self.name is not None:
+ x = self.name
+ elif self.x is not None:
+ x = self.x
+ else:
+ x = constants.EnforcedNull
+
+ if self.y is not None:
+ y = self.y
+ else:
+ y = constants.EnforcedNull
+
+ if self.value is not None:
+ value = self.value
+ else:
+ value = constants.EnforcedNull
+
+ if self.direction is not None:
+ direction = self.direction
+ else:
+ direction = constants.EnforcedNull
+
+ if self.y is None:
+ return [x, value, direction]
+
+ return [x, value, direction, y]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/options/series/data/base.py b/highcharts_core/options/series/data/base.py
index 5e8af127..6fd178f2 100644
--- a/highcharts_core/options/series/data/base.py
+++ b/highcharts_core/options/series/data/base.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
from validator_collection import validators, checkers
@@ -304,6 +304,38 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict:
return untrimmed
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return []
+
+ @property
+ def requires_js_object(self) -> bool:
+ """Indicates whether or not the data point *must* be serialized to a JS literal
+ object or whether it can be serialized to a primitive array.
+
+ :returns: ``True`` if the data point *must* be serialized to a JS literal object.
+ ``False`` if it can be serialized to an array.
+ :rtype: :class:`bool `
+ """
+ from_array_props = [utility_functions.to_camelCase(x)
+ for x in self._get_props_from_array()]
+
+ as_dict = self.to_dict()
+ trimmed_dict = self.trim_dict(as_dict)
+ for prop in from_array_props:
+ if prop in trimmed_dict:
+ del trimmed_dict[prop]
+
+ if trimmed_dict:
+ return True
+
+ return False
+
@classmethod
def from_array(cls, value):
"""Creates a collection of data point instances, parsing the contents of ``value``
@@ -416,3 +448,27 @@ def from_array(cls, value):
collection.append(as_obj)
return collection
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ return [getattr(self, x, constants.EnforcedNull)
+ for x in self._get_props_from_array()]
diff --git a/highcharts_core/options/series/data/boxplot.py b/highcharts_core/options/series/data/boxplot.py
index 82caecfa..55bf1591 100644
--- a/highcharts_core/options/series/data/boxplot.py
+++ b/highcharts_core/options/series/data/boxplot.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
from validator_collection import validators, checkers
@@ -278,8 +278,9 @@ def from_array(cls, value):
f'had {len(item)} dimensions.')
as_obj = cls.from_dict(as_dict)
- if checkers.is_string(as_obj.x):
+ if checkers.is_string(as_obj.x) and not as_obj.name:
as_obj.name = as_obj.x
+ as_obj.x = None
else:
raise errors.HighchartsValueError(f'each data point supplied must either '
f'be a BoxPlot Data Point or be '
@@ -289,6 +290,73 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x', 'low', 'q1', 'median', 'q3', 'high', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.x is not None:
+ x = self.x
+ elif self.name is not None:
+ x = self.name
+ else:
+ x = constants.EnforcedNull
+
+ if self.low is not None:
+ low = self.low
+ else:
+ low = constants.EnforcedNull
+
+ if self.q1 is not None:
+ q1 = self.q1
+ else:
+ q1 = constants.EnforcedNull
+
+ if self.median is not None:
+ median = self.median
+ else:
+ median = constants.EnforcedNull
+
+ if self.q3 is not None:
+ q3 = self.q3
+ else:
+ q3 = constants.EnforcedNull
+
+ if self.high is not None:
+ high = self.high
+ else:
+ high = constants.EnforcedNull
+
+ if self.x is None and self.name is None:
+ return [low, q1, median, q3, high]
+
+ return [x, low, q1, median, q3, high]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/options/series/data/bullet.py b/highcharts_core/options/series/data/bullet.py
index 22ce6fa3..01981a1a 100644
--- a/highcharts_core/options/series/data/bullet.py
+++ b/highcharts_core/options/series/data/bullet.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
from validator_collection import validators, checkers
@@ -87,8 +87,9 @@ def from_array(cls, value):
f'had {len(item)} dimensions.')
as_obj = cls.from_dict(as_dict)
- if checkers.is_string(as_obj.x):
+ if checkers.is_string(as_obj.x) and not as_obj.name:
as_obj.name = as_obj.x
+ as_obj.x = None
else:
raise errors.HighchartsValueError(f'each data point supplied must either '
f'be a Bullet Data Point or be '
@@ -98,6 +99,58 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x', 'y', 'target', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.x is not None:
+ x = self.x
+ elif self.name is not None:
+ x = self.name
+ else:
+ x = constants.EnforcedNull
+
+ if self.y is not None:
+ y = self.y
+ else:
+ y = constants.EnforcedNull
+
+ if self.target is not None:
+ target = self.target
+ else:
+ target = constants.EnforcedNull
+
+ if self.x is None and self.name is None:
+ return [y, target]
+
+ return [x, y, target]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/options/series/data/cartesian.py b/highcharts_core/options/series/data/cartesian.py
index 991ba26b..b5b2c666 100644
--- a/highcharts_core/options/series/data/cartesian.py
+++ b/highcharts_core/options/series/data/cartesian.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
import datetime
@@ -171,8 +171,6 @@ def from_array(cls, value):
elif checkers.is_iterable(item):
if len(item) == 2:
as_obj = cls(x = item[0], y = item[1])
- if checkers.is_string(as_obj.x):
- as_obj.name = as_obj.x
elif len(item) == 1:
as_obj = cls(y = item[0])
else:
@@ -184,10 +182,60 @@ def from_array(cls, value):
f'be a Cartesian Data Point or be '
f'coercable to one. Could not coerce: '
f'{item}')
+ if checkers.is_string(as_obj.x) and not as_obj.name:
+ as_obj.name = as_obj.x
+ as_obj.x = None
collection.append(as_obj)
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x', 'y', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.y is not None:
+ y = self.y
+ else:
+ y = constants.EnforcedNull
+
+ if self.x is None and self.name is None:
+ x = self.x
+ elif self.name is None:
+ x = self.x
+ else:
+ x = self.name
+
+ if self.x is None and self.name is None:
+ return [y]
+
+ return [x, y]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
@@ -324,10 +372,64 @@ def from_array(cls, value):
f'coercable to one. Could not coerce: '
f'{item}')
+ if checkers.is_string(as_obj.x) and not as_obj.name:
+ as_obj.name = as_obj.x
+ as_obj.x = None
collection.append(as_obj)
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x', 'y', 'z', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.y is not None:
+ y = self.y
+ else:
+ y = constants.EnforcedNull
+ if self.z is not None:
+ z = self.z
+ else:
+ z = constants.EnforcedNull
+
+ if self.x is None and self.name is not None:
+ x = self.name
+ elif self.name is None and self.x is not None:
+ x = self.x
+ else:
+ x = constants.EnforcedNull
+
+ if self.x is None and self.name is None:
+ return [y, z]
+
+ return [x, y, z]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
@@ -475,18 +577,71 @@ def from_array(cls, value):
f'collection. Collection received '
f'had {len(item)} dimensions.')
as_obj = cls.from_dict(as_dict)
- if checkers.is_string(as_obj.x):
- as_obj.name = as_obj.x
else:
raise errors.HighchartsValueError(f'each data point supplied must either '
f'be a Cartesian Value Data Point or be'
f' coercable to one. Could not coerce: '
f'{item}')
+ if checkers.is_string(as_obj.x) and not as_obj.name:
+ as_obj.name = as_obj.x
+ as_obj.x = None
collection.append(as_obj)
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x', 'y', 'value', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.y is not None:
+ y = self.y
+ else:
+ y = constants.EnforcedNull
+
+ if self.value is not None:
+ value = self.value
+ else:
+ value = constants.EnforcedNull
+
+ if self.x is None and self.name is not None:
+ x = self.name
+ elif self.x is not None:
+ x = self.x
+ else:
+ x = constants.EnforcedNull
+
+ if self.x is None and self.name is None:
+ return [y, value]
+
+ return [x, y, value]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/options/series/data/connections.py b/highcharts_core/options/series/data/connections.py
index 81f17fb7..c22e3035 100644
--- a/highcharts_core/options/series/data/connections.py
+++ b/highcharts_core/options/series/data/connections.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List
from decimal import Decimal
from validator_collection import validators, checkers
@@ -214,6 +214,16 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['from_',
+ 'to']
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
@@ -321,6 +331,15 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return []
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/options/series/data/range.py b/highcharts_core/options/series/data/range.py
index bfddaffa..2f7e8b8e 100644
--- a/highcharts_core/options/series/data/range.py
+++ b/highcharts_core/options/series/data/range.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
import datetime
@@ -204,8 +204,9 @@ def from_array(cls, value):
f'had {len(item)} dimensions.')
as_obj = cls.from_dict(as_dict)
- if checkers.is_string(as_obj.x):
+ if checkers.is_string(as_obj.x) and not as_obj.name:
as_obj.name = as_obj.x
+ as_obj.x = None
else:
raise errors.HighchartsValueError(f'each data point supplied must either '
f'be an AreaRangeData Point or be '
@@ -215,6 +216,58 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x', 'low', 'high', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.x is not None:
+ x = self.x
+ elif self.name is not None:
+ x = self.name
+ else:
+ x = constants.EnforcedNull
+
+ if self.low is not None:
+ low = self.low
+ else:
+ low = constants.EnforcedNull
+
+ if self.high is not None:
+ high = self.high
+ else:
+ high = constants.EnforcedNull
+
+ if self.x is None and self.name is None:
+ return [low, high]
+
+ return [x, low, high]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/options/series/data/single_point.py b/highcharts_core/options/series/data/single_point.py
index ac8b7f6e..5253ed11 100644
--- a/highcharts_core/options/series/data/single_point.py
+++ b/highcharts_core/options/series/data/single_point.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
from collections import UserDict
@@ -197,6 +197,51 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['y', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.y is not None:
+ y = self.y
+ else:
+ y = constants.EnforcedNull
+
+ if self.name is not None:
+ name = self.name
+ else:
+ name = constants.EnforcedNull
+
+ if not self.name:
+ return [y]
+
+ return [name, y]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
@@ -316,6 +361,43 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['value']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.value is not None:
+ value = self.value
+ else:
+ value = constants.EnforcedNull
+
+ return [value]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
@@ -434,6 +516,43 @@ def from_array(cls, value):
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.x is not None:
+ x = self.x
+ else:
+ x = constants.EnforcedNull
+
+ return [x]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/options/series/data/sunburst.py b/highcharts_core/options/series/data/sunburst.py
index 012052db..1ee63df9 100644
--- a/highcharts_core/options/series/data/sunburst.py
+++ b/highcharts_core/options/series/data/sunburst.py
@@ -1,6 +1,6 @@
from typing import Optional
-from validator_collection import checkers
+from validator_collection import checkers, validators
from highcharts_core import constants, errors
from highcharts_core.decorators import class_sensitive
diff --git a/highcharts_core/options/series/data/vector.py b/highcharts_core/options/series/data/vector.py
index df0ed6c4..27f13531 100644
--- a/highcharts_core/options/series/data/vector.py
+++ b/highcharts_core/options/series/data/vector.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List, Dict
from decimal import Decimal
from validator_collection import validators, checkers
@@ -86,10 +86,70 @@ def from_array(cls, value):
f'coercable to one. Could not coerce: '
f'{item}')
+ if checkers.is_string(as_obj.x) and not as_obj.name:
+ as_obj.name = as_obj.x
+ as_obj.x = None
collection.append(as_obj)
return collection
+ def _get_props_from_array(self) -> List[str]:
+ """Returns a list of the property names that can be set using the
+ :meth:`.from_array() `
+ method.
+
+ :rtype: :class:`list ` of :class:`str `
+ """
+ return ['x', 'y', 'length', 'direction', 'name']
+
+ def to_array(self, force_object = False) -> List | Dict:
+ """Generate the array representation of the data point (the inversion
+ of
+ :meth:`.from_array() `).
+
+ .. warning::
+
+ If the data point *cannot* be serialized to a JavaScript array,
+ this method will instead return the untrimmed :class:`dict `
+ representation of the data point as a fallback.
+
+ :param force_object: if ``True``, forces the return of the instance's
+ untrimmed :class:`dict ` representation. Defaults to ``False``.
+ :type force_object: :class:`bool `
+
+ :returns: The array representation of the data point.
+ :rtype: :class:`list ` of values or :class:`dict `
+ """
+ if self.requires_js_object or force_object:
+ return self._to_untrimmed_dict()
+
+ if self.y is not None:
+ y = self.y
+ else:
+ y = constants.EnforcedNull
+
+ if self.length is not None:
+ length = self.length
+ else:
+ length = constants.EnforcedNull
+
+ if self.direction is not None:
+ direction = self.direction
+ else:
+ direction = constants.EnforcedNull
+
+ if self.x is not None:
+ x = self.x
+ elif self.name is not None:
+ x = self.name
+ else:
+ x = constants.EnforcedNull
+
+ if self.x is None and self.name is None:
+ return [y, length, direction]
+
+ return [x, y, length, direction]
+
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
"""Convenience method which returns the keyword arguments used to initialize the
diff --git a/highcharts_core/utility_functions.py b/highcharts_core/utility_functions.py
index 010a1b94..b42dddf0 100644
--- a/highcharts_core/utility_functions.py
+++ b/highcharts_core/utility_functions.py
@@ -248,6 +248,35 @@ def to_camelCase(snake_case):
return camel_case
+def to_snake_case(camel_case) -> str:
+ """Convert ``camelCase`` to ``snake_case``.
+
+ :param camel_case: A :class:`str ` which is likely to contain
+ ``camelCase``.
+ :type camel_case: :class:`str `
+
+ :returns: A ``snake_case`` representation of ``camel_case``.
+ :rtype: :class:`str `
+ """
+ camel_case = validators.string(camel_case)
+
+ snake_case = ''
+ previous_character = ''
+ for character in camel_case:
+ if character.isupper() and not previous_character.isupper():
+ snake_case += f'_{character.lower()}'
+ elif character.isupper() and previous_character.isupper():
+ snake_case += character.lower()
+ elif character.isupper() and not previous_character:
+ snake_case += character.lower()
+ else:
+ snake_case += character
+
+ previous_character = character
+
+ return snake_case
+
+
def parse_csv(csv_data,
has_header_row = True,
delimiter = ',',
diff --git a/tests/input_files/series/area/01.js b/tests/input_files/series/area/01.js
index 581883b8..8e71aa29 100644
--- a/tests/input_files/series/area/01.js
+++ b/tests/input_files/series/area/01.js
@@ -97,8 +97,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
},
{
dataLabels: {
diff --git a/tests/input_files/series/area/02.js b/tests/input_files/series/area/02.js
index 5f33aafd..26053980 100644
--- a/tests/input_files/series/area/02.js
+++ b/tests/input_files/series/area/02.js
@@ -97,8 +97,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
},
{
dataLabels: {
diff --git a/tests/input_files/series/area/03.js b/tests/input_files/series/area/03.js
index e63edbbb..627dea5d 100644
--- a/tests/input_files/series/area/03.js
+++ b/tests/input_files/series/area/03.js
@@ -97,8 +97,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
},
{
dataLabels: {
diff --git a/tests/input_files/series/area/04.js b/tests/input_files/series/area/04.js
index a306bbb1..2881e907 100644
--- a/tests/input_files/series/area/04.js
+++ b/tests/input_files/series/area/04.js
@@ -99,7 +99,7 @@
symbol: 'circle',
width: 48
},
- x: 'some category'
+ name: 'some category'
},
{
dataLabels: {
@@ -200,8 +200,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
-
accessibility: {
description: 'Some description goes here',
enabled: true
diff --git a/tests/input_files/series/area/05.js b/tests/input_files/series/area/05.js
index 968184ce..1c66a646 100644
--- a/tests/input_files/series/area/05.js
+++ b/tests/input_files/series/area/05.js
@@ -99,7 +99,7 @@
symbol: 'circle',
width: 48
},
- x: 'some category'
+ name: 'some category'
},
{
dataLabels: {
@@ -200,7 +200,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
accessibility: {
description: 'Some description goes here',
diff --git a/tests/input_files/series/area/06.js b/tests/input_files/series/area/06.js
index 0c34b815..e3d06147 100644
--- a/tests/input_files/series/area/06.js
+++ b/tests/input_files/series/area/06.js
@@ -99,7 +99,7 @@
symbol: 'circle',
width: 48
},
- x: 'some category'
+ name: 'some category'
},
{
dataLabels: {
@@ -200,7 +200,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
accessibility: {
description: 'Some description goes here',
diff --git a/tests/input_files/series/bar/01.js b/tests/input_files/series/bar/01.js
index e036ae10..2f4d24c2 100644
--- a/tests/input_files/series/bar/01.js
+++ b/tests/input_files/series/bar/01.js
@@ -108,8 +108,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
id: 'some-id-goes-here',
diff --git a/tests/input_files/series/bar/02.js b/tests/input_files/series/bar/02.js
index f72af2d5..0e20db7a 100644
--- a/tests/input_files/series/bar/02.js
+++ b/tests/input_files/series/bar/02.js
@@ -97,8 +97,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
},
{
dataLabels: {
@@ -197,8 +197,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
id: 'some-id-goes-here',
diff --git a/tests/input_files/series/bar/03.js b/tests/input_files/series/bar/03.js
index a0bc1155..90910614 100644
--- a/tests/input_files/series/bar/03.js
+++ b/tests/input_files/series/bar/03.js
@@ -99,7 +99,7 @@
symbol: 'circle',
width: 48
},
- x: 'some category'
+ name: 'some category'
},
{
dataLabels: {
@@ -200,7 +200,7 @@
symbol: 'circle',
width: 48
},
- x: 'some category'
+ name: 'some category'
}
],
id: 'some-id-goes-here',
diff --git a/tests/input_files/series/bar/04.js b/tests/input_files/series/bar/04.js
index 4371a03f..d79c2867 100644
--- a/tests/input_files/series/bar/04.js
+++ b/tests/input_files/series/bar/04.js
@@ -99,8 +99,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
},
{
z: 456,
@@ -201,8 +201,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
id: 'some-id-goes-here',
diff --git a/tests/input_files/series/bar/06.js b/tests/input_files/series/bar/06.js
index a2fe38e5..3cc86c26 100644
--- a/tests/input_files/series/bar/06.js
+++ b/tests/input_files/series/bar/06.js
@@ -100,8 +100,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
},
{
direction: 45,
@@ -203,8 +203,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
id: 'some-id-goes-here',
diff --git a/tests/input_files/series/boxplot/01.js b/tests/input_files/series/boxplot/01.js
index c0c88632..6a78afea 100644
--- a/tests/input_files/series/boxplot/01.js
+++ b/tests/input_files/series/boxplot/01.js
@@ -107,8 +107,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
id: 'some-id-goes-here',
diff --git a/tests/input_files/series/boxplot/02.js b/tests/input_files/series/boxplot/02.js
index 9c20f098..813882aa 100644
--- a/tests/input_files/series/boxplot/02.js
+++ b/tests/input_files/series/boxplot/02.js
@@ -99,7 +99,7 @@
symbol: 'circle',
width: 48
},
- x: 'some category'
+ name: 'some category'
}
],
id: 'some-id-goes-here',
diff --git a/tests/input_files/series/bubble/01.js b/tests/input_files/series/bubble/01.js
index ceb74614..53010ad7 100644
--- a/tests/input_files/series/bubble/01.js
+++ b/tests/input_files/series/bubble/01.js
@@ -99,8 +99,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
diff --git a/tests/input_files/series/bullet/01.js b/tests/input_files/series/bullet/01.js
index 534acbe6..3cb7c689 100644
--- a/tests/input_files/series/bullet/01.js
+++ b/tests/input_files/series/bullet/01.js
@@ -111,8 +111,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
},
{
target: 123,
@@ -225,8 +225,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
targetOptions: {
diff --git a/tests/input_files/series/data/bar/01.js b/tests/input_files/series/data/bar/01.js
index 948790c3..75cd1a36 100644
--- a/tests/input_files/series/data/bar/01.js
+++ b/tests/input_files/series/data/bar/01.js
@@ -100,6 +100,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/bar/02.js b/tests/input_files/series/data/bar/02.js
index 5da7071c..21f9e1be 100644
--- a/tests/input_files/series/data/bar/02.js
+++ b/tests/input_files/series/data/bar/02.js
@@ -98,6 +98,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/bar/03.js b/tests/input_files/series/data/bar/03.js
index 39f881be..34033a0d 100644
--- a/tests/input_files/series/data/bar/03.js
+++ b/tests/input_files/series/data/bar/03.js
@@ -98,6 +98,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/boxplot/01.js b/tests/input_files/series/data/boxplot/01.js
index 8a454f07..85916f70 100644
--- a/tests/input_files/series/data/boxplot/01.js
+++ b/tests/input_files/series/data/boxplot/01.js
@@ -105,6 +105,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/bullet/01.js b/tests/input_files/series/data/bullet/01.js
index db7ba687..6a2249f6 100644
--- a/tests/input_files/series/data/bullet/01.js
+++ b/tests/input_files/series/data/bullet/01.js
@@ -109,6 +109,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/cartesian/01.js b/tests/input_files/series/data/cartesian/01.js
index 4073d5f0..911e6fc3 100644
--- a/tests/input_files/series/data/cartesian/01.js
+++ b/tests/input_files/series/data/cartesian/01.js
@@ -95,6 +95,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/cartesian/06.js b/tests/input_files/series/data/cartesian/06.js
index 0a3b8669..2fac1af1 100644
--- a/tests/input_files/series/data/cartesian/06.js
+++ b/tests/input_files/series/data/cartesian/06.js
@@ -97,6 +97,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/cartesian/08.js b/tests/input_files/series/data/cartesian/08.js
index 8daec479..6524417c 100644
--- a/tests/input_files/series/data/cartesian/08.js
+++ b/tests/input_files/series/data/cartesian/08.js
@@ -98,6 +98,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/range/01.js b/tests/input_files/series/data/range/01.js
index 06a4f35f..a203e2db 100644
--- a/tests/input_files/series/data/range/01.js
+++ b/tests/input_files/series/data/range/01.js
@@ -97,5 +97,5 @@
symbol: 'circle',
width: 48
},
- x: 'some category'
+ name: 'some category'
}
diff --git a/tests/input_files/series/data/vector/01.js b/tests/input_files/series/data/vector/01.js
index 2d1bbb4a..8f4bf595 100644
--- a/tests/input_files/series/data/vector/01.js
+++ b/tests/input_files/series/data/vector/01.js
@@ -98,6 +98,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
diff --git a/tests/input_files/series/dumbbell/01.js b/tests/input_files/series/dumbbell/01.js
index 7a91bb80..a0498ab0 100644
--- a/tests/input_files/series/dumbbell/01.js
+++ b/tests/input_files/series/dumbbell/01.js
@@ -101,7 +101,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
y: 123,
accessibility: {
diff --git a/tests/input_files/series/heatmap/01.js b/tests/input_files/series/heatmap/01.js
index 0bff47db..da19fe0f 100644
--- a/tests/input_files/series/heatmap/01.js
+++ b/tests/input_files/series/heatmap/01.js
@@ -100,8 +100,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
diff --git a/tests/input_files/series/heatmap/02.js b/tests/input_files/series/heatmap/02.js
index 0a42195e..28d15e64 100644
--- a/tests/input_files/series/heatmap/02.js
+++ b/tests/input_files/series/heatmap/02.js
@@ -100,8 +100,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
diff --git a/tests/input_files/series/polygon/01.js b/tests/input_files/series/polygon/01.js
index c04cecb2..d0dbf767 100644
--- a/tests/input_files/series/polygon/01.js
+++ b/tests/input_files/series/polygon/01.js
@@ -97,7 +97,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
y: 123,
accessibility: {
diff --git a/tests/input_files/series/polygon/02.js b/tests/input_files/series/polygon/02.js
index 0e23b52b..4bb02f05 100644
--- a/tests/input_files/series/polygon/02.js
+++ b/tests/input_files/series/polygon/02.js
@@ -97,7 +97,6 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
y: 123,
accessibility: {
diff --git a/tests/input_files/series/scatter/01.js b/tests/input_files/series/scatter/01.js
index fca2ceb8..b22d4790 100644
--- a/tests/input_files/series/scatter/01.js
+++ b/tests/input_files/series/scatter/01.js
@@ -97,8 +97,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
jitter: {
diff --git a/tests/input_files/series/scatter/02.js b/tests/input_files/series/scatter/02.js
index 6680e32a..6a2ce89d 100644
--- a/tests/input_files/series/scatter/02.js
+++ b/tests/input_files/series/scatter/02.js
@@ -97,8 +97,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
jitter: {
diff --git a/tests/input_files/series/spline/01.js b/tests/input_files/series/spline/01.js
index 51730b3d..79a0dd47 100644
--- a/tests/input_files/series/spline/01.js
+++ b/tests/input_files/series/spline/01.js
@@ -97,8 +97,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
colorAxis: 1,
diff --git a/tests/input_files/series/spline/02.js b/tests/input_files/series/spline/02.js
index 45f49fc8..566fa9a0 100644
--- a/tests/input_files/series/spline/02.js
+++ b/tests/input_files/series/spline/02.js
@@ -97,8 +97,8 @@
symbol: 'circle',
width: 48
},
- x: 'some category',
- y: 123
+ y: 123,
+ name: 'some category'
}
],
colorAxis: 1,
diff --git a/tests/input_files/series/vector/01.js b/tests/input_files/series/vector/01.js
index 4d739d09..f995ddd6 100644
--- a/tests/input_files/series/vector/01.js
+++ b/tests/input_files/series/vector/01.js
@@ -1,9 +1,6 @@
{
data: [
- {
- direction: 45,
- length: 12
- }
+ [0, 45, 12]
],
cluster: {
allowOverlap: true,
diff --git a/tests/input_files/series/vector/02.js b/tests/input_files/series/vector/02.js
index 192f2e69..b3758cf0 100644
--- a/tests/input_files/series/vector/02.js
+++ b/tests/input_files/series/vector/02.js
@@ -1,9 +1,6 @@
{
data: [
- {
- direction: 45,
- length: 12
- }
+ [0, 45, 12]
],
cluster: {
allowOverlap: true,
diff --git a/tests/options/series/data/test_bar.py b/tests/options/series/data/test_bar.py
index 9d4d5419..2aef2592 100644
--- a/tests/options/series/data/test_bar.py
+++ b/tests/options/series/data/test_bar.py
@@ -125,8 +125,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# + DataBase options
# Categorical X Value
@@ -232,7 +232,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -308,8 +307,8 @@ def test_BarData_from_js_literal(input_files, filename, as_file, error):
@pytest.mark.parametrize('array, expected, error', [
- ([['A', 123]], {'name': 'A', 'x': 'A', 'y': 123}, None),
- ([{'x': 'A', 'y': 123, 'name': 'A'}], {'name': 'A', 'x': 'A', 'y': 123}, None)
+ ([['A', 123]], {'name': 'A', 'y': 123}, None),
+ ([{'x': 'A', 'y': 123, 'name': 'A'}], {'name': 'A', 'y': 123}, None)
])
def test_BarData_from_array(array, expected, error):
if not expected:
@@ -325,6 +324,36 @@ def test_BarData_from_array(array, expected, error):
with pytest.raises(error):
result = cls.from_array(array)
+
+utc_now = datetime.datetime.utcnow()
+today = datetime.date.today()
+
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[123, 456], [789, 123]], {}, list, [[123, 456], [789, 123]]),
+ ([['A', 456], ['B', 123]], {}, list, [['A', 456], ['B', 123]]),
+ ([[utc_now, 456], [utc_now, 123]], {}, list, [[utc_now, 456], [utc_now, 123]]),
+ ([[today, 456], [today, 123]], {}, list, [[today, 456], [today, 123]]),
+
+ ([[123, 456], [789, 123]], {'id': 'some_id'}, dict, None),
+])
+def test_BarData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
+
## NEXT CLASS
STANDARD_PARAMS_2 = [
@@ -434,8 +463,8 @@ def test_BarData_from_array(array, expected, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# + DataBase options
# Categorical X Value
@@ -539,7 +568,6 @@ def test_BarData_from_array(array, expected, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -597,6 +625,31 @@ def test_WaterfallData_to_dict(kwargs, error):
def test_WaterfallData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls2, input_files, filename, as_file, error)
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[123, 456], [789, 123]], {}, list, [[123, 456], [789, 123]]),
+ ([['A', 456], ['B', 123]], {}, list, [['A', 456], ['B', 123]]),
+ ([[utc_now, 456], [utc_now, 123]], {}, list, [[utc_now, 456], [utc_now, 123]]),
+ ([[today, 456], [today, 123]], {}, list, [[today, 456], [today, 123]]),
+
+ ([[123, 456], [789, 123]], {'id': 'some_id'}, dict, None),
+])
+def test_WaterfallData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
## NEXT CLASS
STANDARD_PARAMS_3 = [
@@ -706,8 +759,8 @@ def test_WaterfallData_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# + DataBase options
# Categorical X Value
@@ -811,7 +864,6 @@ def test_WaterfallData_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -886,6 +938,37 @@ def test_WindBarbData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls3, input_files, filename, as_file, error)
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[123, 456, 789, 987], [789, 123, 456, 789]], {}, list, [[123, 456, 789, 987], [789, 123, 456, 789]]),
+ ([['A', 456, 789, 987], ['B', 123, 456, 789]], {}, list, [['A', 456, 789, 987], ['B', 123, 456, 789]]),
+ ([[utc_now, 456, 789, 987], [utc_now, 123, 456, 789]], {}, list, [[utc_now, 456, 789, 987], [utc_now, 123, 456, 789]]),
+ ([[today, 456, 789, 987], [today, 123, 456, 789]], {}, list, [[today, 456, 789, 987], [today, 123, 456, 789]]),
+
+ ([[123, 456, 789], [789, 123, 456]], {}, list, [[123, 456, 789], [789, 123, 456]]),
+ ([['A', 456, 789], ['B', 123, 456]], {}, list, [['A', 456, 789], ['B', 123, 456]]),
+ ([[utc_now, 456, 789], [utc_now, 123, 456]], {}, list, [[utc_now, 456, 789], [utc_now, 123, 456]]),
+ ([[today, 456, 789], [today, 123, 456]], {}, list, [[today, 456, 789], [today, 123, 456]]),
+
+ ([[123, 456, 789, 987], [789, 123, 456, 789]], {'id': 'some_id'}, dict, None),
+ ([[123, 456, 789], [789, 123, 456]], {'id': 'some_id'}, dict, None),
+])
+def test_WindBarbData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls3.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
+
## NEXT CLASS
STANDARD_PARAMS_4 = [
diff --git a/tests/options/series/data/test_base.py b/tests/options/series/data/test_base.py
index 9d89724d..b0e338aa 100644
--- a/tests/options/series/data/test_base.py
+++ b/tests/options/series/data/test_base.py
@@ -20,6 +20,11 @@ def from_array(cls, value):
cls = NonAbstractDataBase
+class RequiringJSObject(NonAbstractDataBase):
+ def _to_untrimmed_dict(self):
+ return {'someKey': 123}
+
+
STANDARD_PARAMS = [
({}, None),
({
@@ -93,3 +98,13 @@ def test_to_dict(kwargs, error):
])
def test_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls, input_files, filename, as_file, error)
+
+
+@pytest.mark.parametrize('cls, expected', [
+ (NonAbstractDataBase, False),
+ (RequiringJSObject, True),
+
+])
+def test_requires_js_object(cls, expected):
+ obj = cls()
+ assert obj.requires_js_object is expected
\ No newline at end of file
diff --git a/tests/options/series/data/test_boxplot.py b/tests/options/series/data/test_boxplot.py
index 5dace449..96a68f41 100644
--- a/tests/options/series/data/test_boxplot.py
+++ b/tests/options/series/data/test_boxplot.py
@@ -132,8 +132,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# Datetime X Value
({
@@ -466,7 +466,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -807,3 +806,37 @@ def test_BoxPlotData_to_dict(kwargs, error):
])
def test_BoxPlotData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls, input_files, filename, as_file, error)
+
+
+utc_now = datetime.datetime.utcnow()
+today = datetime.date.today()
+
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[123, 456, 789, 987, 654, 321]], {}, list, [[123, 456, 789, 987, 654, 321]]),
+ ([['A', 456, 789, 987, 654, 321], ['B', 123, 456, 789, 987, 654]], {}, list, [['A', 456, 789, 987, 654, 321], ['B', 123, 456, 789, 987, 654]]),
+ ([[utc_now, 456, 789, 987, 654, 321]], {}, list, [[utc_now, 456, 789, 987, 654, 321]]),
+ ([[today, 456, 789, 987, 654, 321]], {}, list, [[today, 456, 789, 987, 654, 321]]),
+
+ ([[456, 789, 987, 654, 321]], {}, list, [[456, 789, 987, 654, 321]]),
+ ([[456, 789, 987, 654, 321], [123, 456, 789, 987, 654]], {}, list, [[456, 789, 987, 654, 321], [123, 456, 789, 987, 654]]),
+ ([[456, 789, 987, 654, 321]], {}, list, [[456, 789, 987, 654, 321]]),
+
+ ([[123, 456, 789, 987, 654, 321]], {'id': 'some_id'}, dict, None),
+
+])
+def test_BoxPlotData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
diff --git a/tests/options/series/data/test_bullet.py b/tests/options/series/data/test_bullet.py
index 28977a9f..77ff9b16 100644
--- a/tests/options/series/data/test_bullet.py
+++ b/tests/options/series/data/test_bullet.py
@@ -142,8 +142,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# + DataBase options
# Categorical X Value
@@ -259,7 +259,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -332,3 +331,33 @@ def test_BulletData_to_dict(kwargs, error):
])
def test_BulletData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls, input_files, filename, as_file, error)
+
+
+utc_now = datetime.datetime.utcnow()
+today = datetime.date.today()
+
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[321, 123, 456], [123, 789, 123]], {}, list, [[321, 123, 456], [123, 789, 123]]),
+ ([[123, 456], [789, 123]], {}, list, [[123, 456], [789, 123]]),
+ ([['A', 123, 456], ['B', 321, 123]], {}, list, [['A', 123, 456], ['B', 321, 123]]),
+ ([[utc_now, 123, 456], [utc_now, 321, 123]], {}, list, [[utc_now, 123, 456], [utc_now, 321, 123]]),
+ ([[today, 123, 456], [today, 321, 123]], {}, list, [[today, 123, 456], [today, 321, 123]]),
+
+ ([[123, 456], [789, 123]], {'id': 'some_id'}, dict, None),
+])
+def test_BulletData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
diff --git a/tests/options/series/data/test_cartesian.py b/tests/options/series/data/test_cartesian.py
index 352295b4..6041c085 100644
--- a/tests/options/series/data/test_cartesian.py
+++ b/tests/options/series/data/test_cartesian.py
@@ -113,7 +113,7 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
+ 'name': 'some category',
'y': 123
}, None),
# Datetime X Value
@@ -746,6 +746,35 @@ def test_CartesianData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls, input_files, filename, as_file, error)
+utc_now = datetime.datetime.utcnow()
+today = datetime.date.today()
+
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[123, 456], [789, 123]], {}, list, [[123, 456], [789, 123]]),
+ ([['A', 456], ['B', 123]], {}, list, [['A', 456], ['B', 123]]),
+ ([[utc_now, 456], [utc_now, 123]], {}, list, [[utc_now, 456], [utc_now, 123]]),
+ ([[today, 456], [today, 123]], {}, list, [[today, 456], [today, 123]]),
+
+ ([[123, 456], [789, 123]], {'id': 'some_id'}, dict, None),
+])
+def test_CartesianData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
+
## NEXT CLASS
STANDARD_PARAMS_2 = [
@@ -853,8 +882,8 @@ def test_CartesianData_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# + DataBase options
# Categorical X Value
@@ -1033,6 +1062,33 @@ def test_Cartesian3DData_to_dict(kwargs, error):
def test_Cartesian3DData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls2, input_files, filename, as_file, error)
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[321, 123, 456], [123, 789, 123]], {}, list, [[321, 123, 456], [123, 789, 123]]),
+ ([[123, 456], [789, 123]], {}, list, [[123, 456], [789, 123]]),
+ ([['A', 123, 456], ['B', 321, 123]], {}, list, [['A', 123, 456], ['B', 321, 123]]),
+ ([[utc_now, 123, 456], [utc_now, 321, 123]], {}, list, [[utc_now, 123, 456], [utc_now, 321, 123]]),
+ ([[today, 123, 456], [today, 321, 123]], {}, list, [[today, 123, 456], [today, 321, 123]]),
+
+ ([[123, 456], [789, 123]], {'id': 'some_id'}, dict, None),
+])
+def test_Cartesian3DData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls2.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
+
## NEXT CLASS
STANDARD_PARAMS_3 = [
@@ -1142,8 +1198,8 @@ def test_Cartesian3DData_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# + DataBase options
# Categorical X Value
@@ -1322,3 +1378,29 @@ def test_CartesianValueData_to_dict(kwargs, error):
])
def test_CartesianValueData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls3, input_files, filename, as_file, error)
+
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[321, 123, 456], [123, 789, 123]], {}, list, [[321, 123, 456], [123, 789, 123]]),
+ ([[123, 456], [789, 123]], {}, list, [[123, 456], [789, 123]]),
+ ([['A', 123, 456], ['B', 321, 123]], {}, list, [['A', 123, 456], ['B', 321, 123]]),
+ ([[utc_now, 123, 456], [utc_now, 321, 123]], {}, list, [[utc_now, 123, 456], [utc_now, 321, 123]]),
+ ([[today, 123, 456], [today, 321, 123]], {}, list, [[today, 123, 456], [today, 321, 123]]),
+
+ ([[123, 456], [789, 123]], {'id': 'some_id'}, dict, None),
+])
+def test_CartesianValueData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls3.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
diff --git a/tests/options/series/data/test_range.py b/tests/options/series/data/test_range.py
index 56a57e4d..3e27ecaf 100644
--- a/tests/options/series/data/test_range.py
+++ b/tests/options/series/data/test_range.py
@@ -1,6 +1,7 @@
"""Tests for ``highcharts.no_data``."""
import pytest
+import datetime
from json.decoder import JSONDecodeError
@@ -112,7 +113,7 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
}, None),
# + DataBase Options
({
@@ -290,6 +291,36 @@ def test_RangeData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls, input_files, filename, as_file, error)
+utc_now = datetime.datetime.utcnow()
+today = datetime.date.today()
+
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[321, 123, 456], [123, 789, 123]], {}, list, [[321, 123, 456], [123, 789, 123]]),
+ ([[123, 456], [789, 123]], {}, list, [[123, 456], [789, 123]]),
+ ([['A', 123, 456], ['B', 321, 123]], {}, list, [['A', 123, 456], ['B', 321, 123]]),
+ ([[utc_now, 123, 456], [utc_now, 321, 123]], {}, list, [[utc_now, 123, 456], [utc_now, 321, 123]]),
+ ([[today, 123, 456], [today, 321, 123]], {}, list, [[today, 123, 456], [today, 321, 123]]),
+
+ ([[123, 456], [789, 123]], {'id': 'some_id'}, dict, None),
+])
+def test_RangeData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
+
## NEXT CLASS
STANDARD_PARAMS_2 = [
@@ -401,8 +432,8 @@ def test_RangeData_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# + DataBase Options
({
diff --git a/tests/options/series/data/test_single_point.py b/tests/options/series/data/test_single_point.py
index fc9f0a7c..ab6684ea 100644
--- a/tests/options/series/data/test_single_point.py
+++ b/tests/options/series/data/test_single_point.py
@@ -1,6 +1,7 @@
"""Tests for ``highcharts.no_data``."""
import pytest
+import datetime
from json.decoder import JSONDecodeError
@@ -506,6 +507,35 @@ def test_SinglePointData_to_dict(kwargs, error):
def test_SinglePointData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls2, input_files, filename, as_file, error)
+
+utc_now = datetime.datetime.utcnow()
+today = datetime.date.today()
+
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([['123', 456], ['789', 123]], {}, list, [['123', 456], ['789', 123]]),
+ ([['A', 456], ['B', 123]], {}, list, [['A', 456], ['B', 123]]),
+ ([[123], [456]], {}, list, [[123], [456]]),
+
+ ([['123', 456], ['789', 123]], {'id': 'some_id'}, dict, None),
+])
+def test_SinglePointData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls2.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
+
## NEXT CLASS
@@ -756,6 +786,28 @@ def test_SingleValueData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls3, input_files, filename, as_file, error)
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([123, 456], {}, list, [[123], [456]]),
+
+ ([123, 456], {'id': 'some_id'}, dict, None),
+])
+def test_SingleValueData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls3.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
+
## NEXT CLASS
@@ -1006,6 +1058,28 @@ def test_SingleXData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls4, input_files, filename, as_file, error)
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([123, 456], {}, list, [[123], [456]]),
+
+ ([123, 456], {'id': 'some_id'}, dict, None),
+])
+def test_SingleXData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls4.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
+
+
## NEXT CLASS
diff --git a/tests/options/series/data/test_vector.py b/tests/options/series/data/test_vector.py
index 18a97329..7804322e 100644
--- a/tests/options/series/data/test_vector.py
+++ b/tests/options/series/data/test_vector.py
@@ -117,8 +117,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}, None),
# Datetime X Value
({
@@ -757,3 +757,35 @@ def test_VectorData_to_dict(kwargs, error):
])
def test_VectorData_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls, input_files, filename, as_file, error)
+
+
+utc_now = datetime.datetime.utcnow()
+today = datetime.date.today()
+
+
+@pytest.mark.parametrize('input_array, set_props, expected_type, expected', [
+ ([], {}, list, []),
+ ([[123, 456, 789, 987], [789, 123, 456, 789]], {}, list, [[123, 456, 789, 987], [789, 123, 456, 789]]),
+ ([['A', 456, 789, 987], ['B', 123, 456, 789]], {}, list, [['A', 456, 789, 987], ['B', 123, 456, 789]]),
+ ([[utc_now, 456, 789, 987], [utc_now, 123, 456, 789]], {}, list, [[utc_now, 456, 789, 987], [utc_now, 123, 456, 789]]),
+ ([[today, 456, 789, 987], [today, 123, 456, 789]], {}, list, [[today, 456, 789, 987], [today, 123, 456, 789]]),
+
+ ([[123, 456, 789], [789, 123, 456]], {}, list, [[123, 456, 789], [789, 123, 456]]),
+
+ ([[123, 456, 789, 987], [789, 123, 456, 789]], {'id': 'some_id'}, dict, None),
+ ([[123, 456, 789], [789, 123, 456]], {'id': 'some_id'}, dict, None),
+])
+def test_VectorData_to_array(input_array, set_props, expected_type, expected):
+ iterable = cls.from_array(input_array)
+ for data_point in iterable:
+ for key in set_props:
+ setattr(data_point, key, set_props[key])
+
+ results = []
+ for data_point in iterable:
+ result = data_point.to_array()
+ assert isinstance(result, expected_type) is True
+ results.append(result)
+
+ if expected_type == list:
+ assert results == expected
diff --git a/tests/options/series/test_area.py b/tests/options/series/test_area.py
index 125afc61..55802229 100644
--- a/tests/options/series/test_area.py
+++ b/tests/options/series/test_area.py
@@ -119,8 +119,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -432,8 +432,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -762,8 +762,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -1501,7 +1501,7 @@ def test_AreaSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -1602,7 +1602,6 @@ def test_AreaSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'accessibility': {
'description': 'Some description goes here',
@@ -1840,7 +1839,6 @@ def test_AreaSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'accessibility': {
'description': 'Some description goes here',
@@ -1994,7 +1992,7 @@ def test_AreaSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -2095,7 +2093,6 @@ def test_AreaSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'accessibility': {
'description': 'Some description goes here',
@@ -2786,7 +2783,7 @@ def test_bugfix32_LineSeries_from_csv(kwargs, error):
assert result.data is not None
assert isinstance(result.data, list) is True
for item in result.data:
- assert item.x is not None
+ assert item.name is not None
assert item.y is not None
diff --git a/tests/options/series/test_bar.py b/tests/options/series/test_bar.py
index a7db164d..a77e7a7e 100644
--- a/tests/options/series/test_bar.py
+++ b/tests/options/series/test_bar.py
@@ -132,8 +132,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -256,8 +256,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -425,8 +425,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -1055,8 +1055,8 @@ def test_ColumnSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -1155,8 +1155,8 @@ def test_ColumnSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -1268,8 +1268,8 @@ def test_ColumnSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -1368,8 +1368,8 @@ def test_ColumnSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -1486,8 +1486,8 @@ def test_ColumnSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -1586,8 +1586,8 @@ def test_ColumnSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -1749,8 +1749,8 @@ def test_ColumnSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'data_labels': {
@@ -1849,8 +1849,8 @@ def test_ColumnSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -2374,7 +2374,7 @@ def test_ColumnPyramidSeries_from_js_literal(input_files, filename, as_file, err
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -2475,7 +2475,6 @@ def test_ColumnPyramidSeries_from_js_literal(input_files, filename, as_file, err
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'accessibility': {
'description': 'Some description goes here',
@@ -2617,7 +2616,7 @@ def test_ColumnPyramidSeries_from_js_literal(input_files, filename, as_file, err
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -2718,7 +2717,6 @@ def test_ColumnPyramidSeries_from_js_literal(input_files, filename, as_file, err
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'accessibility': {
'description': 'Some description goes here',
@@ -2905,7 +2903,7 @@ def test_ColumnPyramidSeries_from_js_literal(input_files, filename, as_file, err
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
},
{
'dataLabels': {
@@ -3006,7 +3004,6 @@ def test_ColumnPyramidSeries_from_js_literal(input_files, filename, as_file, err
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'accessibility': {
'description': 'Some description goes here',
@@ -3601,8 +3598,8 @@ def test_CylinderSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'z': 456,
@@ -3703,8 +3700,8 @@ def test_CylinderSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -3818,8 +3815,8 @@ def test_CylinderSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'z': 456,
@@ -3920,8 +3917,8 @@ def test_CylinderSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
@@ -4080,8 +4077,8 @@ def test_CylinderSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'z': 456,
@@ -4182,8 +4179,8 @@ def test_CylinderSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
'id': 'some-id-goes-here',
diff --git a/tests/options/series/test_base.py b/tests/options/series/test_base.py
index 4553d86a..9dbef12c 100644
--- a/tests/options/series/test_base.py
+++ b/tests/options/series/test_base.py
@@ -683,4 +683,28 @@ def test_load_from_pandas(input_files, filename, property_map, error):
assert getattr(item, key) is not None
else:
with pytest.raises(error):
- instance.load_from_pandas(df, property_map = property_map)
\ No newline at end of file
+ instance.load_from_pandas(df, property_map = property_map)
+
+
+@pytest.mark.parametrize('kwargs, error', STANDARD_PARAMS)
+def test__repr__(kwargs, error):
+ obj = cls(**kwargs)
+ if not error:
+ result = repr(obj)
+ if 'data' in kwargs:
+ assert 'data = ' in result
+ else:
+ with pytest.raises(error):
+ result = repr(obj)
+
+
+@pytest.mark.parametrize('kwargs, error', STANDARD_PARAMS)
+def test__str__(kwargs, error):
+ obj = cls(**kwargs)
+ if not error:
+ result = str(obj)
+ print(result)
+ assert 'data = ' not in result
+ else:
+ with pytest.raises(error):
+ result = str(obj)
\ No newline at end of file
diff --git a/tests/options/series/test_boxplot.py b/tests/options/series/test_boxplot.py
index 84ceb61a..8aaea371 100644
--- a/tests/options/series/test_boxplot.py
+++ b/tests/options/series/test_boxplot.py
@@ -653,7 +653,7 @@ def test_BoxPlotSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
},
],
'id': 'some-id-goes-here',
@@ -767,7 +767,7 @@ def test_BoxPlotSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
},
],
'id': 'some-id-goes-here',
@@ -926,7 +926,7 @@ def test_BoxPlotSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category'
+ 'name': 'some category'
},
],
'id': 'some-id-goes-here',
diff --git a/tests/options/series/test_bubble.py b/tests/options/series/test_bubble.py
index 2f2163e6..beba2bf9 100644
--- a/tests/options/series/test_bubble.py
+++ b/tests/options/series/test_bubble.py
@@ -113,8 +113,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
]
}, None),
@@ -220,8 +220,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
@@ -340,8 +340,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
@@ -546,8 +546,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
diff --git a/tests/options/series/test_dumbbell.py b/tests/options/series/test_dumbbell.py
index 8714cebb..482787d9 100644
--- a/tests/options/series/test_dumbbell.py
+++ b/tests/options/series/test_dumbbell.py
@@ -116,8 +116,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
]
}, None),
@@ -225,8 +225,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
@@ -431,8 +431,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
diff --git a/tests/options/series/test_heatmap.py b/tests/options/series/test_heatmap.py
index cae615d9..a4001463 100644
--- a/tests/options/series/test_heatmap.py
+++ b/tests/options/series/test_heatmap.py
@@ -115,7 +115,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -246,7 +245,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -383,7 +381,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -895,7 +892,6 @@ def test_HeatmapSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -1026,7 +1022,6 @@ def test_HeatmapSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -1159,7 +1154,6 @@ def test_HeatmapSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -1298,7 +1292,6 @@ def test_HeatmapSeries_from_js_literal(input_files, filename, as_file, error):
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
diff --git a/tests/options/series/test_pictorial.py b/tests/options/series/test_pictorial.py
index 6bedca56..ad18df84 100644
--- a/tests/options/series/test_pictorial.py
+++ b/tests/options/series/test_pictorial.py
@@ -112,8 +112,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category',
},
{
'dataLabels': {
@@ -420,8 +420,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
},
{
'dataLabels': {
diff --git a/tests/options/series/test_polygon.py b/tests/options/series/test_polygon.py
index 2887a60b..66f9d122 100644
--- a/tests/options/series/test_polygon.py
+++ b/tests/options/series/test_polygon.py
@@ -111,7 +111,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -239,7 +238,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
@@ -453,7 +451,6 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
'y': 123,
'accessibility': {
diff --git a/tests/options/series/test_scatter.py b/tests/options/series/test_scatter.py
index 55898834..a9da9d3d 100644
--- a/tests/options/series/test_scatter.py
+++ b/tests/options/series/test_scatter.py
@@ -112,8 +112,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
]
}, None),
@@ -217,8 +217,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
@@ -327,8 +327,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
@@ -523,8 +523,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
diff --git a/tests/options/series/test_spline.py b/tests/options/series/test_spline.py
index 7f89ad64..d63b7e93 100644
--- a/tests/options/series/test_spline.py
+++ b/tests/options/series/test_spline.py
@@ -111,8 +111,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
]
}, None),
@@ -216,8 +216,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
@@ -407,8 +407,8 @@
'symbol': 'circle',
'width': 48
},
- 'x': 'some category',
- 'y': 123
+ 'y': 123,
+ 'name': 'some category'
}
],
diff --git a/tests/options/test_options.py b/tests/options/test_options.py
index 2c4d045c..c1653bc5 100644
--- a/tests/options/test_options.py
+++ b/tests/options/test_options.py
@@ -42,3 +42,82 @@ def test_to_dict(kwargs, error):
])
def test_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls, input_files, filename, as_file, error)
+
+
+@pytest.mark.parametrize('kwargs, error', [
+ ({}, None),
+ ({
+ 'title': {
+ 'text': 'My Chart'
+ }
+ }, None),
+ ({
+ 'plot_options': {
+ 'series': {
+ 'crisp': True
+ }
+ }
+ }, None),
+ ({
+ 'series': [
+ {
+ 'crisp': True,
+ 'type': 'bar'
+ },
+ {
+ 'crisp': False,
+ 'type': 'bar'
+ }
+ ]
+ }, None),
+])
+def test__repr__(kwargs, error):
+ obj = cls(**kwargs)
+ if not error:
+ result = repr(obj)
+ if 'plot_options' in kwargs:
+ assert 'plot_options = ' in result
+ if 'series' in kwargs:
+ assert 'series = ' in result
+ else:
+ with pytest.raises(error):
+ result = repr(obj)
+
+
+@pytest.mark.parametrize('kwargs, error', [
+ ({}, None),
+ ({
+ 'title': {
+ 'text': 'My Chart'
+ }
+ }, None),
+ ({
+ 'plot_options': {
+ 'series': {
+ 'crisp': True
+ }
+ }
+ }, None),
+ ({
+ 'series': [
+ {
+ 'crisp': True,
+ 'type': 'bar'
+ },
+ {
+ 'crisp': False,
+ 'type': 'bar'
+ }
+ ]
+ }, None),
+])
+def test__str__(kwargs, error):
+ obj = cls(**kwargs)
+ if not error:
+ result = str(obj)
+ print(result)
+ assert 'plot_options = ' not in result
+ assert 'series = ' not in result
+ else:
+ with pytest.raises(error):
+ result = str(obj)
\ No newline at end of file
diff --git a/tests/test_chart.py b/tests/test_chart.py
index adbabb9f..30eb7564 100644
--- a/tests/test_chart.py
+++ b/tests/test_chart.py
@@ -144,3 +144,197 @@ def test_get_required_modules(json_str, expected_modules, error):
else:
with pytest.raises(error):
result = chart.get_required_modules()
+
+
+@pytest.mark.parametrize('options_str, as_str, expected, error', [
+ ("""{
+ "chart": {
+ "type": "column"
+ },
+ "colors": null,
+ "credits": false,
+ "exporting": {
+ "scale": 1
+ },
+ "series": [{
+ "baseSeries": 1,
+ "color": "#434343",
+ "name": "Pareto",
+ "tooltip": {
+ "valueDecimals": 2,
+ "valueSuffix": "%"
+ },
+ "type": "pareto",
+ "yAxis": 1,
+ "zIndex": 10
+ }, {
+ "color": "#7cb5ec",
+ "data": [1, 23, 45, 54, 84, 13, 8, 7, 23, 1, 34, 6, 8, 99, 85, 23, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1],
+ "name": "random-name",
+ "type": "column",
+ "zIndex": 2
+ }],
+ "title": {
+ "text": "Random Name Pareto"
+ },
+ "tooltip": {
+ "shared": true
+ },
+ "xAxis": {
+ "categories": ["Something", "Something", "Something", "Something", "Something", "Something", "Hypovolemia", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something"],
+ "crosshair": true,
+ "labels": {
+ "rotation": 90
+ }
+ },
+ "yAxis": [{
+ "title": {
+ "text": "count"
+ }
+ }, {
+ "labels": {
+ "format": "{value}%"
+ },
+ "max": 100,
+ "maxPadding": 0,
+ "min": 0,
+ "minPadding": 0,
+ "opposite": true,
+ "title": {
+ "text": "accum percent"
+ }
+ }]
+ }""",
+ False,
+ [
+ '',
+ '',
+ ''
+ ], None),
+ ("""{
+ "chart": {
+ "type": "column"
+ },
+ "colors": null,
+ "credits": false,
+ "exporting": {
+ "scale": 1
+ },
+ "series": [{
+ "baseSeries": 1,
+ "color": "#434343",
+ "name": "Pareto",
+ "tooltip": {
+ "valueDecimals": 2,
+ "valueSuffix": "%"
+ },
+ "type": "pareto",
+ "yAxis": 1,
+ "zIndex": 10
+ }, {
+ "color": "#7cb5ec",
+ "data": [1, 23, 45, 54, 84, 13, 8, 7, 23, 1, 34, 6, 8, 99, 85, 23, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1],
+ "name": "random-name",
+ "type": "column",
+ "zIndex": 2
+ }],
+ "title": {
+ "text": "Random Name Pareto"
+ },
+ "tooltip": {
+ "shared": true
+ },
+ "xAxis": {
+ "categories": ["Something", "Something", "Something", "Something", "Something", "Something", "Hypovolemia", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something", "Something"],
+ "crosshair": true,
+ "labels": {
+ "rotation": 90
+ }
+ },
+ "yAxis": [{
+ "title": {
+ "text": "count"
+ }
+ }, {
+ "labels": {
+ "format": "{value}%"
+ },
+ "max": 100,
+ "maxPadding": 0,
+ "min": 0,
+ "minPadding": 0,
+ "opposite": true,
+ "title": {
+ "text": "accum percent"
+ }
+ }]
+ }""",
+ True,
+ """\n\n""", None),
+])
+def test_get_script_tags(options_str, as_str, expected, error):
+ from highcharts_core.options import HighchartsOptions
+ options = HighchartsOptions.from_json(options_str)
+ chart = cls.from_options(options)
+
+ if not error:
+ result = chart.get_script_tags(as_str = as_str)
+ if isinstance(expected, list):
+ assert isinstance(result, list) is True
+ assert len(result) == len(expected)
+ for item in expected:
+ assert item in result
+ elif result:
+ assert result == expected
+ else:
+ assert result is None or len(result) == 0
+ else:
+ with pytest.raises(error):
+ result = chart.get_script_tags(as_str = as_str)
+
+
+@pytest.mark.parametrize('kwargs, error', [
+ ({}, None),
+ ({
+ 'container': 'my-container-name',
+ 'module_url': 'https://mycustomurl.com/',
+ 'options': {
+ 'title': {
+ 'text': 'My Chart'
+ }
+ }
+ }, None),
+])
+def test__repr__(kwargs, error):
+ obj = cls(**kwargs)
+ if not error:
+ result = repr(obj)
+ if 'options' in kwargs:
+ assert 'options = ' in result
+ else:
+ with pytest.raises(error):
+ result = repr(obj)
+
+
+@pytest.mark.parametrize('kwargs, error', [
+ ({}, None),
+ ({
+ 'container': 'my-container-name',
+ 'module_url': 'https://mycustomurl.com/',
+ 'options': {
+ 'title': {
+ 'text': 'My Chart'
+ }
+ }
+ }, None),
+])
+def test__str__(kwargs, error):
+ obj = cls(**kwargs)
+ if not error:
+ result = str(obj)
+ print(result)
+ if 'options' in kwargs:
+ assert 'options = ' in result
+ else:
+ with pytest.raises(error):
+ result = str(obj)
\ No newline at end of file
diff --git a/tests/test_metaclasses.py b/tests/test_metaclasses.py
index 0dd09726..e8198d8c 100644
--- a/tests/test_metaclasses.py
+++ b/tests/test_metaclasses.py
@@ -8,6 +8,19 @@
from json.decoder import JSONDecodeError
from validator_collection import checkers
+try:
+ import orjson as json
+ json_as_bytes = True
+except ImportError:
+ json_as_bytes = False
+ try:
+ import rapidjson as json
+ except ImportError:
+ try:
+ import simplejson as json
+ except ImportError:
+ import json
+
class TestClass(HighchartsMeta):
"""Class used to test the :class:`HighchartsMeta` functionality."""
@@ -36,9 +49,40 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict:
test_class_instance = TestClass(item1 = 123, item2 = 456)
test_class_trimmed_instance = TestClass(item1 = 123)
-test_class_iterable = TestClass(item1 = [1, 2, constants.EnforcedNullType], item2 = 456)
+test_class_iterable = TestClass(item1 = [1, 2, constants.EnforcedNull], item2 = 456)
test_class_none_iterable = TestClass(item1 = [1, None, 3], item2 = 456)
+
+class TestClassCamelCase(TestClass):
+ def __init__(self, **kwargs):
+ self.camel_case_item = kwargs.get('camel_case_item', None)
+
+ super().__init__(**kwargs)
+
+ @classmethod
+ def _get_kwargs_from_dict(cls, as_dict):
+ kwargs = {
+ 'item1': as_dict.get('item1', None),
+ 'item2': as_dict.get('item2', None),
+ 'camel_case_item': as_dict.get('camelCaseItem', None),
+ }
+
+ return kwargs
+
+ def _to_untrimmed_dict(self, in_cls = None) -> dict:
+ return {
+ 'item1': self.item1,
+ 'item2': self.item2,
+ 'camelCaseItem': self.camel_case_item
+ }
+
+test_class_camel_case_instance = TestClassCamelCase(item1 = 123, item2 = 456, camel_case_item = 'test')
+test_class_camel_case_trimmed_instance = TestClassCamelCase(item1 = 123, camel_case_item = 'test')
+test_class_camel_case_iterable = TestClassCamelCase(item1 = [1, 2, constants.EnforcedNull], item2 = 456, camel_case_item = 'test')
+test_class_camel_case__none_iterable = TestClassCamelCase(item1 = [1, None, 3], item2 = 456, camel_case_item = 'test')
+
+
+
@pytest.mark.parametrize('kwargs, error', [
({'item1': 123,
'item2': 456},
@@ -333,3 +377,122 @@ def test_from_js_literal(cls, as_str, error):
else:
with pytest.raises(error):
result = cls.from_js_literal(as_str)
+
+@pytest.mark.parametrize('error', [
+ (None),
+])
+def test_to_json_with_timestamp(error):
+ from datetime import datetime
+ import json
+
+ try:
+ from pandas import Timestamp
+ import_successful = True
+ except ImportError:
+ import_successful = False
+
+ if import_successful:
+ class ClassWithTimestamp(HighchartsMeta):
+ @property
+ def timestamp_value(self):
+ return Timestamp(datetime.utcnow())
+
+ def _to_untrimmed_dict(self, in_cls=None) -> dict:
+ return {
+ 'timestamp_value': self.timestamp_value
+ }
+
+ @classmethod
+ def _get_kwargs_from_dict(cls, as_dict):
+ return {}
+
+ if not error:
+ obj = ClassWithTimestamp()
+ result = obj.to_json()
+ if json_as_bytes:
+ assert b'timestamp_value' in result
+ else:
+ assert 'timestamp_value' in result
+ as_dict = json.loads(result)
+ assert 'timestamp_value' in as_dict
+ assert checkers.is_numeric(as_dict['timestamp_value']) is True
+ else:
+ with pytest.raises(error):
+ obj = ClassWithTimestamp()
+
+
+@pytest.mark.parametrize('error', [
+ (None),
+])
+def test_to_json_with_date(error):
+ from datetime import datetime, date
+ import json
+
+ class ClassWithDate(HighchartsMeta):
+ @property
+ def date_value(self):
+ return date.today()
+
+ def _to_untrimmed_dict(self, in_cls=None) -> dict:
+ return {
+ 'date_value': self.date_value
+ }
+
+ @classmethod
+ def _get_kwargs_from_dict(cls, as_dict):
+ return {}
+
+ if not error:
+ obj = ClassWithDate()
+ result = obj.to_json()
+ if json_as_bytes:
+ assert b'date_value' in result
+ else:
+ assert 'date_value' in result
+ as_dict = json.loads(result)
+ assert 'date_value' in as_dict
+ assert checkers.is_string(as_dict['date_value']) is True
+ else:
+ with pytest.raises(error):
+ obj = ClassWithDate()
+
+
+@pytest.mark.parametrize('instance, expected', [
+ (test_class_camel_case_instance, "TestClassCamelCase(item1 = 123, item2 = 456, camel_case_item = 'test')"),
+ (constants.EnforcedNull, "EnforcedNullType()"),
+ (test_class_camel_case_iterable, "TestClassCamelCase(item1 = [1, 2, 'null'], item2 = 456, camel_case_item = 'test')"),
+])
+def test__repr__(instance, expected):
+ result = repr(instance)
+ assert result == expected
+
+
+class ClassWithEnforcedNull(HighchartsMeta):
+ @property
+ def enforced_null_value(self):
+ return constants.EnforcedNull
+
+ def _to_untrimmed_dict(self, in_cls=None) -> dict:
+ return {
+ 'enforced_null_value': self.enforced_null_value
+ }
+
+ @classmethod
+ def _get_kwargs_from_dict(cls, as_dict):
+ return {}
+
+
+def test_enforced_null_to_dict():
+ obj = ClassWithEnforcedNull()
+ result = obj.to_dict()
+ assert 'enforced_null_value' in result
+ assert isinstance(result['enforced_null_value'], constants.EnforcedNullType) is True
+
+
+def test_enforced_null_to_json():
+ obj = ClassWithEnforcedNull()
+ result = obj.to_json()
+ if json_as_bytes:
+ assert result == b'{"enforced_null_value":null}'
+ else:
+ assert result == '{"enforced_null_value": null}'
\ No newline at end of file
diff --git a/tests/test_utility_functions.py b/tests/test_utility_functions.py
index e7f47d76..8c6cdc50 100644
--- a/tests/test_utility_functions.py
+++ b/tests/test_utility_functions.py
@@ -22,4 +22,18 @@ def test_parse_csv(kwargs, expected_column_names, expected_records, error):
assert len(records_as_dicts) == expected_records
else:
with pytest.raises(error):
- result = utility_functions.parse_csv(**kwargs)
\ No newline at end of file
+ result = utility_functions.parse_csv(**kwargs)
+
+
+@pytest.mark.parametrize('camelCase, expected, error', [
+ ('camelCase', 'camel_case', None),
+ ('camelCaseURL', 'camel_case_url', None),
+ ('camel123Case', 'camel123_case', None),
+])
+def test_to_snake_case(camelCase, expected, error):
+ if not error:
+ result = utility_functions.to_snake_case(camelCase)
+ assert result == expected
+ else:
+ with pytest.raises(error):
+ result = utility_functions.to_snake_case(camelCase)