Skip to content

Commit

Permalink
Merge pull request #26 from highcharts-for-python/develop
Browse files Browse the repository at this point in the history
PR for v.1.0.0-rc5
  • Loading branch information
hcpchris authored Apr 2, 2023
2 parents a7f7a18 + 856080d commit 2fa6fdf
Show file tree
Hide file tree
Showing 43 changed files with 405 additions and 72 deletions.
17 changes: 17 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
Release 1.0.0-rc5
=========================================

* Bug fixes to Jupyter Labs rendering.
* Bug fix for timestamp serialization of timezone-naive ``datetime`` objects.
* Bug fix: typo in Plot Bands serialization.
* Added null support to color validation.
* Bug fix in ``style`` deserialization.
* Bug fix in ``CartesianData.from_array()``.
* Fixed ``NaN`` handling in ``.load_from_pandas()``.
* Fixed JSON deserialization in ``.from_array()``.
* Added support for stylesheet links in Jupyter Labs context.
* Several bug fixes in JS literal serialization.
* Major improvements to JavaScript module inclusion.

---------------

Release 1.0.0-rc4
=========================================

Expand Down
2 changes: 1 addition & 1 deletion highcharts_core/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.0.0-rc4'
__version__ = '1.0.0-rc5'
111 changes: 91 additions & 20 deletions highcharts_core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,108 @@ def __eq__(self, other):
'https://code.highcharts.com/highcharts-3d.js',
'https://code.highcharts.com/modules/accessibility.js',
'https://code.highcharts.com/modules/annotations.js',
'https://code.highcharts.com/modules/annotations-advanced.js',
'https://code.highcharts.com/modules/sankey.js',
'https://code.highcharts.com/modules/arc-diagram.js',
'https://code.highcharts.com/modules/boost.js',
'https://code.highcharts.com/modules/broken-axis.js',
'https://code.highcharts.com/modules/bullet.js',
'https://code.highcharts.com/modules/cylinder.js',
'https://code.highcharts.com/modules/data.js',
'https://code.highcharts.com/modules/exporting.js',
'https://code.highcharts.com/modules/datagrouping.js',
'https://code.highcharts.com/modules/debugger.js',
'https://code.highcharts.com/modules/dependency-wheel.js',
'https://code.highcharts.com/modules/drag-panes.js',
'https://code.highcharts.com/modules/draggable-points.js',
'https://code.highcharts.com/modules/drilldown.js',
'https://code.highcharts.com/modules/dumbbell.js',
'https://code.highcharts.com/modules/export-data.js',
'https://code.highcharts.com/modules/exporting.js',
'https://code.highcharts.com/modules/funnel.js',
'https://code.highcharts.com/modules/funnel3d.js',
'https://code.highcharts.com/modules/heatmap.js',
'https://code.highcharts.com/modules/item-series.js',
'https://code.highcharts.com/modules/lollipop.js',
'https://code.highcharts.com/modules/networkgraph.js',
'https://code.highcharts.com/modules/no-data-to-display.js',
'https://code.highcharts.com/modules/offline-exporting.js',
'https://code.highcharts.com/modules/solid-gauge.js',
'https://code.highcharts.com/modules/oldie.js',
'https://code.highcharts.com/modules/organization.js',
'https://code.highcharts.com/modules/parallel-coordinates.js',
'https://code.highcharts.com/modules/pareto.js',
'https://code.highcharts.com/modules/pyramid3d.js',
'https://code.highcharts.com/modules/series-label.js',
'https://code.highcharts.com/modules/treemap.js'
'https://code.highcharts.com/modules/series-on-point.js',
'https://code.highcharts.com/modules/solid-gauge.js',
'https://code.highcharts.com/modules/streamgraph.js',
'https://code.highcharts.com/modules/sunburst.js',
'https://code.highcharts.com/modules/tilemap.js',
'https://code.highcharts.com/modules/timeline.js',
'https://code.highcharts.com/modules/treegraph.js',
'https://code.highcharts.com/modules/treemap.js',
'https://code.highcharts.com/modules/variable-pie.js',
'https://code.highcharts.com/modules/variwide.js',
'https://code.highcharts.com/modules/vector.js',
'https://code.highcharts.com/modules/venn.js',
'https://code.highcharts.com/modules/windbarb.js',
'https://code.highcharts.com/modules/wordcloud.js',
'https://code.highcharts.com/modules/xrange.js',
]

INCLUDE_STR = """<script src="https://code.highcharts.com/highcharts.js"/>
<script src="https://code.highcharts.com/highcharts-more.js"/>
<script src="https://code.highcharts.com/highcharts-3d.js"/>
<script src="https://code.highcharts.com/modules/accessibility.js"/>
<script src="https://code.highcharts.com/modules/annotations.js"/>
<script src="https://code.highcharts.com/modules/boost.js"/>
<script src="https://code.highcharts.com/modules/broken-axis.js"/>
<script src="https://code.highcharts.com/modules/data.js"/>
<script src="https://code.highcharts.com/modules/exporting.js"/>
<script src="https://code.highcharts.com/modules/drilldown.js"/>
<script src="https://code.highcharts.com/modules/funnel.js"/>
<script src="https://code.highcharts.com/modules/heatmap.js"/>
<script src="https://code.highcharts.com/modules/no-data-to-display.js"/>
<script src="https://code.highcharts.com/modules/offline-exporting.js"/>
<script src="https://code.highcharts.com/modules/solid-gauge.js"/>
<script src="https://code.highcharts.com/modules/treemap.js"/>
<script src="https://code.highcharts.com/modules/series-label.js"/>
INCLUDE_STR = """
<script src="https://code.highcharts.com/highcharts.js"/>
<script src="https://code.highcharts.com/highcharts-more.js"/>
<script src="https://code.highcharts.com/highcharts-3d.js"/>
<script src="https://code.highcharts.com/modules/accessibility.js"/>
<script src="https://code.highcharts.com/modules/annotations.js"/>
<script src="https://code.highcharts.com/modules/annotations-advanced.js"/>
<script src="https://code.highcharts.com/modules/arc-diagram.js"/>
<script src="https://code.highcharts.com/modules/bellcurve.js"/>
<script src="https://code.highcharts.com/modules/boost.js"/>
<script src="https://code.highcharts.com/modules/broken-axis.js"/>
<script src="https://code.highcharts.com/modules/bullet.js"/>
<script src="https://code.highcharts.com/modules/cylinder.js"/>
<script src="https://code.highcharts.com/modules/data.js"/>
<script src="https://code.highcharts.com/modules/datagrouping.js"/>
<script src="https://code.highcharts.com/modules/debugger.js"/>
<script src="https://code.highcharts.com/modules/dependency-wheel.js"/>
<script src="https://code.highcharts.com/modules/drag-panes'
<script src="https://code.highcharts.com/modules/draggable-points.js"/>
<script src="https://code.highcharts.com/modules/drilldown.js"/>
<script src="https://code.highcharts.com/modules/dumbbell.js"/>
<script src="https://code.highcharts.com/modules/export-data.js"/>
<script src="https://code.highcharts.com/modules/exporting.js"/>
<script src="https://code.highcharts.com/modules/full-screen.js"/>
<script src="https://code.highcharts.com/modules/funnel.js"/>
<script src="https://code.highcharts.com/modules/funnel3d.js"/>
<script src="https://code.highcharts.com/modules/histogram.js"/>
<script src="https://code.highcharts.com/modules/item-series.js"/>
<script src="https://code.highcharts.com/modules/lollipop.js"/>
<script src="https://code.highcharts.com/modules/networkgraph.js"/>
<script src="https://code.highcharts.com/modules/no-data-to-display.js"/>
<script src="https://code.highcharts.com/modules/offline-exporting.js"/>
<script src="https://code.highcharts.com/modules/oldie.js"/>
<script src="https://code.highcharts.com/modules/organization.js"/>
<script src="https://code.highcharts.com/modules/parallel-coordinates.js"/>
<script src="https://code.highcharts.com/modules/pareto.js"/>
<script src="https://code.highcharts.com/modules/pyramid3d.js"/>
<script src="https://code.highcharts.com/modules/sankey.js"/>
<script src="https://code.highcharts.com/modules/series-label.js"/>
<script src="https://code.highcharts.com/modules/series-on-point.js"/>
<script src="https://code.highcharts.com/modules/solid-gauge.js"/>
<script src="https://code.highcharts.com/modules/streamgraph.js"/>
<script src="https://code.highcharts.com/modules/sunburst.js"/>
<script src="https://code.highcharts.com/modules/tilemap.js"/>
<script src="https://code.highcharts.com/modules/timeline.js"/>
<script src="https://code.highcharts.com/modules/treegraph.js"/>
<script src="https://code.highcharts.com/modules/treemap.js"/>
<script src="https://code.highcharts.com/modules/variable-pie.js"/>
<script src="https://code.highcharts.com/modules/variwide.js"/>
<script src="https://code.highcharts.com/modules/vector.js"/>
<script src="https://code.highcharts.com/modules/venn.js"/>
<script src="https://code.highcharts.com/modules/windbarb.js"/>
<script src="https://code.highcharts.com/modules/wordcloud.js"/>
<script src="https://code.highcharts.com/modules/xrange.js"/>
"""

AXIS_TYPES = [
Expand Down
2 changes: 1 addition & 1 deletion highcharts_core/global_options/language/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ def style(self) -> Optional[str]:

@style.setter
def style(self, value):
self._style = validators.string(value, allow_empty = True)
self._style = validators.string(value, allow_empty = True, coerce_value = True)

@property
def time_cycles(self) -> Optional[str]:
Expand Down
50 changes: 48 additions & 2 deletions highcharts_core/js_literal_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def serialize_to_js_literal(item, encoding = 'utf-8') -> Optional[str]:
elif isinstance(item, bool):
return item
elif checkers.is_string(item):
return item
return_value = item.replace("'", "\\'")
return return_value
elif checkers.is_numeric(item) and not isinstance(item, Decimal):
return item
elif isinstance(item, Decimal):
Expand All @@ -46,6 +47,8 @@ def serialize_to_js_literal(item, encoding = 'utf-8') -> Optional[str]:
as_dict[key] = serialize_to_js_literal(item[key], encoding = encoding)
return str(as_dict)
elif checkers.is_datetime(item):
if not item.tzinfo:
item = item.replace(tzinfo = datetime.timezone.utc)
return item.timestamp()
elif checkers.is_date(item):
return f'Date.UTC({item.year}, {item.month - 1}, {item.day})'
Expand All @@ -57,6 +60,41 @@ def serialize_to_js_literal(item, encoding = 'utf-8') -> Optional[str]:
return None


def is_js_object(as_str):
"""Determine whether ``as_str`` is a JavaScript object.
:param as_str: The string to evaluate.
:type as_str: :class:`str <python:str>`
:returns: ``True`` if ``as_str`` is a JavaScript function. ``False`` if not.
:rtype: :class:`bool <python:bool>`
"""
expression_item = f'const testName = {as_str}'
try:
parsed = esprima.parseScript(expression_item)
except ParseError:
try:
parsed = esprima.parseModule(expression_item)
except ParseError:
return False

body = parsed.body
if not body:
return False

first_item = body[0]
if first_item.type != 'VariableDeclaration':
return False

init = first_item.declarations[0].init
if not init:
return False
if init.type in ('ObjectExpression'):
return True

return False


def attempt_variable_declaration(as_str):
"""Attempt to coerce ``as_str`` to a JavaScript variable declaration form.
Expand Down Expand Up @@ -160,13 +198,21 @@ def get_js_literal(item) -> str:
subitem_counter = 0
for subitem in subitems:
subitem_counter += 1
if subitem == 'None':
subitem = 'null'
as_str += f"""{subitem}"""
if subitem_counter < len(subitems):
as_str += ',\n'
as_str += ']'
elif checkers.is_string(item):
if item.startswith('{') or item.startswith('[') or item.startswith('Date'):
if item.startswith('[') or item.startswith('Date'):
as_str += f"""{item}"""
elif item.startswith('{') and item.endswith('}'):
if is_js_object(item):
as_str += f"""{item}"""
elif "'" in item:
item = item.replace("'", "\\'")
as_str += f'"{item}"'
elif item in string.whitespace:
as_str += f"""`{item}`"""
elif item.startswith == 'HCP: REPLACE-WITH-':
Expand Down
2 changes: 1 addition & 1 deletion highcharts_core/options/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ def __init__(self, **kwargs):
self.navigation = kwargs.get('navigation', None)
self.plot_options = kwargs.get('plot_options', None)
self.responsive = kwargs.get('responsive', None)
self.series = kwargs.get('series', None)
self.subtitle = kwargs.get('subtitle', None)
self.time = kwargs.get('time', None)
self.title = kwargs.get('title', None)
self.tooltip = kwargs.get('tooltip', None)
self.x_axis = kwargs.get('x_axis', None)
self.y_axis = kwargs.get('y_axis', None)

self.series = kwargs.get('series', None)
@property
def accessibility(self) -> Optional[Accessibility]:
"""Options for configuring accessibility for the chart.
Expand Down
4 changes: 2 additions & 2 deletions highcharts_core/options/annotations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ def draggable(self, value):
if value is None:
self._draggable = None
else:
value = validators.string(value, allow_empty = True)
value = validators.string(value, allow_empty = True) or ''
value = value.lower()
if value not in ['x', 'xy', 'y']:
if value not in ['x', 'xy', 'y', '']:
raise errors.HighchartsValueError(f'draggable must be "x", "xy", "y", '
f'or "". Was: {value}')
self._draggable = value
Expand Down
2 changes: 1 addition & 1 deletion highcharts_core/options/annotations/label_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ def style(self) -> Optional[str]:

@style.setter
def style(self, value):
self._style = validators.string(value, allow_empty = True)
self._style = validators.string(value, allow_empty = True, coerce_value = True)

@property
def text(self) -> Optional[str]:
Expand Down
8 changes: 6 additions & 2 deletions highcharts_core/options/axes/labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,9 @@ def style(self) -> Optional[str]:

@style.setter
def style(self, value):
self._style = validators.string(value, allow_empty = True)
self._style = validators.string(value,
allow_empty = True,
coerce_value = True)

@property
def use_html(self) -> Optional[bool]:
Expand Down Expand Up @@ -632,7 +634,9 @@ def style(self) -> Optional[str]:

@style.setter
def style(self, value):
self._style = validators.string(value, allow_empty = True)
self._style = validators.string(value,
allow_empty = True,
coerce_value = True)

@property
def text(self) -> Optional[str]:
Expand Down
4 changes: 2 additions & 2 deletions highcharts_core/options/axes/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def categories(self, value):
if not value:
self._categories = None
else:
self._categories = [validators.string(x) for x in validators.iterable(value)]
self._categories = [validators.string(x, allow_empty = True) or '' for x in validators.iterable(value)]

@property
def date_time_label_formats(self) -> Optional[DateTimeLabelFormats]:
Expand Down Expand Up @@ -541,7 +541,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict:
'opposite': self.opposite,
'pane': self.pane,
'panningEnabled': self.panning_enabled,
'plotPands': self.plot_bands,
'plotBands': self.plot_bands,
'plotLines': self.plot_lines,
'reversed': self.reversed,
'reversedStacks': self.reversed_stacks,
Expand Down
11 changes: 7 additions & 4 deletions highcharts_core/options/axes/title.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from validator_collection import validators

from highcharts_core import errors
from highcharts_core import errors, constants
from highcharts_core.metaclasses import HighchartsMeta


Expand Down Expand Up @@ -208,10 +208,10 @@ def style(self) -> Optional[str]:

@style.setter
def style(self, value):
self._style = validators.string(value, allow_empty = True)
self._style = validators.string(value, allow_empty = True, coerce_value = True)

@property
def text(self) -> Optional[str]:
def text(self) -> Optional[str | constants.EnforcedNullType]:
"""The actual text of the title.
.. note::
Expand All @@ -225,7 +225,10 @@ def text(self) -> Optional[str]:

@text.setter
def text(self, value):
self._text = validators.string(value, allow_empty = True)
if isinstance(value, constants.EnforcedNullType):
self._text = constants.EnforcedNull
else:
self._text = validators.string(value, allow_empty = True)

@property
def text_align(self) -> Optional[str]:
Expand Down
10 changes: 7 additions & 3 deletions highcharts_core/options/axes/x_axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from validator_collection import validators

from highcharts_core import errors
from highcharts_core.decorators import class_sensitive
from highcharts_core.decorators import class_sensitive, validate_types
from highcharts_core.utility_classes.gradients import Gradient
from highcharts_core.utility_classes.patterns import Pattern

Expand Down Expand Up @@ -49,9 +49,13 @@ def crosshair(self) -> Optional[CrosshairOptions]:
return self._crosshair

@crosshair.setter
@class_sensitive(CrosshairOptions)
def crosshair(self, value):
self._crosshair = value
if isinstance(value, bool):
value = {
'enabled': value
}

self._crosshair = validate_types(value, CrosshairOptions)

@property
def height(self) -> Optional[str | int | float | Decimal]:
Expand Down
4 changes: 3 additions & 1 deletion highcharts_core/options/caption.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ def style(self) -> Optional[str]:

@style.setter
def style(self, value):
self._style = validators.string(value, allow_empty = True)
self._style = validators.string(value,
allow_empty = True,
coerce_value = True)

@property
def text(self) -> Optional[str]:
Expand Down
Loading

0 comments on commit 2fa6fdf

Please sign in to comment.