diff --git a/CHANGES.rst b/CHANGES.rst index 761b3d1f..122a6b55 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,30 @@ +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. + * Added ``ColorAxis.height`` property. + * Added ``ColorAxis.width`` property. + * Added ``Data.column_types`` property. + * Added ``Exporting.fetch_options`` property. + * Implemented support for verbose axis date-time unit labelling configuration (see: ``DateTimeLabelFormats``). + + +* **BUGFIX:** Added support for ``nodeFormat`` and ``nodeFormatter`` to tooltip properties for + diagram series (Organization, Dependency Wheel, and Sankey). (#148) +* **ENHANCEMENT:** Added ability to remove or override the JavaScript event listener when + serializing a chart to a JavaScript literal (#131 - courtesy of + `@ByronCook `__ ). +* **ENHANCEMENT:** Added support for the ``HCP_ALLOW_SPECIAL_IPS`` environment variable + which allows localhost (or other special URLs) to pass URL validation. (#147) +* **DOCUMENTATION:** Fixed typos in **Getting Started** tutorial. (#149) +* **DOCUMENTATION:** Fixed typos in documentation (courtesy of `@karlacio ` + and `@maxduganknight `). + +-------------------- + Release 1.5.1 ========================================= diff --git a/LICENSE b/LICENSE index 781347c9..a0ec9b42 100644 --- a/LICENSE +++ b/LICENSE @@ -18,6 +18,8 @@ Without prejudice to the limited right to download and test the Wrapper for eval A Wrapper License (as defined below) or renewal thereof can only be purchased by a Licensee either already holding a License to the Software, or at the same time as purchasing such a License, as an add-on product to such License, and only by a Licensee holding an active and valid enrolment in the Highcharts Advantage plan. The purchase of, and right to use the Licensed Software shall remain governed by the terms and conditions of the License issued by Highsoft to Licensee, whether this is Highsoft Standard License Agreement, Highsoft Terms and Conditions for Subscription to Annual License to Highsoft Software, or another licensing agreement as agreed between Highsoft and Licensee, as applicable (such terms and conditions between Highsoft and Licensee hereinafter collectively referred to as the “Main Agreement”). +A Wrapper License (as defined below) can additionally be obtained by holding an active and valid Educational or Personal License to the Software. The right to use the Licensed Software shall remain governed by the terms and conditions of the Authorized User issued by Highsoft to Authorized User, as defined in the Educational and Personal License 1.0, (such terms and conditions between Highsoft and Authorized User hereinafter collectively referred to as the "Main Agreement"). + These Wrapper T&Cs constitute an addendum to the Main Agreement and an integral part thereof and apply to the use of the Wrapper only. In the case of any discrepancy between the Main Agreement and these Wrapper T&Cs related to the licensing of the Wrapper, these Wrapper T&Cs shall prevail. @@ -86,13 +88,16 @@ Licensee shall not modify, delete or obscure any notices of proprietary rights o 6. Wrapper Support *********************************************** -A Wrapper License entitles Licensee to the support services and access to new Releases of the Wrapper as set out herein. While support for the Wrapper is contingent upon Licensee’s valid enrollment in the Highcharts Advantage plan and the annual number of hours of support available for support of the Licensed Software for each successive twelve month term during the period that Licensee is enrolled in Highcharts Advantage (each, a “Support Year”) are inclusive of the number of hours of support offered during such Support Year for the Wrapper, support of the Wrapper is not covered under Licensee’s enrollment in Highcharts Advantage, but is offered by Highsoft for a separate fee and provided separately, directly and independently by HCP. +A Wrapper License entitles Licensee to the support services and access to new Releases of the Wrapper as set out herein. While support for the Wrapper is contingent upon Licensee’s valid enrollment in the Highcharts Advantage plan and the annual number of hours of support available for support of the Licensed Software for each successive twelve month term during the period that Licensee is enrolled in Highcharts Advantage (each, a “Support Year”) are inclusive of the number of hours of support offered during such Support Year for the Wrapper, support of the Wrapper is not covered under Licensee’s enrollment in Highcharts Advantage, but is offered by Highsoft for a separate fee and provided separately, directly and independently by HCP. In the event that the Licensee is not enrolled in the Highcharts Advantage Plan, for example if the Main Agreement grants the Licensee a Personal or Educational License, then the Licensee shall not be entitled to the support services outlined herein. All support inquiries related to the Wrapper shall be sent to support@highchartspython.com or filed at https://www.highchartspython.com. -Under a valid and effective Wrapper License, Licensee shall be entitled to receive from HCP: +Under a valid and effective Wrapper License, Licensee shall be entitled to receive from HCP: i. All new releases or updates of the Wrapper released during the applicable Advantage Period; + +Under a valid and effective Wrapper License, contingent upon the Licensee’s valid enrollment in the Highcharts Advantage Plan and payment of applicable fees, Licensee shall be entitled to receive from HCP: + ii. Up to ten (10) hours of the personalized technical support for the Wrapper and/or the Licensed Software (combined) per Developer per Support Year based on the number of Developers stated in the License Statement for the License. Licensee may freely distribute its included total of ten (10) hours of personalized technical diff --git a/README.rst b/README.rst index 6dc994df..b89749ab 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.2.0. +The library supports Highcharts (JS) v.10.2 and higher, including Highcharts (JS) v.11.3.0. **COMPLETE DOCUMENTATION:** https://core-docs.highchartspython.com/en/latest/index.html diff --git a/docs/_contributors.rst b/docs/_contributors.rst index 6c33d97b..8e30f462 100644 --- a/docs/_contributors.rst +++ b/docs/_contributors.rst @@ -1,3 +1,6 @@ * Chris Modzelewski (`@hcpchris `__ / `@insightindustry `__) * Erin Modzelewski (`@EBModz `__) -* Jitendra Mishra (`@jmishra01 `__) \ No newline at end of file +* Jitendra Mishra (`@jmishra01 `__) +* Byron Cook (`@ByronCook `__) +* karlacio (`@karlacio `__) +* Max Dugan Knight (`@maxduganknight `__) diff --git a/docs/errors.rst b/docs/errors.rst index 2ec53a41..f273e72e 100644 --- a/docs/errors.rst +++ b/docs/errors.rst @@ -96,6 +96,18 @@ HighchartsNotSupportedError (from :exc:`ValueError ` and :exc .. inheritance-diagram:: HighchartsNotSupportedError :parts: -1 +-------------------- + +HighchartsInstanceNeededError (from :exc:`HighchartsNotSupportedError ` and :exc:`TypeError `) +==================================================================================================================================== + +.. autoexception:: HighchartsInstanceNeededError + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: HighchartsInstanceNeededError + :parts: -1 + -------------- HighchartsJavaScriptError (from :class:`ValueError `) diff --git a/docs/index.rst b/docs/index.rst index 97b5e19e..9072ee90 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.2.0 + **Latest Highcharts (JS) version supported:** v.11.3.0 **Highcharts Core for Python** is designed to be compatible with: @@ -289,7 +289,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/license.rst b/docs/license.rst index 960fa805..879a2704 100644 --- a/docs/license.rst +++ b/docs/license.rst @@ -10,6 +10,7 @@ Terms and Conditions for the Highcharts for Python Toolkit License creators of the Highcharts JavaScript library. **TO PURCHASE A LICENSE:** `https://shop.highcharts.com/ `__ + **TO APPLY FOR A PERSONAL / EDUCATIONAL LICENSE:** `https://shop.highcharts.com/contact/educational `__ *********************************************** 1. Acceptance of the Terms and Conditions @@ -27,6 +28,8 @@ Without prejudice to the limited right to download and test the Wrapper for eval A Wrapper License (as defined below) or renewal thereof can only be purchased by a Licensee either already holding a License to the Software, or at the same time as purchasing such a License, as an add-on product to such License, and only by a Licensee holding an active and valid enrolment in the Highcharts Advantage plan. The purchase of, and right to use the Licensed Software shall remain governed by the terms and conditions of the License issued by Highsoft to Licensee, whether this is Highsoft Standard License Agreement, Highsoft Terms and Conditions for Subscription to Annual License to Highsoft Software, or another licensing agreement as agreed between Highsoft and Licensee, as applicable (such terms and conditions between Highsoft and Licensee hereinafter collectively referred to as the “Main Agreement”). +A Wrapper License (as defined below) can additionally be obtained by holding an active and valid Educational or Personal License to the Software. The right to use the Licensed Software shall remain governed by the terms and conditions of the Authorized User issued by Highsoft to Authorized User, as defined in the Educational and Personal License 1.0, (such terms and conditions between Highsoft and Authorized User hereinafter collectively referred to as the "Main Agreement"). + These Wrapper T&Cs constitute an addendum to the Main Agreement and an integral part thereof and apply to the use of the Wrapper only. In the case of any discrepancy between the Main Agreement and these Wrapper T&Cs related to the licensing of the Wrapper, these Wrapper T&Cs shall prevail. @@ -95,13 +98,16 @@ Licensee shall not modify, delete or obscure any notices of proprietary rights o 6. Wrapper Support *********************************************** -A Wrapper License entitles Licensee to the support services and access to new Releases of the Wrapper as set out herein. While support for the Wrapper is contingent upon Licensee’s valid enrollment in the Highcharts Advantage plan and the annual number of hours of support available for support of the Licensed Software for each successive twelve month term during the period that Licensee is enrolled in Highcharts Advantage (each, a “Support Year”) are inclusive of the number of hours of support offered during such Support Year for the Wrapper, support of the Wrapper is not covered under Licensee’s enrollment in Highcharts Advantage, but is offered by Highsoft for a separate fee and provided separately, directly and independently by HCP. +A Wrapper License entitles Licensee to the support services and access to new Releases of the Wrapper as set out herein. While support for the Wrapper is contingent upon Licensee's valid enrollment in the Highcharts Advantage plan and the annual number of hours of support available for support of the Licensed Software for each successive twelve month term during the period that Licensee is enrolled in Highcharts Advantage (each, a “Support Year”) are inclusive of the number of hours of support offered during such Support Year for the Wrapper, support of the Wrapper is not covered under Licensee’s enrollment in Highcharts Advantage, but is offered by Highsoft for a separate fee and provided separately, directly and independently by HCP. In the event that the Licensee is not enrolled in the Highcharts Advantage Plan, for example if the Main Agreement grants the Licensee a Personal or Educational License, then the Licensee shall not be entitled to the support services outlined herein. All support inquiries related to the Wrapper shall be sent to support@highchartspython.com or filed at https://www.highchartspython.com. -Under a valid and effective Wrapper License, Licensee shall be entitled to receive from HCP: +Under a valid and effective Wrapper License, Licensee shall be entitled to receive from HCP: i. All new releases or updates of the Wrapper released during the applicable Advantage Period; + +Under a valid and effective Wrapper License, contingent upon the Licensee’s valid enrollment in the Highcharts Advantage Plan and payment of applicable fees, Licensee shall be entitled to receive from HCP: + ii. Up to ten (10) hours of the personalized technical support for the Wrapper and/or the Licensed Software (combined) per Developer per Support Year based on the number of Developers stated in the License Statement for the License. Licensee may freely distribute its included total of ten (10) hours of personalized technical diff --git a/docs/tutorials/_assembling_a_chart.rst b/docs/tutorials/_assembling_a_chart.rst index 5474dfd3..0be11a9b 100644 --- a/docs/tutorials/_assembling_a_chart.rst +++ b/docs/tutorials/_assembling_a_chart.rst @@ -18,7 +18,7 @@ from a :class:`pandas.DataFrame `, # From a Numpy ndarray - my_chart = Chart.from_array(data = as_ndarray, series_type = 'line') + my_chart = Chart.from_array(as_ndarray, series_type = 'line') # From a Python dict diff --git a/docs/tutorials/getting_started.rst b/docs/tutorials/getting_started.rst index 2cd09aea..011a3503 100644 --- a/docs/tutorials/getting_started.rst +++ b/docs/tutorials/getting_started.rst @@ -92,7 +92,7 @@ or Python :class:`dict `: df = pandas.DataFrame(my_iterable, columns=['x', 'y']) # As a Numpy ndarray - as_ndarray = numpy.as_ndarray(my_iterable) + as_ndarray = numpy.asarray(my_iterable) # As a Python dict as_dict = {'x': x[0], 'y': x[1] for x in my_iterable} diff --git a/highcharts_core/__version__.py b/highcharts_core/__version__.py index 1b37c690..bcd8d54e 100644 --- a/highcharts_core/__version__.py +++ b/highcharts_core/__version__.py @@ -1 +1 @@ -__version__ = '1.5.1' \ No newline at end of file +__version__ = '1.6.0' diff --git a/highcharts_core/ai.py b/highcharts_core/ai.py index b2f46794..3c81e0df 100644 --- a/highcharts_core/ai.py +++ b/highcharts_core/ai.py @@ -103,6 +103,8 @@ 'gpt-3.5-turbo-16k': ('OpenAI', OPENAI_MESSAGES), 'gpt-4': ('OpenAI', OPENAI_MESSAGES), 'gpt-4-32k': ('OpenAI', OPENAI_MESSAGES), + 'gpt-4-1106-preview': ('OpenAI', OPENAI_MESSAGES), + 'gpt-4-vision-preview': ('OpenAI', OPENAI_MESSAGES), 'claude-instant-1': ('Anthropic', ANTHROPIC_PROMPT), 'claude-2': ('Anthropic', ANTHROPIC_PROMPT), } @@ -157,6 +159,8 @@ def convert_to_js(callable, * ``'gpt-3.5-turbo-16k'`` * ``'gpt-4'`` * ``'gpt-4-32k'`` + * ``'gpt-4-1106-preview'`` + * ``'gpt-4-vision-preview'`` * ``'claude-instant-1'`` * ``'claude-2'`` diff --git a/highcharts_core/chart.py b/highcharts_core/chart.py index 26099432..bdcce4db 100644 --- a/highcharts_core/chart.py +++ b/highcharts_core/chart.py @@ -20,42 +20,42 @@ class Chart(HighchartsMeta): def __init__(self, **kwargs): """Creates a :class:`Chart ` instance. - + When creating a :class:`Chart ` instance, you can provide any of the object's properties as keyword arguments. **Positional arguments are not supported**. - - In addition to the standard properties, there are three special keyword - arguments which streamline the creation of + + In addition to the standard properties, there are three special keyword + arguments which streamline the creation of :class:`Chart ` instances: - - * ``series`` which accepts an iterable of + + * ``series`` which accepts an iterable of :class:`SeriesBase ` descendents. These are automatically then populated as series within the chart. - + .. note:: - + Each member of ``series`` must be coercable into a Highcharts Core for Python series. And it must contain a ``type`` property. - + * ``data`` which accepts an iterable of objects that are coercable to Highcharts - data point objects, which are then automatically used to create/populate a + data point objects, which are then automatically used to create/populate a series on your chart instance * ``series_type`` which accepts a string indicating the type of series to render for your data. - + .. warning:: - - If you supply ``series``, the ``data`` and ``series_type`` keywords will be + + If you supply ``series``, the ``data`` and ``series_type`` keywords will be *ignored*. - - If you supply ``data``, then ``series_type`` must *also* be supplied. Failure - to do so will raise a + + If you supply ``data``, then ``series_type`` must *also* be supplied. Failure + to do so will raise a :exc:`HighchartsValueError `. - If you are also supplying an - :meth:`options ` keyword argument, then - any series derived from ``series``, ``data``, and ``series_type`` will be + If you are also supplying an + :meth:`options ` keyword argument, then + any series derived from ``series``, ``data``, and ``series_type`` will be *added* to any series defined in that ``options`` value. :raises: :exc:`HighchartsValueError ` @@ -106,19 +106,19 @@ def __str__(self): """Return a human-readable :class:`str ` representation of the chart. .. warning:: - - To ensure that the result is human-readable, the chart's ``options`` property will - be rendered *without* its ``plot_options`` and ``series`` sub-properties. - + + To ensure that the result is human-readable, the chart's ``options`` property will + be rendered *without* its ``plot_options`` and ``series`` sub-properties. + .. tip:: - - If you would like a *complete* and *unambiguous* :class:`str ` + + If you would like a *complete* and *unambiguous* :class:`str ` representation, then you can: - + * use the :meth:`__repr__() ` method, * call ``repr(my_chart)``, or * serialize the chart to JSON using ``my_chart.to_json()``. - + :returns: A :class:`str ` representation of the chart. :rtype: :class:`str ` """ @@ -140,7 +140,7 @@ def __str__(self): kwargs_as_str += f'options = {kwargs[key]}' else: kwargs_as_str += f'{key} = {repr(kwargs[key])}' - + return f'{self.__class__.__name__}({kwargs_as_str})' def _jupyter_include_scripts(self, **kwargs): @@ -148,13 +148,13 @@ def _jupyter_include_scripts(self, **kwargs): :rtype: :class:`str ` """ - required_modules = [f'{self.module_url}{x}' + required_modules = [f'{self.module_url}{x}' for x in self.get_required_modules(include_extension = True)] js_str = '' for item in required_modules: js_str += utility_functions.jupyter_add_script(item) js_str += """.then(() => {""" - + for item in required_modules: js_str += """});""" @@ -247,49 +247,114 @@ def _jupyter_container_html(self, return container_str - def _repr_html_(self): + def _repr_html_(self) -> str: """Produce the HTML representation of the chart. - .. note:: + :returns: The HTML representation of the chart. + :rtype: :class:`str ` + """ + container = self.container or 'highcharts_target_div' + if not self._random_slug: + self._random_slug = {} - Currently includes *all* `Highcharts JS `_ modules - in the HTML. This issue will be addressed when roadmap issue :issue:`2` is - released. + random_slug = self._random_slug.get(container, None) - :returns: The HTML representation of the chart. + if not random_slug: + random_slug = utility_functions.get_random_string() + self._random_slug[container] = random_slug + + html_str = self._jupyter_container_html(container, random_slug) + + chart_js_str = self._jupyter_javascript(container = container, + random_slug = random_slug) + wrapped_chart_js_str = utility_functions.wrap_for_requirejs('', chart_js_str) + + include_js_str = self._get_jupyter_script_loader(chart_js_str) + + include_str = f"""""" + js_str = f"""""" + + as_str = f"""{html_str}\n{include_str}\n{js_str}""" + + return as_str + + def _repr_png_(self) -> bytes: + """Return a PNG representation of the chart, expressed as bytes. + + .. note:: + + This relies on the + :meth:`.download_chart() ` method, + which in turn relies on the Highcharts Export Server. If you need to override + the default Export Server configuration, you can do so using environment + variables as documented for the + :class:`ExportServer ` class. + + :rtype: :class:`bytes ` + """ + return self.download_chart(format = 'png') + + def _repr_svg_(self): + """Return an SVG representation of the chart. + + .. note:: + + This relies on the + :meth:`.download_chart() ` method, + which in turn relies on the Highcharts Export Server. If you need to override + the default Export Server configuration, you can do so using environment + variables as documented for the + :class:`ExportServer ` class. + :rtype: :class:`str ` """ - return self.display() + return self.download_chart(format = 'svg') + + def _repr_jpeg_(self) -> bytes: + """Return a JPEG representation of the chart, expressed as bytes. + + .. note:: + + This relies on the + :meth:`.download_chart() ` method, + which in turn relies on the Highcharts Export Server. If you need to override + the default Export Server configuration, you can do so using environment + variables as documented for the + :class:`ExportServer ` class. + + :rtype: :class:`bytes ` + """ + return self.download_chart(format = 'jpeg') def get_script_tags(self, as_str = False) -> List[str] | str: """Return the collection of ``' + scripts = [f'' for x in self.get_required_modules(include_extension = True)] if as_str: return '\n'.join(scripts) - + return scripts - def get_required_modules(self, + 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 + + :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 ` of :class:`str ` """ initial_scripts = ['highcharts'] @@ -298,12 +363,12 @@ def get_required_modules(self, return scripts def _get_jupyter_script_loader(self, chart_js_str) -> str: - """Return the JavaScript code that loads ``required_modules`` in a Jupyter + """Return the JavaScript code that loads ``required_modules`` in a Jupyter context. - + :param chart_js_str: The JavaScript code that renders the chart. :type chart_js_str: :class:`str ` - + :returns: The JavaScript code that loads the required modules. :rtype: :class:`str ` """ @@ -315,7 +380,7 @@ def _get_jupyter_script_loader(self, chart_js_str) -> str: if_requirejs += """ paths: {\n""" if_requirejs += f""" 'highcharts': '{self.module_url}'\n""" if_requirejs += """ }\n\n});""" - + if_requirejs += """ require([""" requirejs_modules = [] for item in self.get_required_modules(): @@ -325,7 +390,7 @@ def _get_jupyter_script_loader(self, chart_js_str) -> str: revised_item = f'highcharts/{item}' if revised_item not in requirejs_modules: requirejs_modules.append(revised_item) - + for index, item in enumerate(requirejs_modules): is_last = index == len(requirejs_modules) - 1 if_requirejs += f"""'{item}'""" @@ -335,7 +400,7 @@ def _get_jupyter_script_loader(self, chart_js_str) -> str: if_requirejs += chart_js_str if_requirejs += """\n});""" - required_modules = [f'{self.module_url}{x}' + required_modules = [f'{self.module_url}{x}' for x in self.get_required_modules(include_extension = True)] for item in required_modules: if_no_requirejs += utility_functions.jupyter_add_script(item) @@ -343,9 +408,9 @@ def _get_jupyter_script_loader(self, chart_js_str) -> str: for item in required_modules: if_no_requirejs += """});""" - + js_str = utility_functions.wrap_for_requirejs(if_requirejs, if_no_requirejs) - + return js_str @property @@ -369,46 +434,48 @@ def callback(self, value): @property def module_url(self) -> str: - """The URL from which Highcharts modules should be downloaded when + """The URL from which Highcharts modules should be downloaded when generating the ``