From 2d6315d91ee26f049c12b1230b01688485db7600 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 21:37:09 -0400 Subject: [PATCH 01/12] Updated docs. --- README.rst | 4 ++-- docs/index.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index b89749a..3aabb1d 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,7 @@ visualization library, with full integration into the robust Python ecosystem, i dataframe. * ...and even more use-case specific integrations across the broader toolkit. -The library supports Highcharts (JS) v.10.2 and higher, including Highcharts (JS) v.11.3.0. +The library supports Highcharts (JS) v.10.2 and higher, including Highcharts (JS) v.11.4.0. **COMPLETE DOCUMENTATION:** https://core-docs.highchartspython.com/en/latest/index.html @@ -291,7 +291,7 @@ Hello World, and Basic Usage # EXAMPLE 1. # Using dicts my_chart.title = { - 'align': 'center' + 'align': 'center', 'floating': True, 'text': 'The Title for My Chart', 'use_html': False, diff --git a/docs/index.rst b/docs/index.rst index 9072ee9..63f368a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,7 +37,7 @@ Highcharts Core for Python .. sidebar:: Version Compatibility - **Latest Highcharts (JS) version supported:** v.11.3.0 + **Latest Highcharts (JS) version supported:** v.11.4.0 **Highcharts Core for Python** is designed to be compatible with: From 6d64a60f2eff3e0b5b392fd80b2d504af2879a06 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 21:37:17 -0400 Subject: [PATCH 02/12] Bumped version number. --- highcharts_core/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/highcharts_core/__version__.py b/highcharts_core/__version__.py index bcd8d54..0e1a38d 100644 --- a/highcharts_core/__version__.py +++ b/highcharts_core/__version__.py @@ -1 +1 @@ -__version__ = '1.6.0' +__version__ = '1.7.0' From 432c3b5a608236c417744c4049b66778e9cd6f83 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 21:47:30 -0400 Subject: [PATCH 03/12] Added Accessibility.high_contrast_mode. --- .../options/accessibility/__init__.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/highcharts_core/options/accessibility/__init__.py b/highcharts_core/options/accessibility/__init__.py index bce14b9..6866edd 100644 --- a/highcharts_core/options/accessibility/__init__.py +++ b/highcharts_core/options/accessibility/__init__.py @@ -40,6 +40,7 @@ def __init__(self, **kwargs): self._custom_components = None self._description = None self._enabled = True + self._high_contrast_mode = None self._high_contrast_theme = None self._keyboard_navigation = None self._landmark_verbosity = None @@ -53,6 +54,7 @@ def __init__(self, **kwargs): self.custom_components = kwargs.get('custom_components', None) self.description = kwargs.get('description', None) self.enabled = kwargs.get('enabled', None) + self.high_contrast_mode = kwargs.get('high_contrast_mode', None) self.high_contrast_theme = kwargs.get('high_contrast_theme', None) self.keyboard_navigation = kwargs.get('keyboard_navigation', None) self.landmark_verbosity = kwargs.get('landmark_verbosity', None) @@ -174,6 +176,25 @@ def enabled(self, value): else: self._enabled = bool(value) + @property + def high_contrast_mode(self) -> Optional[str]: + """Controls how + :meth:`.high_contrast_theme ` + is applied. + + .. note:: + + Defaults to ``'auto'``, which applies the high contrast theme when the user's system has a + high contrast theme active. + + :rtype: :class:`str ` or :obj:`None ` + """ + return self._high_contrast_mode + + @high_contrast_mode.setter + def high_contrast_mode(self, value): + self._high_contrast_mode = validators.string(value, allow_empty = True) + @property def high_contrast_theme(self) -> Any: """Theme to apply to the chart when Windows High Contrast Mode is detected. @@ -356,6 +377,7 @@ def _get_kwargs_from_dict(cls, as_dict): 'custom_components': as_dict.get('customComponents', None), 'description': as_dict.get('description', None), 'enabled': as_dict.get('enabled', None), + 'high_contrast_mode': as_dict.get('highContrastMode', None), 'high_contrast_theme': as_dict.get('highContrastTheme', None), 'keyboard_navigation': as_dict.get('keyboardNavigation', None), 'landmark_verbosity': as_dict.get('landmarkVerbosity', None), @@ -374,6 +396,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict: 'customComponents': self.custom_components, 'description': self.description, 'enabled': self.enabled, + 'highContrastMode': self.high_contrast_mode, 'highContrastTheme': self.high_contrast_theme, 'keyboardNavigation': self.keyboard_navigation, 'landmarkVerbosity': self.landmark_verbosity, From 354af377bfc63a4200220d2ea7d5c1446181a090 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 21:50:04 -0400 Subject: [PATCH 04/12] Implemented OrganizationOptions.hanging_side. --- .../options/plot_options/organization.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/highcharts_core/options/plot_options/organization.py b/highcharts_core/options/plot_options/organization.py index ab34c85..2b7743e 100644 --- a/highcharts_core/options/plot_options/organization.py +++ b/highcharts_core/options/plot_options/organization.py @@ -36,6 +36,7 @@ class OrganizationOptions(BarOptions): def __init__(self, **kwargs): self._hanging_indent = None self._hanging_indent_translation = None + self._hanging_side = None self._levels = None self._link_color = None self._link_line_width = None @@ -48,6 +49,7 @@ def __init__(self, **kwargs): self.hanging_indent = kwargs.get('hanging_indent', None) self.hanging_indent_translation = kwargs.get('hanging_indent_translation', None) + self.hanging_side = kwargs.get('hanging_side', None) self.levels = kwargs.get('levels', None) self.link_color = kwargs.get('link_color', None) self.link_line_width = kwargs.get('link_line_width', None) @@ -138,6 +140,36 @@ def hanging_indent_translation(self, value): self._hanging_indent_translation = value + @property + def hanging_side(self) -> Optional[str]: + """Whether links connecting hanging nodes should be drawn on the left or right side. + + Accepts ``'left'`` or ``'right'``. Defaults to ``'left'``. + + .. tip:: + + Useful for RTL (Right-to-Left) layouts. + + .. note:: + + Only affects inverted (vertical) charts. + + :rtype: :class:`str ` or :obj:`None ` + """ + return self._hanging_side + + @hanging_side.setter + def hanging_side(self, value): + if not value: + self._hanging_side = None + else: + value = validators.string(value, allow_empty = False) + value = value.lower() + if value not in ['left', 'right']: + raise errors.HighchartsValueError(f'hanging_side expects a value of "left", ' + f'or "right". Received: {value}') + self._hanging_side = value + @property def levels(self) -> Optional[List[LevelOptions]]: """Set options on specific levels. Takes precedence over series options, but not @@ -380,6 +412,7 @@ def _get_kwargs_from_dict(cls, as_dict): 'hanging_indent': as_dict.get('hangingIndent', None), 'hanging_indent_translation': as_dict.get('hangingIndentTranslation', None), + 'hanging_side': as_dict.get('hangingSide', None), 'levels': as_dict.get('levels', None), 'link_color': as_dict.get('linkColor', None), 'link_line_width': as_dict.get('linkLineWidth', None), @@ -397,6 +430,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict: untrimmed = { 'hangingIndent': self.hanging_indent, 'hangingIndentTranslation': self.hanging_indent_translation, + 'hangingSide': self.hanging_side, 'levels': self.levels, 'linkColor': self.link_color, 'linkLineWidth': self.link_line_width, From ac1fc1f5c8bf1e564ca2dd2328d7ef31f9f90c27 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 21:57:37 -0400 Subject: [PATCH 05/12] Implemented SankeyOptions.node_distance --- .../options/plot_options/sankey.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/highcharts_core/options/plot_options/sankey.py b/highcharts_core/options/plot_options/sankey.py index 107c802..25643d0 100644 --- a/highcharts_core/options/plot_options/sankey.py +++ b/highcharts_core/options/plot_options/sankey.py @@ -1,4 +1,5 @@ from typing import Optional +from decimal import Decimal from validator_collection import validators @@ -36,9 +37,11 @@ class SankeyOptions(DependencyWheelOptions): def __init__(self, **kwargs): self._link_color_mode = None self._node_alignment = None + self._node_distance = None self.link_color_mode = kwargs.get('link_color_mode', None) self.node_alignment = kwargs.get('node_alignment', None) + self.node_distance = kwargs.get('node_distance', None) super().__init__(**kwargs) @@ -104,6 +107,44 @@ def node_alignment(self, value): f'"bottom". Received "{value}"') self._node_alignment = value + @property + def node_distance(self) -> Optional[str | int | float | Decimal]: + """The distance between nodes in a sankey diagram in the longitudinal direction. + Defaults to ``30``. + + .. note:: + + The longitudinal direction means the direction that the chart flows - in a + horizontal chart the distance is horizontal, in an inverted chart (vertical), + the distance is vertical. + + If a number is given, it denotes pixels. If a percentage string is given, the + distance is a percentage of the rendered node width. A value of 100% will render + equal widths for the nodes and the gaps between them. + + .. note:: + + This option applies only when the ``.node_width`` option is ``'auto'``, making + the node width respond to the number of columns. + + :rtype: :class:`str ` or numeric or :obj:`None ` + """ + return self._node_distance + + @node_distance.setter + def node_distance(self, value): + if value is None: + self._node_distance = None + else: + try: + value = validators.string(value) + if "%" not in value: + raise ValueError + except (TypeError, ValueError): + value = validators.numeric(value) + + self._node_distance = value + @classmethod def _get_kwargs_from_dict(cls, as_dict): kwargs = { @@ -159,6 +200,7 @@ def _get_kwargs_from_dict(cls, as_dict): 'link_color_mode': as_dict.get('linkColorMode', None), 'node_alignment': as_dict.get('nodeAlignment', None), + 'node_distance': as_dict.get('nodeDistance', None), } return kwargs @@ -167,6 +209,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict: untrimmed = { 'linkColorMode': self.link_color_mode, 'nodeAlignment': self.node_alignment, + 'nodeDistance': self.node_distance, } parent_as_dict = super()._to_untrimmed_dict(in_cls = in_cls) From 4c1272652e9ee2e9c6969de725d1fda767901430 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 22:00:47 -0400 Subject: [PATCH 06/12] Implemented TreegraphOptions.node_distance. --- .../options/plot_options/treegraph.py | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/highcharts_core/options/plot_options/treegraph.py b/highcharts_core/options/plot_options/treegraph.py index a1a76b4..e5dc8dd 100644 --- a/highcharts_core/options/plot_options/treegraph.py +++ b/highcharts_core/options/plot_options/treegraph.py @@ -122,6 +122,7 @@ def __init__(self, **kwargs): self._traverse_up_button = None self._levels = None + self._node_distance = None self.animation_limit = kwargs.get('animation_limit', None) self.boost_blending = kwargs.get('boost_blending', None) @@ -148,6 +149,7 @@ def __init__(self, **kwargs): self.reversed = kwargs.get('reversed', None) self.levels = kwargs.get('levels', None) + self.node_distance = kwargs.get('node_distance', None) super().__init__(**kwargs) @@ -649,7 +651,45 @@ def levels(self) -> Optional[List[TreegraphLevelOptions]]: @class_sensitive(TreegraphLevelOptions) def levels(self, value): self._levels = value - + + @property + def node_distance(self) -> Optional[str | int | float | Decimal]: + """The distance between nodes in a treegraph diagram in the longitudinal + direction. Defaults to ``30``. + + .. note:: + + The longitudinal direction means the direction that the chart flows - in a + horizontal chart the distance is horizontal, in an inverted chart (vertical), + the distance is vertical. + + If a number is given, it denotes pixels. If a percentage string is given, the + distance is a percentage of the rendered node width. A value of 100% will render + equal widths for the nodes and the gaps between them. + + .. note:: + + This option applies only when the ``.node_width`` option is ``'auto'``, making + the node width respond to the number of columns. + + :rtype: :class:`str ` or numeric or :obj:`None ` + """ + return self._node_distance + + @node_distance.setter + def node_distance(self, value): + if value is None: + self._node_distance = None + else: + try: + value = validators.string(value) + if "%" not in value: + raise ValueError + except (TypeError, ValueError): + value = validators.numeric(value) + + self._node_distance = value + @classmethod def _get_kwargs_from_dict(cls, as_dict): """Convenience method which returns the keyword arguments used to initialize the @@ -723,6 +763,7 @@ class from a Highcharts Javascript-compatible :class:`dict ` object 'link': as_dict.get('link', None), 'reversed': as_dict.get('reversed', None), 'levels': as_dict.get('levels', None), + 'node_distance': as_dict.get('nodeDistance', None), } return kwargs @@ -787,6 +828,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict: 'link': self.link, 'reversed': self.reversed, 'levels': self.levels, + 'nodeDistance': self.node_distance, } return untrimmed From afd2318fb094ef7137ca0ab8350a62e270c8f759 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 22:15:39 -0400 Subject: [PATCH 07/12] Implemented diagram series .node_width properties. --- .../options/plot_options/arcdiagram.py | 22 ++++++++++-- .../options/plot_options/dependencywheel.py | 24 +++++++++---- .../options/plot_options/sankey.py | 28 +++++++++++++++ .../options/plot_options/treegraph.py | 35 +++++++++++++++++++ 4 files changed, 100 insertions(+), 9 deletions(-) diff --git a/highcharts_core/options/plot_options/arcdiagram.py b/highcharts_core/options/plot_options/arcdiagram.py index 9b67918..bd7e601 100644 --- a/highcharts_core/options/plot_options/arcdiagram.py +++ b/highcharts_core/options/plot_options/arcdiagram.py @@ -221,16 +221,32 @@ def min_link_width(self, value): minimum = 0) @property - def node_width(self) -> Optional[int | float | Decimal]: + def node_width(self) -> Optional[str | int | float | Decimal]: """The pixel width of each node in a sankey diagram or dependency wheel, or the - height in case the chart is inverted. Defaults to ``20``. + height in case the chart is inverted. - :rtype: numeric or :obj:`None ` + Can be a number or a percentage string. + + Defaults to ``20``. + + :rtype: :class:`str ` or numeric or :obj:`None ` """ return self._node_width @node_width.setter def node_width(self, value): + if value is None: + self._node_width = None + else: + try: + value = validators.string(value) + if "%" not in value: + raise ValueError + except (TypeError, ValueError): + value = validators.numeric(value) + + self._node_width = value + self._node_width = validators.numeric(value, allow_empty = True, minimum = 0) diff --git a/highcharts_core/options/plot_options/dependencywheel.py b/highcharts_core/options/plot_options/dependencywheel.py index 2afa437..3b39df4 100644 --- a/highcharts_core/options/plot_options/dependencywheel.py +++ b/highcharts_core/options/plot_options/dependencywheel.py @@ -277,19 +277,31 @@ def node_padding(self, value): allow_empty = True) @property - def node_width(self) -> Optional[int | float | Decimal]: + def node_width(self) -> Optional[str | int | float | Decimal]: """The pixel width of each node in a sankey diagram or dependency wheel, or the - height in case the chart is inverted. Defaults to ``20``. + height in case the chart is inverted. - :rtype: numeric or :obj:`None ` + Can be a number or a percentage string. + + Defaults to ``20``. + + :rtype: :class:`str ` or numeric or :obj:`None ` """ return self._node_width @node_width.setter def node_width(self, value): - self._node_width = validators.numeric(value, - allow_empty = True, - minimum = 0) + if value is None: + self._node_width = None + else: + try: + value = validators.string(value) + if "%" not in value: + raise ValueError + except (TypeError, ValueError): + value = validators.numeric(value) + + self._node_width = value @property def start_angle(self) -> Optional[int | float | Decimal]: diff --git a/highcharts_core/options/plot_options/sankey.py b/highcharts_core/options/plot_options/sankey.py index 25643d0..17b03aa 100644 --- a/highcharts_core/options/plot_options/sankey.py +++ b/highcharts_core/options/plot_options/sankey.py @@ -145,6 +145,34 @@ def node_distance(self, value): self._node_distance = value + @property + def node_width(self) -> Optional[str | int | float | Decimal]: + """The pixel width of each node in a sankey diagram, or the height in case + the chart is inverted. Defaults to ``20``. + + Can be a number, a percentage string, or ``'auto'``. If ``'auto'``, the nodes + are sized to fill up the plot area in the longitudinal direction, regardless + of the number of levels. + + :rtype: :class:`str ` or numeric or :obj:`None ` + """ + return self._node_width + + @node_width.setter + def node_width(self, value): + if value is None: + self._node_width = None + else: + try: + value = validators.string(value) + value = value.lower() + if value != 'auto' and "%" not in value: + raise ValueError + except (TypeError, ValueError): + value = validators.numeric(value) + + self._node_width = value + @classmethod def _get_kwargs_from_dict(cls, as_dict): kwargs = { diff --git a/highcharts_core/options/plot_options/treegraph.py b/highcharts_core/options/plot_options/treegraph.py index e5dc8dd..dbbed14 100644 --- a/highcharts_core/options/plot_options/treegraph.py +++ b/highcharts_core/options/plot_options/treegraph.py @@ -123,6 +123,7 @@ def __init__(self, **kwargs): self._levels = None self._node_distance = None + self._node_width = None self.animation_limit = kwargs.get('animation_limit', None) self.boost_blending = kwargs.get('boost_blending', None) @@ -150,6 +151,7 @@ def __init__(self, **kwargs): self.levels = kwargs.get('levels', None) self.node_distance = kwargs.get('node_distance', None) + self.node_width = kwargs.get('node_width', None) super().__init__(**kwargs) @@ -690,6 +692,37 @@ def node_distance(self, value): self._node_distance = value + @property + def node_width(self) -> Optional[str | int | float | Decimal]: + """The pixel width of each node in a treegraph, or the height in case the chart is + inverted. Defaults to :obj:`None `. + + For tree graphs, the node width is only applied if the marker symbol is ``'rect'``, + otherwise the marker sizing options apply. + + Can be a number or a percentage string, or ``'auto'``. If ``'auto'``, the nodes are + sized to fill up the plot area in the longitudinal direction, regardless of the + number of levels. + + :rtype: :class:`str ` or numeric or :obj:`None ` + """ + return self._node_width + + @node_width.setter + def node_width(self, value): + if value is None: + self._node_width = None + else: + try: + value = validators.string(value) + value = value.lower() + if value != "auto" and "%" not in value: + raise ValueError + except (TypeError, ValueError): + value = validators.numeric(value) + + self._node_width = value + @classmethod def _get_kwargs_from_dict(cls, as_dict): """Convenience method which returns the keyword arguments used to initialize the @@ -764,6 +797,7 @@ class from a Highcharts Javascript-compatible :class:`dict ` object 'reversed': as_dict.get('reversed', None), 'levels': as_dict.get('levels', None), 'node_distance': as_dict.get('nodeDistance', None), + 'node_width': as_dict.get('nodeWidth', None), } return kwargs @@ -829,6 +863,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict: 'reversed': self.reversed, 'levels': self.levels, 'nodeDistance': self.node_distance, + 'nodeWidth': self.node_width, } return untrimmed From 9ef5e28693afcb654c771559b9d1a1056a7bf7f5 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 22:24:45 -0400 Subject: [PATCH 08/12] Implemented NodeOptions.height support. --- highcharts_core/utility_classes/nodes.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/highcharts_core/utility_classes/nodes.py b/highcharts_core/utility_classes/nodes.py index c4bd00d..bcf1f3c 100644 --- a/highcharts_core/utility_classes/nodes.py +++ b/highcharts_core/utility_classes/nodes.py @@ -20,6 +20,7 @@ def __init__(self, **kwargs): self._color = None self._color_index = None self._data_labels = None + self._height = None self._id = None self._name = None self._offset_horizontal = None @@ -28,6 +29,7 @@ def __init__(self, **kwargs): self.color = kwargs.get('color', None) self.color_index = kwargs.get('color_index', None) self.data_labels = kwargs.get('data_labels', None) + self.height = kwargs.get('height', None) self.id = kwargs.get('id', None) self.name = kwargs.get('name', None) self.offset_horizontal = kwargs.get('offset_horizontal', None) @@ -76,6 +78,18 @@ def data_labels(self) -> Optional[DataLabel]: def data_labels(self, value): self._data_labels = value + @property + def height(self) -> Optional[int | float | Decimal]: + """The height of the node. + + :rtype: numeric or :obj:`None ` + """ + return self._height + + @height.setter + def height(self, value): + self._height = validators.numeric(value, allow_empty=True) + @property def id(self) -> Optional[str]: """The id of the auto-generated node, refering to the ``from`` or ``to`` setting @@ -173,6 +187,7 @@ def _get_kwargs_from_dict(cls, as_dict): 'color': as_dict.get('color', None), 'color_index': as_dict.get('colorIndex', None), 'data_labels': as_dict.get('dataLabels', None), + 'height': as_dict.get('height', None), 'id': as_dict.get('id', None), 'name': as_dict.get('name', None), 'offset_horizontal': as_dict.get('offsetHorizontal', None), @@ -186,6 +201,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict: 'color': self.color, 'colorIndex': self.color_index, 'dataLabels': self.data_labels, + 'height': self.height, 'id': self.id, 'name': self.name, 'offsetHorizontal': self.offset_horizontal, @@ -263,6 +279,7 @@ def _get_kwargs_from_dict(cls, as_dict): 'color': as_dict.get('color', None), 'color_index': as_dict.get('colorIndex', None), 'data_labels': as_dict.get('dataLabels', None), + 'height': as_dict.get('height', None), 'id': as_dict.get('id', None), 'name': as_dict.get('name', None), 'offset_horizontal': as_dict.get('offsetHorizontal', None), @@ -379,6 +396,7 @@ def _get_kwargs_from_dict(cls, as_dict): 'color': as_dict.get('color', None), 'color_index': as_dict.get('colorIndex', None), 'data_labels': as_dict.get('dataLabels', None), + 'height': as_dict.get('height', None), 'id': as_dict.get('id', None), 'name': as_dict.get('name', None), 'offset_horizontal': as_dict.get('offsetHorizontal', None), From 8a7c606f8c9ee5ea7065bb5c27763ae9912919b9 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sat, 6 Apr 2024 22:56:31 -0400 Subject: [PATCH 09/12] Updated changelog. --- CHANGES.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 122a6b5..a170d1b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,21 @@ Release 1.6.0 ========================================= +* **ENHANCEMENT:** Align the API to **Highcharts (JS) v.11.4** (#163). In particular, this includes: + + * Added ``Accessibility.high_contrast_mode`` support. + * Added ``OrganizationOptions.hanging_side`` support. + * Added ``SankeyOptions.node_distance`` support. + * Added ``TreegraphOptions.node_distance`` support. + * Adjusted diagram (``ArcDiagramOptions``, ``TreegraphOptions``, ``DependencyWheelOptions``, and + ``SankeyOptions``) ``.node_width`` support and documentation. + * Added ``NodeOptions.height`` support. + +-------------------- + +Release 1.6.0 +========================================= + * **ENHANCEMENT:** Align the API to **Highcharts (JS) v.11.3** (#146). In particular, this includes: * Added ``ChartOptions.axis_layout_runs`` property. From fc69697d151f486d15450bf631ceea333be00c52 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sun, 7 Apr 2024 12:59:28 -0400 Subject: [PATCH 10/12] Implemented unit tests for bug 162. --- tests/input_files/series/base/bugfix162.csv | 5 +++++ tests/options/series/test_base.py | 25 ++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/input_files/series/base/bugfix162.csv diff --git a/tests/input_files/series/base/bugfix162.csv b/tests/input_files/series/base/bugfix162.csv new file mode 100644 index 0000000..99d438e --- /dev/null +++ b/tests/input_files/series/base/bugfix162.csv @@ -0,0 +1,5 @@ +task_id,name,start,end,parent,dependency +1,DoThing1,2023-01-01,2023-02-01,, +2,DoThing1-sub1,2023-01-11,2023-01-14,1, +3,DoThing1-sub2,2023-01-01,2023-02-01,1,2 +4,DoThing2,2023-02-02,2023-03-01,, \ No newline at end of file diff --git a/tests/options/series/test_base.py b/tests/options/series/test_base.py index b6230fd..7f1bf47 100644 --- a/tests/options/series/test_base.py +++ b/tests/options/series/test_base.py @@ -923,4 +923,27 @@ def test_convert_to(input_files, filename, target_type, as_cls, error): converted = original.convert_to(target_type) assert converted is not None assert isinstance(converted, SeriesBase) - assert isinstance(converted, target_type_cls) \ No newline at end of file + assert isinstance(converted, target_type_cls) + + +def test_bugfix162(input_files): + from highcharts_core.chart import Chart + from highcharts_core.options import HighchartsOptions + from highcharts_core.options.series.area import LineSeries + + import pandas as pd + from collections import defaultdict + + filename = 'series/base/bugfix162.csv' + input_file = check_input_file(input_files, filename) + + dtype = defaultdict(lambda: "str") + + df = pd.read_csv(input_file, dtype=dtype, parse_dates=["start", "end"]) + + # Creating a Series from the DataFrame + my_series = LineSeries.from_pandas( + df, property_map={"id": "task_id", "name": "name", "start": "start", "end": "end"} + ) + + assert my_series is not None \ No newline at end of file From 87b0c8a2bdfe04ac37da67de5d1fa14c04e09f6c Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sun, 7 Apr 2024 12:59:46 -0400 Subject: [PATCH 11/12] Implemented utility_functions.datetime64_to_datetime. --- highcharts_core/utility_functions.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/highcharts_core/utility_functions.py b/highcharts_core/utility_functions.py index a347b16..93c1b79 100644 --- a/highcharts_core/utility_functions.py +++ b/highcharts_core/utility_functions.py @@ -1,5 +1,6 @@ """Collection of utility functions used across the library.""" import csv +import datetime import os import string import random @@ -907,4 +908,29 @@ def dict_to_ndarray(as_dict): columns = [as_dict[key] for key in as_dict] as_ndarray = np.column_stack(columns) - return as_ndarray \ No newline at end of file + return as_ndarray + + +def datetime64_to_datetime(dt64): + """Convert a NumPy :class:`datetime64 ` to a Python + :class:`datetime `. + + :param dt64: The NumPy :class:`datetime64 ` to convert. + :type dt64: :class:`numpy.datetime64 ` + + :returns: A Python :class:`datetime ` instance. + :rtype: :class:`datetime ` + + :raises HighchartsDependencyError: if NumPy is not available in the runtime + environment + + """ + if not HAS_NUMPY: + raise errors.HighchartsDependencyError('NumPy is required for this feature. ' + 'It was not found in your runtime ' + 'environment. Please make sure it is ' + 'installed in your runtime ' + 'environment.') + timestamp = (dt64 - np.datetime64("1970-01-01T00:00:00")) / np.timedelta64(1, "s") + + return datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc) \ No newline at end of file From c8df7b87db98217f060e9b0eb870d198ee108fd9 Mon Sep 17 00:00:00 2001 From: Chris Modzelewski Date: Sun, 7 Apr 2024 12:59:55 -0400 Subject: [PATCH 12/12] Updated changelog. --- CHANGES.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index a170d1b..38eee4f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,9 +1,8 @@ -Release 1.6.0 +Release 1.7.0 ========================================= * **ENHANCEMENT:** Align the API to **Highcharts (JS) v.11.4** (#163). In particular, this includes: - * Added ``Accessibility.high_contrast_mode`` support. * Added ``OrganizationOptions.hanging_side`` support. * Added ``SankeyOptions.node_distance`` support. @@ -12,6 +11,9 @@ Release 1.6.0 ``SankeyOptions``) ``.node_width`` support and documentation. * Added ``NodeOptions.height`` support. +* **ENHANCEMENT:** Added ``utility_functions.datetime64_to_datetime()`` function to convert + ``numpy.datetime64`` to ``datetime.datetime`` (needed to close #162). + -------------------- Release 1.6.0