From 4c1bac67f83a8890b635c3ca0edf1af503ba453e Mon Sep 17 00:00:00 2001 From: David Miller Date: Wed, 30 Nov 2022 10:36:02 +0000 Subject: [PATCH] Fixed ImportError: cannot import name 'Iterable' from 'collections' for python 3.10 and above. --- build/lib/highcharts/__init__.py | 9 + build/lib/highcharts/highcharts/__init__.py | 1 + build/lib/highcharts/highcharts/common.py | 737 ++++++++++++++++++ .../highcharts/highcharts/highchart_types.py | 672 ++++++++++++++++ build/lib/highcharts/highcharts/highcharts.py | 473 +++++++++++ build/lib/highcharts/highcharts/options.py | 657 ++++++++++++++++ .../highcharts/highcharts/templates/base.html | 21 + .../highcharts/templates/content.html | 88 +++ .../highcharts/highcharts/templates/page.html | 12 + .../highcharts/highcharts/templates/test.html | 17 + build/lib/highcharts/highmaps/__init__.py | 1 + build/lib/highcharts/highmaps/common.py | 701 +++++++++++++++++ .../lib/highcharts/highmaps/highmap_helper.py | 255 ++++++ .../lib/highcharts/highmaps/highmap_types.py | 301 +++++++ build/lib/highcharts/highmaps/highmaps.py | 499 ++++++++++++ build/lib/highcharts/highmaps/options.py | 534 +++++++++++++ .../highcharts/highmaps/templates/base.html | 21 + .../highmaps/templates/content.html | 57 ++ .../highcharts/highmaps/templates/page.html | 12 + .../highcharts/highmaps/templates/test.html | 17 + build/lib/highcharts/highstock/__init__.py | 1 + build/lib/highcharts/highstock/common.py | 729 +++++++++++++++++ build/lib/highcharts/highstock/highstock.py | 440 +++++++++++ .../highcharts/highstock/highstock_helper.py | 153 ++++ .../highcharts/highstock/highstock_types.py | 559 +++++++++++++ build/lib/highcharts/highstock/options.py | 587 ++++++++++++++ .../highcharts/highstock/templates/base.html | 21 + .../highstock/templates/content.html | 56 ++ .../highcharts/highstock/templates/page.html | 13 + .../highcharts/highstock/templates/test.html | 17 + build/lib/highcharts/ipynb.py | 35 + build/lib/highcharts/version.py | 7 + highcharts/highcharts/highcharts.py | 7 +- highcharts/highmaps/highmaps.py | 7 +- highcharts/highstock/highstock.py | 7 +- highcharts/version.py | 2 +- 36 files changed, 7722 insertions(+), 4 deletions(-) create mode 100644 build/lib/highcharts/__init__.py create mode 100644 build/lib/highcharts/highcharts/__init__.py create mode 100644 build/lib/highcharts/highcharts/common.py create mode 100644 build/lib/highcharts/highcharts/highchart_types.py create mode 100644 build/lib/highcharts/highcharts/highcharts.py create mode 100644 build/lib/highcharts/highcharts/options.py create mode 100644 build/lib/highcharts/highcharts/templates/base.html create mode 100644 build/lib/highcharts/highcharts/templates/content.html create mode 100644 build/lib/highcharts/highcharts/templates/page.html create mode 100644 build/lib/highcharts/highcharts/templates/test.html create mode 100644 build/lib/highcharts/highmaps/__init__.py create mode 100644 build/lib/highcharts/highmaps/common.py create mode 100644 build/lib/highcharts/highmaps/highmap_helper.py create mode 100644 build/lib/highcharts/highmaps/highmap_types.py create mode 100644 build/lib/highcharts/highmaps/highmaps.py create mode 100644 build/lib/highcharts/highmaps/options.py create mode 100644 build/lib/highcharts/highmaps/templates/base.html create mode 100644 build/lib/highcharts/highmaps/templates/content.html create mode 100644 build/lib/highcharts/highmaps/templates/page.html create mode 100644 build/lib/highcharts/highmaps/templates/test.html create mode 100644 build/lib/highcharts/highstock/__init__.py create mode 100644 build/lib/highcharts/highstock/common.py create mode 100644 build/lib/highcharts/highstock/highstock.py create mode 100644 build/lib/highcharts/highstock/highstock_helper.py create mode 100644 build/lib/highcharts/highstock/highstock_types.py create mode 100644 build/lib/highcharts/highstock/options.py create mode 100644 build/lib/highcharts/highstock/templates/base.html create mode 100644 build/lib/highcharts/highstock/templates/content.html create mode 100644 build/lib/highcharts/highstock/templates/page.html create mode 100644 build/lib/highcharts/highstock/templates/test.html create mode 100644 build/lib/highcharts/ipynb.py create mode 100644 build/lib/highcharts/version.py diff --git a/build/lib/highcharts/__init__.py b/build/lib/highcharts/__init__.py new file mode 100644 index 0000000..eacf192 --- /dev/null +++ b/build/lib/highcharts/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +from .version import version_info, __version__ + +from .highcharts.highcharts import Highchart +from .highmaps.highmaps import Highmap +from .highstock.highstock import Highstock + +from . import ipynb diff --git a/build/lib/highcharts/highcharts/__init__.py b/build/lib/highcharts/highcharts/__init__.py new file mode 100644 index 0000000..8ca57d6 --- /dev/null +++ b/build/lib/highcharts/highcharts/__init__.py @@ -0,0 +1 @@ +from .highcharts import Highchart \ No newline at end of file diff --git a/build/lib/highcharts/highcharts/common.py b/build/lib/highcharts/highcharts/common.py new file mode 100644 index 0000000..ab929fe --- /dev/null +++ b/build/lib/highcharts/highcharts/common.py @@ -0,0 +1,737 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +""" Python-Highcharts common.py +Common Functions For Highcharts +""" +from past.builtins import basestring +import datetime, re + +FORMATTER_TYPE_MAPPINGS = { + "default": "function() { return this.value }", + "date": "function() { return''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + ': '+ this.y; }", + "pie": "function() { return ''+ this.point.name +': '+ \ + this.percentage +' %'; }", + "pound_yAxis": "function() { '£' + return this.value }", + "pound_tooltip": "function() { return''+ this.x + ': '+ '£' +this.y; }", + "percent": "function() { return this.value + ' %' }", + "default_tooltip": "function () { return''+ this.series.name + ': ' + this.y; }", + "percent_tooltip": "function () { return''+ this.series.name + ': ' + this.y + ' %'; }", + "date_percent_tooltip": "function () { return''+Highcharts.dateFormat('%e. %b %Y',this.x) + '
'+ this.series.name + ': ' + this.y + ' %'; }", + 'filesize': """ +function() { + fileSizeInBytes = this.value; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'date_filesize_tooltip': """ +function() { + fileSizeInBytes = this.y; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return ''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + '
' + this.series.name + ': ' + Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'filesize_tooltip': """ +function() { + fileSizeInBytes = this.y; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return '' + this.series.name + ': ' + Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'duration': """ +function() { + seconds = this.value; + + days = Math.floor(seconds / 86400); + seconds = seconds - (days * 86400); + + hours = Math.floor(seconds / 3600); + seconds = seconds - (hours * 3600); + + mins = Math.floor(seconds / 60); + seconds = seconds - (mins * 60); + + res = ""; + if(days > 0){ + res += days + " d "; + } + if(hours > 0){ + res += hours + ' hr '; + } + if(mins > 0){ + res += mins + ' m '; + } + if(seconds > 0){ + res += seconds + ' s '; + } + return res; +} +""", + 'date_duration_tooltip': """ +function() { + seconds = this.y; + + days = Math.floor(seconds / 86400); + seconds = seconds - (days * 86400); + + hours = Math.floor(seconds / 3600); + seconds = seconds - (hours * 3600); + + mins = Math.floor(seconds / 60); + seconds = seconds - (mins * 60); + + res = ""; + if(days > 0){ + res += days + " d "; + } + if(hours > 0){ + res += hours + ' hr '; + } + if(mins > 0){ + res += mins + ' m '; + } + if(seconds > 0){ + res += seconds + ' s '; + } + return ''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + '
'+ this.series.name + ': ' + res; +} +""", +} + +REGEX_LIST = { + "re_funct" : re.compile(r'.*function\(.*\)\{.*\}', re.I), #for inputs such as function(xxx){xxx} + "re_hcharts" : re.compile(r'.*Highcharts.*', re.I), #for inputs such as highcharts.xxxxx +} + + +class Formatter(object): + """ Base Formatter Class """ + + def __init__(self, format=None): + ### Choose either from default functions in FORMATTER_TYPE_MAPPINGS using format_type + ### or wriet a function in format_string + if format: + if format in FORMATTER_TYPE_MAPPINGS: + self.formatter = RawJavaScriptText(FORMATTER_TYPE_MAPPINGS[format]) + elif isinstance(format, basestring): + self.formatter = RawJavaScriptText(format) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring) + + def __options__(self): + return self.formatter + + def __jsonable__(self): + return self.formatter + +class ColorObject(object): + """ color object """ + + def __init__(self, color = None, **kwargs): + if not color: + color = kwargs + + if color: + if isinstance(color, dict): + tmp = [] + for i, item in enumerate(color['stops']): + tmp.append([RawJavaScriptText(x) if isinstance(x, basestring) and any([REGEX_LIST[key].search(x) for key in REGEX_LIST.keys()]) + else x for x in item ]) + color['stops'] = tmp + self.color = color + elif any([REGEX_LIST[key].search(color) for key in REGEX_LIST.keys()]): + self.color = RawJavaScriptText(color) + elif isinstance(color, basestring): + self.color = color + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % (basestring or dict)) + else: + self.color = None + + def __options__(self): + return self.color + + def __jsonable__(self): + return self.color + + +class CSSObject(object): + """ CSS style class """ + ALLOWED_OPTIONS = {} + def __init__(self, **kwargs): + self.css = kwargs + + for k, v in self.css.items(): + if isinstance(v, basestring) and any([REGEX_LIST[key].search(v) for key in REGEX_LIST.keys()]): + v = RawJavaScriptText(v) + self.css.update({k:v}) + + def __options__(self): + return self.css + + def __jsonable__(self): + return self.css + + +class SVGObject(object): + """ SVG style class """ + + def __init__(self, **kwargs): + self.svg = kwargs + + for k, v in self.svg.items(): + if isinstance(v, basestring) and any([REGEX_LIST[key].search(v) for key in REGEX_LIST.keys()]): + v = RawJavaScriptText(v) + self.svg.update({k:v}) + + def __options__(self): + return self.svg + + def __jsonable__(self): + return self.svg + + +class JSfunction(object): + + def __init__(self, function = None): + if function: + if isinstance(function, basestring): + self.function = RawJavaScriptText(function) + elif isinstance(function, JSfunction): + self.function = function + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring) + + + def __options__(self): + return self.function + + def __jsonable__(self): + return self.function + + +class RawJavaScriptText: + + def __init__(self, jstext): + self._jstext = jstext + def get_jstext(self): + return self._jstext + + +class CommonObject(object): + + def __init__(self, **kwargs): + self.process_kwargs(kwargs) + + def __validate_options__(self, k, v, ov): + + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.__dict__ + + def __jsonable__(self): + return self.__dict__ + + def update(self, kwargs): + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), CommonObject): + # re-construct input dict with existing options in objects + if self.__getattr__(k): + if isinstance(v, dict): + self.__options__()[k].update(v) + else: + self.__options__()[k].__options__().update(v) + else: + self.__options__().update({k:allowed_args[k][0](**v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), ArrayObject): + # update array + if isinstance(v, dict): + self.__dict__[k].append(self.ALLOWED_OPTIONS[k][0](**v)) + elif isinstance(v, list): + for item in v: + self.__dict__[k].append(self.ALLOWED_OPTIONS[k][0](**item)) + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + (isinstance(self.ALLOWED_OPTIONS[k][0](), CSSObject) or isinstance(self.ALLOWED_OPTIONS[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): + self.__dict__[k].__options__().update({key:value}) + else: + self.__dict__.update({k:allowed_args[k][0](**v)}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and (isinstance(self.ALLOWED_OPTIONS[k][0](), JSfunction) or + isinstance(self.ALLOWED_OPTIONS[k][0](), Formatter) or isinstance(self.ALLOWED_OPTIONS[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + + else: + raise OptionTypeError("Not An Accepted Option Type: %s" % k) + + def process_kwargs(self, kwargs): + + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if self.__validate_options__(k,v,self.ALLOWED_OPTIONS[k]): + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + self.ALLOWED_OPTIONS[k][0] in IDV_OBJECT_LIST: + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % self.ALLOWED_OPTIONS[k]) + else: + raise OptionTypeError("Option: %s Not Allowed For Event Class:" % k) + + def __getattr__(self, item): + if not item in self.__dict__: + return None # Attribute Not Set + else: + return True + + +class Events(CommonObject): + """ Class for event listener """ + + ALLOWED_OPTIONS = { + "addSeries": (JSfunction, basestring), + "afterPrint": (JSfunction, basestring), + "beforePrint": (JSfunction, basestring), + "click": (JSfunction, basestring), + "drilldown": (JSfunction, basestring), + "drillup": (JSfunction, basestring), + "load": (JSfunction, basestring), + "redraw": (JSfunction, basestring), + "selection": (JSfunction, basestring), + "afterAnimate": (JSfunction, basestring), + "checkboxClick": (JSfunction, basestring), + "hide": (JSfunction, basestring), + "legendItemClick": (JSfunction, basestring), + "mouseOut": (JSfunction, basestring), + "mouseOver": (JSfunction, basestring), + "show": (JSfunction, basestring), + "remove": (JSfunction, basestring), + "select": (JSfunction, basestring), + "unselect": (JSfunction, basestring), + "update": (JSfunction, basestring), + "afterBreaks": (JSfunction, basestring), + "afterSetExtremes": (JSfunction, basestring), + "pointBreak": (JSfunction, basestring), + "setExtremes": (JSfunction, basestring) + } + +class Point(CommonObject): + ALLOWED_OPTIONS = { + "events": (Events, dict) + } + +class Position(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "verticalAlign": basestring, + "x": int, + "y": int, + } + +class ContextButton(CommonObject): + """ Option class for the export button """ + ALLOWED_OPTIONS = { + "align": basestring, + "enabled": bool, + "height": int, + "menuItems": NotImplemented, + "onclick": (JSfunction, basestring), + "symbol": basestring, + "symbolFill": basestring, + "symbolSize": int, + "symbolStroke": basestring, + "symbolStrokeWidth": int, + "symbolX": [float, int], + "symbolY": [float, int], + "text": basestring, + "theme": NotImplemented,#ThemeObject + "verticalAlign": basestring, + "width": int, + "x": int, + "y": int, + } + +class Back(CommonObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "size": [int, float] + } + +class Bottom(CommonObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "size": [int, float] + } + +class Side(CommonObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "size": [int, float] + } + +class Frame(CommonObject): + ALLOWED_OPTIONS = { + "back": (Back, dict), + "bottom": (Bottom, dict), + "side": (Side, dict), + } + +class Options3d(CommonObject): + ALLOWED_OPTIONS = { + "alpha": [float, int], + "beta": [float, int], + "depth": int, + "enabled": bool, + "frame": (Frame, dict), + "viewDistance": int + } + +class ResetZoomButton(CommonObject): + ALLOWED_OPTIONS = { + "position": (Position, dict), + "relativeTo": basestring, + "theme": NotImplemented #ThemeObject + } + +class DrillUpButton(CommonObject): + ALLOWED_OPTIONS = { + "position": (Position, dict), + "relativeTo": basestring, + "theme": NotImplemented #ThemeObject + }, + +class Labels(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": [float, int], + "borderWidth": [int, basestring], + "color": (ColorObject, basestring, dict), + "connectorColor": (ColorObject, basestring, dict), + "connectorPadding": [float, int], + "connectorWidth": [float, int], + "crop": bool, + "defer": bool, + "distance": int, + "enabled": bool, + "format": basestring, + "formatter": (Formatter, JSfunction, basestring), + "inside": bool, + "overflow": basestring, + "padding": [float, int], + "rotation": int, + "shadow": [bool, dict], #shadow object + "shape": basestring, + "softConnector": bool, + "staggerLines": int, + "step": int, + "style": (CSSObject, dict), + "text": basestring, + "textAlign": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + "zIndex": int, + } + +class Title(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "enabled": bool, + "margin": int, + "offset": int, + "rotation": int, + "style": (CSSObject, dict), + "text": [basestring, type(None)] + } + +class Navigation(CommonObject): + ALLOWED_OPTIONS = { + "activeColor": (ColorObject, basestring, dict), + "animation": NotImplemented, + "arrowSize": int, + "inactiveColor": (ColorObject, basestring, dict), + "style": (CSSObject, dict), + } + +class DateTimeLabelFormats(CommonObject): + ALLOWED_OPTIONS = { + "millisecond": basestring, + "second": basestring, + "minute": basestring, + "hour": basestring, + "day": basestring, + "week": basestring, + "month": basestring, + "year": basestring, + } + +class Select(CommonObject): + ALLOWED_OPTIONS = { + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "radius": int, + } + +class States(CommonObject): + ALLOWED_OPTIONS = { + "hover": dict, + "select": dict, + } + +class Marker(CommonObject): + ALLOWED_OPTIONS = { + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "height": int, + "lineWidth": int, + "lineColor": (ColorObject, basestring, dict), + "radius": int, + "states": (States, dict), + "symbol": basestring, + "width": int + } + +class Halo(CommonObject): + ALLOWED_OPTIONS = { + "attributes": (SVGObject, dict), + "opacity": float, + "size": int + } + +class Hover(CommonObject): + ALLOWED_OPTIONS = { + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "halo": (Halo, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "lineWidthPlus": int, + "marker": (Marker, dict), + "radius": int, + "radiusPlus": int, + "color": (ColorObject, basestring, dict), + } + +class States(CommonObject): + ALLOWED_OPTIONS = { + "hover": (Hover, dict), + "select": (Select, dict) + } + +class Tooltip(CommonObject): + ALLOWED_OPTIONS = { + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "followPointer": bool, + "followTouchMove": bool, + "footerFormat": basestring, + "headerFormat": basestring, + "hideDelay": int, + "pointFormat": basestring, + "pointFormatter": (Formatter, JSfunction, basestring), + "shape": basestring, + "valueDecimals": int, + "valuePrefix": basestring, + "valueSuffix": basestring, + "xDateFormat": basestring + } + + +class ArrayObject(object): + + def __init__(self, **kwargs): + self.data =[] + self.process_kwargs(kwargs) + + def __validate_options__(self, k,v,ov): + + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.data + + def __jsonable__(self): + return self.data + + def update(self, kwargs): + self.process_kwargs(kwargs) + + def process_kwargs(self, kwargs): + temp = {} + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if self.__validate_options__(k,v,self.ALLOWED_OPTIONS[k]): + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + self.ALLOWED_OPTIONS[k][0] in IDV_OBJECT_LIST: + if isinstance(v, dict): + temp.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + temp.update({k:v}) + else: + temp.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + temp.update({k:v}) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % self.ALLOWED_OPTIONS[k]) + else: + raise OptionTypeError("Option: %s Not Allowed For Event Class:" % k) + self.data.append(temp) + +class PlotBands(ArrayObject): + ALLOWED_OPTIONS = { + "borderColor": (ColorObject, basestring, dict), + "borderWidth": [int, basestring], + "color": (ColorObject, basestring, dict), + "events": (Events, dict), + "from": [int, float, datetime.datetime], + "id": basestring, + "label": (Labels, dict), + "to": [int, float, datetime.datetime], + "zIndex": int + } + +class PlotLines(ArrayObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "dashStyle": basestring, + "events": (Events, dict), + "id": basestring, + "label": (Labels, dict), + "value": [int, float], + "width": int, + "zIndex": int + } + +class Items(ArrayObject): + ALLOWED_OPTIONS = { + "html": basestring, + "style": (CSSObject, dict) + } + +class Background(ArrayObject): + ALLOWED_OPTIONS = { + "backgroundColor": (ColorObject, basestring, dict), + "borderWidth": [int, basestring], + "borderColor": (ColorObject, basestring, dict), + "innerWidth": int, + "outerWidth": int, + "outerRadius": basestring, + "shape": basestring, + } + +class Breaks(ArrayObject): + ALLOWED_OPTIONS = { + "breakSize": int, + "from": [int, float], + "repeat": int, + "to": [int, float], + } + +class DataClasses(ArrayObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "from": [int, float], + "name": basestring, + "to": [int, float], + } + +class Zones(ArrayObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "value": [int, float], + } + +class Levels(ArrayObject): + ALLOWED_OPTIONS = { + "borderColor": (ColorObject, basestring, dict), + "borderDashStyle": basestring, + "borderWidth": [int, basestring], + "color": (ColorObject, basestring, dict), + "dataLabels": (Labels, dict), + "layoutAlgorithm": basestring, + "layoutStartingDirection": basestring, + "level": int, + } + + +IDV_OBJECT_LIST = [JSfunction, Formatter, Halo, Marker, Labels, + Position, Hover, Select, Events, States, ContextButton, + CSSObject, SVGObject, ColorObject, + RawJavaScriptText, DateTimeLabelFormats] + + +class OptionTypeError(Exception): + + def __init__(self, *args): + self.args = args diff --git a/build/lib/highcharts/highcharts/highchart_types.py b/build/lib/highcharts/highcharts/highchart_types.py new file mode 100644 index 0000000..c8c241d --- /dev/null +++ b/build/lib/highcharts/highcharts/highchart_types.py @@ -0,0 +1,672 @@ +# -*- coding: UTF-8 -*- +from past.builtins import basestring + +import json, datetime +from .common import Formatter, Events, Position, ContextButton, Options3d, ResetZoomButton, DrillUpButton, \ + Labels, Marker, Point, PlotBands, States, Tooltip, Title, Zones, Levels, \ + JSfunction, ColorObject, CSSObject, SVGObject, \ + CommonObject, ArrayObject + +PLOT_OPTION_ALLOWED_ARGS = { + "common": { + "animation": bool, + "color": (ColorObject, basestring, dict), + "cursor": basestring, + "dataLabels": (Labels, dict), + "enableMouseTracking": bool, + "events": (Events, dict), + "id": basestring, + "index": [float, int], + "name": basestring, + "point": (Point, dict), + "selected": bool, + "showCheckbox": bool, + "showInLegend": bool, + "states": (States, dict), + "stickyTracking": bool, + "tooltip": (Tooltip, dict), + "visible": bool, + "xAxis": [int, basestring], + "yAxis": [int, basestring], + "zIndex": int, + "zoneAxis": basestring, + "zones": (Zones, dict), + }, + "area": { + "allowPointSelect": bool, + "connectEnds": bool, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "getExtremesFromAll": bool, + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "negativeColor": (ColorObject, basestring, dict), + "negativeFillColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": (int,basestring, datetime.datetime), + "shadow": [bool, dict], #shadow object + "stacking": basestring, + "step": bool, + "threshold": [int, type(None)], + "trackByArea": bool, + "turboThreshold": int, + }, + "arearange": { + "allowPointSelect": bool, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "getExtremesFromAll": bool, + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "negativeColor": (ColorObject, basestring, dict), + "negativeFillColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "trackByArea": bool, + "turboThreshold": int, + }, + "areaspline": { + "allowPointSelect": bool, + "cropThreshold": int, + "connectEnds": bool, + "connectNulls": bool, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "getExtremesFromAll": bool, + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "negativeColor": (ColorObject, basestring, dict), + "negativeFillColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "threshold": [int, type(None)], + "turboThreshold": int, + "trackByArea": bool, + }, + "areasplinerange": { + "allowPointSelect": bool, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "pointInterval": int, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "turboThreshold": int, + "trackByArea": bool, + }, + "bar": { + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": [int, basestring], + "colorByPoint": bool, + "colors": list, + "cropThreshold": int, + "depth": [int, float], + "edgeColor": (ColorObject, basestring, dict), + "edgeWidth": int, + "getExtremesFromAll": bool, + "groupPadding": [float, int], + "groupZPadding": [float, int], + "grouping": bool, + "keys": list, + "linkedTo": basestring, + "minPointLength": int, + "negativeColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPadding": [float, int], + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "pointWidth": int, + "shadow": [bool, dict], + "stacking": basestring, + "threshold": [int, type(None)], + "turboThreshold": int, + }, + "boxplot": { + "allowPointSelect": bool, + "colorByPoint": bool, + "colors": list, + "depth": [int, float], + "edgeColor": (ColorObject, basestring, dict), + "edgeWidth": int, + "fillColor": (ColorObject, basestring, dict), + "getExtremesFromAll": bool, + "groupPadding": [float, int], + "groupZPadding": [float, int], + "grouping": bool, + "keys": list, + "lineWidth": int, + "linkedTo": basestring, + "medianColor": (ColorObject, basestring, dict), + "medianWidth": [int,float], + "negativeColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPadding": [float, int], + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "pointWidth": int, + "shadow": [bool, dict], + "size": [int,basestring], + "slicedOffset": int, + "startAngle": int, + "showInLegend": bool + }, + "bubble": { + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": int, + "colors": list, + "colorByPoint": bool, + "cropThreshold": int, + "depth": [int, float], + "edgeColor": (ColorObject, basestring, dict), + "edgeWidth": int, + "getExtremesFromAll": bool, + "groupPadding": [float, int], + "groupZPadding": [float, int], + "grouping": bool, + "keys": list, + "linkedTo": basestring, + "minPointLength": int, + "negativeColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPadding": [float, int], + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "pointWidth": int, + "shadow": [bool, dict], + "stacking": basestring, + "threshold": [int, type(None)], + "turboThreshold": int, + }, + "column": { + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": [int, basestring], + "colors": list, + "colorByPoint": bool, + "cropThreshold": int, + "depth": [int, float], + "edgeColor": (ColorObject, basestring, dict), + "edgeWidth": int, + "getExtremesFromAll": bool, + "groupPadding": [float, int], + "groupZPadding": [float, int], + "grouping": bool, + "keys": list, + "linkedTo": basestring, + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "minPointLength": int, + "negativeColor": (ColorObject, basestring, dict), + "pointPadding": [float, int], + "pointRange": int, + "pointWidth": [int, float], + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stacking": basestring, + "threshold": [int, type(None)], + "turboThreshold": int, + }, + "columnrange": { + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": [int, basestring], + "colors": list, + "colorByPoint": bool, + "depth": [int, float], + "edgeColor": (ColorObject, basestring, dict), + "edgeWidth": int, + "getExtremesFromAll": bool, + "cropThreshold": int, + "groupPadding": [float, int], + "groupZPadding": [float, int], + "grouping": bool, + "keys": list, + "linkedTo": basestring, + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "minPointLength": int, + "negativeColor": (ColorObject, basestring, dict), + "pointPadding": [float, int], + "pointRange": int, + "pointWidth": int, + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stacking": basestring, + "threshold": [int, type(None)], + "turboThreshold": int, + }, + "errorbar": { + "allowPointSelect": bool, + "colors": list, + "colorByPoint": bool, + "cursor": basestring, + "depth": [int, float], + "edgeColor": (ColorObject, basestring, dict), + "edgeWidth": int, + "getExtremesFromAll": bool, + "cropThreshold": int, + "groupZPadding": [float, int], + "keys": list, + "linkedTo": basestring, + "lineWidth": int, + "negativeColor": (ColorObject, basestring, dict), + "pointPadding": [float, int], + "pointRange": int, + "pointWidth": int, + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "stemColor": (ColorObject, basestring, dict), + "stemDashStyle": basestring, + "stemWidth": [float, int], + "stickyTracking": bool, + "turboThreshold": int, + "whiskerColor": (ColorObject, basestring, dict), + "whiskerLength": [float, int, basestring], + "whiskerWidth": [float, int] + }, + "gauge": { + "dial": NotImplemented, + "linkedTo": basestring, + "negativeColor": (ColorObject, basestring, dict), + "overshoot": [int, float], + "pivot": NotImplemented, + "stickyTracking": bool, + "threshold": [int, type(None)], + "wrap": bool, + }, + "heatmap": { + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": [int, basestring], + "colors": list, + "colorByPoint": bool, + "colsize": int, + "cropThreshold": int, + "getExtremesFromAll": bool, + "keys": list, + "legendIndex": [int, float], + "linkedTo": basestring, + "rowsize": int, + "shadow": [bool, dict], #shadow object + "stickyTracking": bool, + "turboThreshold": int, + }, + "line": { + "allowPointSelect": bool, + "connectEnds": bool, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "getExtremesFromAll": bool, + "keys": list, + "legendIndex": [int, float], + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "negativeColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "step": basestring, + "turboThreshold": int, + }, + "pie": { + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderWidth": [int, basestring], + "center": list, + "colors": list, + "depth": [int, float], + "endAngle": [int, float], + "ignoreHiddenPoint": bool, + "innerSize": [int, basestring], + "legendIndex": [int, basestring], + "linkedTo": basestring, + "minSize": [int, basestring], + "shadow": [bool, dict], + "showInLegend": bool, + "size": [int,basestring], + "slicedOffset": int, + "startAngle": int, + }, + "scatter": { + "allowPointSelect": bool, + "colorByPoint": bool, + "cropThreshold": int, + "dashStyle": basestring, + "keys": list, + "legendIndex": [int, float], + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "negativeColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "threshold": [int, float], + "turboThreshold": int, + }, + "series": { + "allowPointSelect": bool, + "borderWidth": [int, basestring], + "connectEnds": bool, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "lineWidth": int, + "marker": (Marker, dict), + "pointInterval": int, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stacking": basestring, + "turboThreshold": int, + }, + "spline": { + "allowPointSelect": bool, + "connectEnds": bool, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "pointInterval": int, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stacking": basestring, + "turboThreshold": int, + }, + "treemap": { + "allowDrillToNode": bool, + "allowPointSelect": bool, + "alternateStartingDirection": bool, + "borderColor": (ColorObject, basestring, dict), + "borderWidth": [int, basestring], + "colors": list, + "colorByPoint": bool, + "cropThreshold": int, + "getExtremesFromAll": bool, + "interactByLeaf": bool, + "keys": list, + "layoutAlgorithm": basestring, + "layoutStartingDirection": basestring, + "legendIndex": [int, float], + "levelIsConstant": bool, + "levels": (Levels, dict, list), + "linkedTo": basestring, + "shadow": [bool, dict], + "stickyTracking": bool, + "turboThreshold": int, + }, +} + +DATA_SERIES_ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "connectEnds": bool, + "connectNulls": bool, + "dataLabels": (Labels, dict), + "dataParser": NotImplemented, + "dataURL": NotImplemented, + "drilldown": basestring, + "events": (Events, dict), + "high": [int, float], + "id": basestring, + "index": int, + "legendIndex": int, + "name": basestring, + "marker": (Marker, dict), + "selected": bool, + "sliced": bool, + "showInLegend": bool, + "stack": basestring, + "type": basestring, + "visible": bool, + "x": [int, float], + "xAxis": int, + "yAxis": int, +} + +DEFAULT_OPTIONS = { + +} + +class OptionTypeError(Exception): + + def __init__(self,*args): + self.args = args + + +class SeriesOptions(object): + """Class for plotOptions""" + + def __init__(self,series_type="line",supress_errors=False,**kwargs): + self.load_defaults(series_type) + self.process_kwargs(kwargs,series_type=series_type,supress_errors=supress_errors) + + @staticmethod + def __validate_options__(k,v,ov): + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.__dict__ + + def __jsonable__(self): + return self.__dict__ + + def update(self,series_type, **kwargs): + allowed_args = PLOT_OPTION_ALLOWED_ARGS[series_type] + allowed_args.update(PLOT_OPTION_ALLOWED_ARGS["common"]) + for k, v in kwargs.items(): + if k in allowed_args: + if SeriesOptions.__validate_options__(k,v,allowed_args[k]): + if isinstance(allowed_args[k], tuple) and isinstance(allowed_args[k][0](), CommonObject): + # re-construct input dict with existing options in objects + if self.__getattr__(k): + if isinstance(v, dict): + self.__options__()[k].update(v) + else: + self.__options__()[k].__options__().update(v) + else: + self.__options__().update({k:allowed_args[k][0](**v)}) + + elif isinstance(allowed_args[k], tuple) and isinstance(allowed_args[k][0](), ArrayObject): + # update array + if isinstance(v, dict): + self.__dict__[k].append(allowed_args[k][0](**v)) + elif isinstance(v, list): + for item in v: + self.__dict__[k].append(allowed_args[k][0](**item)) + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(v)) + + elif isinstance(allowed_args[k], tuple) and \ + (isinstance(allowed_args[k][0](), CSSObject) or isinstance(allowed_args[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): + self.__dict__[k].__options__().update({key:value}) + else: + self.__dict__.update({k:allowed_args[k][0](**v)}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + + elif isinstance(allowed_args[k], tuple) and (isinstance(allowed_args[k][0](), JSfunction) or + isinstance(allowed_args[k][0](), Formatter) or isinstance(allowed_args[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + print(k,v) + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % allowed_args[k]) + + + def process_kwargs(self,kwargs,series_type,supress_errors=False): + allowed_args = PLOT_OPTION_ALLOWED_ARGS[series_type] + allowed_args.update(PLOT_OPTION_ALLOWED_ARGS["common"]) + + for k, v in kwargs.items(): + if k in allowed_args: + if SeriesOptions.__validate_options__(k,v,allowed_args[k]): + if isinstance(allowed_args[k], tuple): + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + elif isinstance(v, list): + if len(v) == 1: + self.__dict__.update({k:allowed_args[k][0](**v[0])}) + else: + self.__dict__.update({k:allowed_args[k][0](**v[0])}) + for item in v[1:]: + self.__dict__[k].update(item) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + print(k,v) + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % allowed_args[k]) + + + def load_defaults(self,series_type): # not in use + self.process_kwargs(DEFAULT_OPTIONS.get(series_type,{}),series_type) + + def __getattr__(self,item): + if not item in self.__dict__: + return None # Attribute Not Set + else: + return True + + +class Series(object): + """Series class for input data """ + + def __init__(self, data, series_type="line", supress_errors=False, **kwargs): + + # List of dictionaries. Each dict contains data and properties, + # which need to handle before construct the object for series + if isinstance(data, list): + for item in data: + if isinstance(item, dict): + for k, v in item.items(): + if k in DATA_SERIES_ALLOWED_OPTIONS: + if SeriesOptions.__validate_options__(k,v,DATA_SERIES_ALLOWED_OPTIONS[k]): + if isinstance(DATA_SERIES_ALLOWED_OPTIONS[k], tuple): + if isinstance(v, dict): + item.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + item.update({k:v}) + else: + item.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](v)}) + else: + item.update({k:v}) + + self.__dict__.update({ + "data": data, + "type": series_type, + }) + + # Series propertie can be input as kwargs, which is handled here + for k, v in kwargs.items(): + if k in DATA_SERIES_ALLOWED_OPTIONS: + if SeriesOptions.__validate_options__(k,v,DATA_SERIES_ALLOWED_OPTIONS[k]): + if isinstance(DATA_SERIES_ALLOWED_OPTIONS[k], tuple): + if isinstance(v, dict): + self.__dict__.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % DATA_SERIES_ALLOWED_OPTIONS[k]) + + + def __options__(self): + return self.__dict__ + + def __jsonable__(self): + return self.__dict__ diff --git a/build/lib/highcharts/highcharts/highcharts.py b/build/lib/highcharts/highcharts/highcharts.py new file mode 100644 index 0000000..6262c42 --- /dev/null +++ b/build/lib/highcharts/highcharts/highcharts.py @@ -0,0 +1,473 @@ +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals, absolute_import +from future.standard_library import install_aliases +install_aliases() +from past.builtins import basestring + +from jinja2 import Environment, PackageLoader + +import json, uuid +import re +import datetime +import urllib.request, urllib.error, urllib.parse +import html +try: + # works with older (3.9 and below) versions, throws an exception with newer (3.10 and above) versions + from collections import Iterable +#if an import error change to trying 3.10 and above notation +except ImportError: + from collections.abc import Iterable +from .options import BaseOptions, ChartOptions, ColorAxisOptions, \ + ColorsOptions, CreditsOptions, DrilldownOptions, ExportingOptions, \ + GlobalOptions, LabelsOptions, LangOptions, \ + LegendOptions, LoadingOptions, NavigationOptions, PaneOptions, \ + PlotOptions, SeriesData, SubtitleOptions, TitleOptions, \ + TooltipOptions, xAxisOptions, yAxisOptions, zAxisOptions, MultiAxis + +from .highchart_types import Series, SeriesOptions +from .common import Levels, Formatter, CSSObject, SVGObject, JSfunction, RawJavaScriptText, \ + CommonObject, ArrayObject, ColorObject + +CONTENT_FILENAME = "content.html" +PAGE_FILENAME = "page.html" + +pl = PackageLoader('highcharts.highcharts', 'templates') +jinja2_env = Environment(lstrip_blocks=True, trim_blocks=True, loader=pl) + +template_content = jinja2_env.get_template(CONTENT_FILENAME) +template_page = jinja2_env.get_template(PAGE_FILENAME) + + +class Highchart(object): + """ + Highcharts Base class. + """ + #: chart count + count = 0 + + # this attribute is overriden by children of this + # class + CHART_FILENAME = None + template_environment = Environment(lstrip_blocks=True, trim_blocks=True, + loader=pl) + + def __init__(self, **kwargs): + """ + This is the base class for all the charts. The following keywords are + accepted: + :keyword: **display_container** - default: ``True`` + **offline - default: ``False`` + If True, download all .js and .css file and put them + into the generated .html so it can be viewed offline. + """ + # set the model + self.model = self.__class__.__name__ #: The chart model, + self.div_name = kwargs.get("renderTo", "container") + + # an Instance of Jinja2 template + self.template_page_highcharts = template_page + self.template_content_highcharts = template_content + + + # set Javascript src, Highcharts lib needs to make sure it's up to date + self.JSsource = [ + 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js', + 'https://code.highcharts.com/6/highcharts.js', + 'https://code.highcharts.com/6/highcharts-more.js', + 'https://code.highcharts.com/6/modules/heatmap.js', + 'https://code.highcharts.com/6/modules/exporting.js' + ] + + # set CSS src + self.CSSsource = [ + 'https://www.highcharts.com/highslide/highslide.css', + + ] + + self.offline = kwargs.get("offline", False) + + # set data + self.data = [] + self.data_temp = [] + # Data from jsonp + self.jsonp_data_flag = False + self.jsonp_data_url_list = [] # DEM 2017/07/27: List of JSON data sources + + # set drilldown data + self.drilldown_data = [] + self.drilldown_data_temp = [] + + # javascript + self.jscript_head_flag = False + self.jscript_head = kwargs.get('jscript_head', None) + self.jscript_end_flag = False + self.jscript_end = kwargs.get('jscript_end', None) + + # accepted keywords + self.div_style = kwargs.get('style', '') + self.drilldown_flag = kwargs.get('drilldown_flag', False) + + # None keywords attribute that should be modified by methods + # We should change all these to _attr + + self._htmlcontent = '' #: written by buildhtml + self.htmlheader = '' + #: Place holder for the graph (the HTML div) + #: Written by ``buildcontainer`` + self.container = u'' + #: Header for javascript code + self.containerheader = u'' + # Loading message + self.loading = 'Loading....' + + # Bind Base Classes to self + self.options = { + "chart": ChartOptions(), + #"colorAxis" : ColorAxisOptions(), + "colors": ColorsOptions(), + "credits": CreditsOptions(), + #"data": #NotImplemented + "drilldown": DrilldownOptions(), + "exporting": ExportingOptions(), + "labels": LabelsOptions(), + "legend": LegendOptions(), + "loading": LoadingOptions(), + "navigation": NavigationOptions(), + "pane": PaneOptions(), + "plotOptions": PlotOptions(), + "series": SeriesData(), + "subtitle": SubtitleOptions(), + "title": TitleOptions(), + "tooltip": TooltipOptions(), + "xAxis": xAxisOptions(), + "yAxis": yAxisOptions(), + } + + self.setOptions = { + "global": GlobalOptions(), + "lang": LangOptions(), + } + + self.__load_defaults__() + + # Process kwargs + allowed_kwargs = [ + "width", + "height", + "renderTo", + "backgroundColor", + "events", + "marginBottom", + "marginTop", + "marginRight", + "marginLeft" + ] + + for keyword in allowed_kwargs: + if keyword in kwargs: + self.options['chart'].update_dict(**{keyword:kwargs[keyword]}) + # Some Extra Vals to store: + self.data_set_count = 0 + self.drilldown_data_set_count = 0 + + + def __load_defaults__(self): + self.options["chart"].update_dict(renderTo='container') + self.options["title"].update_dict(text='A New Highchart') + self.options["credits"].update_dict(enabled=False) + + + def add_JSsource(self, new_src): + """add additional js script source(s)""" + if isinstance(new_src, list): + for h in new_src: + self.JSsource.append(h) + elif isinstance(new_src, basestring): + self.JSsource.append(new_src) + else: + raise OptionTypeError("Option: %s Not Allowed For Series Type: %s" % type(new_src)) + + + def add_CSSsource(self, new_src): + """add additional css source(s)""" + if isinstance(new_src, list): + for h in new_src: + self.CSSsource.append(h) + elif isinstance(new_src, basestring): + self.CSSsource.append(new_src) + else: + raise OptionTypeError("Option: %s Not Allowed For Series Type: %s" % type(new_src)) + + + def add_3d_rotation(self): + self.options3d_rotation_flag = True + + def add_data_set(self, data, series_type="line", name=None, **kwargs): + """set data for series option in highcharts""" + + self.data_set_count += 1 + if not name: + name = "Series %d" % self.data_set_count + kwargs.update({'name':name}) + + if series_type == 'treemap': + self.add_JSsource('http://code.highcharts.com/modules/treemap.js') + + series_data = Series(data, series_type=series_type, **kwargs) + + series_data.__options__().update(SeriesOptions(series_type=series_type, **kwargs).__options__()) + self.data_temp.append(series_data) + + + def add_drilldown_data_set(self, data, series_type, id, **kwargs): + """set data for drilldown option in highcharts""" + + self.drilldown_data_set_count += 1 + if self.drilldown_flag == False: + self.drilldown_flag = True + + kwargs.update({'id':id}) + series_data = Series(data, series_type=series_type, **kwargs) + + series_data.__options__().update(SeriesOptions(series_type=series_type, **kwargs).__options__()) + self.drilldown_data_temp.append(series_data) + + + def add_data_from_jsonp(self, data_src, data_name='json_data', series_type="line", name=None, **kwargs): + """set map data directly from a https source + the data_src is the https link for data + and it must be in jsonp format + """ + if not self.jsonp_data_flag: + self.jsonp_data_flag = True + + if data_name == 'data': + data_name = 'json_'+ data_name + + self.jsonp_data = data_name + self.add_data_set(RawJavaScriptText(data_name), series_type, name=name, **kwargs) + # DEM 2017/07/27: Append new JSON data source to a list instead of + # replacing whatever already exists + self.jsonp_data_url_list.append(json.dumps(data_src)) + + + def add_JSscript(self, js_script, js_loc): + """add (highcharts) javascript in the beginning or at the end of script + use only if necessary + """ + if js_loc == 'head': + self.jscript_head_flag = True + if self.jscript_head: + self.jscript_head = self.jscript_head + '\n' + js_script + else: + self.jscript_head = js_script + elif js_loc == 'end': + self.jscript_end_flag = True + if self.jscript_end: + self.jscript_end = self.jscript_end + '\n' + js_script + else: + self.jscript_end = js_script + else: + raise OptionTypeError("Not An Accepted script location: %s, either 'head' or 'end'" + % js_loc) + + + def set_options(self, option_type, option_dict, force_options=False): + """set plot options """ + if force_options: + self.options[option_type].update(option_dict) + elif (option_type == 'yAxis' or option_type == 'xAxis') and isinstance(option_dict, list): + # For multi-Axis + self.options[option_type] = MultiAxis(option_type) + for each_dict in option_dict: + self.options[option_type].update(**each_dict) + elif option_type == 'colors': + self.options["colors"].set_colors(option_dict) # option_dict should be a list + elif option_type == 'zAxis': + self.options.update({'zAxis': zAxisOptions()}) + self.options[option_type].update_dict(**option_dict) + elif option_type in ["global" , "lang"]: #Highcharts.setOptions: + self.setOptions[option_type].update_dict(**option_dict) + elif option_type == 'colorAxis': + self.options.update({'colorAxis': ColorAxisOptions()}) + self.options[option_type].update_dict(**option_dict) + else: + self.options[option_type].update_dict(**option_dict) + + if option_type == 'chart' and 'options3d' in option_dict: + # Add 3d.js into Javascript source header + self.add_JSsource("http://code.highcharts.com/highcharts-3d.js") + + + def set_dict_options(self, options): + """for dictionary-like inputs (as object in Javascript) + options must be in python dictionary format + """ + if isinstance(options, dict): + for key, option_data in options.items(): + self.set_options(key, option_data) + else: + raise OptionTypeError("Not An Accepted Input Format: %s. Must be Dictionary" %type(options)) + + + def buildcontent(self): + """build HTML content only, no header or body tags""" + + self.buildcontainer() + self.option = json.dumps(self.options, cls = HighchartsEncoder) + self.setoption = json.dumps(self.setOptions, cls = HighchartsEncoder) + self.data = json.dumps(self.data_temp, cls = HighchartsEncoder) + + # DEM 2017/04/25: Make 'data' available as an array + # ... this permits jinja2 array access to each data definition + # ... which is useful for looping over multiple data sources + self.data_list = [json.dumps(x, cls = HighchartsEncoder) for x in self.data_temp] + + if self.drilldown_flag: + self.drilldown_data = json.dumps(self.drilldown_data_temp, cls = HighchartsEncoder) + self._htmlcontent = self.template_content_highcharts.render(chart=self).encode('utf-8') + + + def buildhtml(self): + """build the HTML page + create the htmlheader with css / js + create html page + """ + self.buildcontent() + self.buildhtmlheader() + self.content = self._htmlcontent.decode('utf-8') # need to ensure unicode + self._htmlcontent = self.template_page_highcharts.render(chart=self) + return self._htmlcontent + + + def buildhtmlheader(self): + """generate HTML header content""" + + if self.drilldown_flag: + self.add_JSsource('http://code.highcharts.com/modules/drilldown.js') + + + + if self.offline: + opener = urllib.request.build_opener() + opener.addheaders = [('User-Agent', 'Mozilla/5.0')] + + self.header_css = [ + '' % opener.open(h).read() for h in self.CSSsource + ] + + self.header_js = [ + '' % opener.open(h).read() for h in self.JSsource + ] + else: + self.header_css = [ + '' % h for h in self.CSSsource + ] + + self.header_js = [ + '' % h for h in self.JSsource + ] + + self.htmlheader = '' + for css in self.header_css: + self.htmlheader += css + for js in self.header_js: + self.htmlheader += js + + + def buildcontainer(self): + """generate HTML div""" + if self.container: + return + # Create HTML div with style + if self.options['chart'].width: + if str(self.options['chart'].width)[-1] != '%': + self.div_style += 'width:%spx;' % self.options['chart'].width + else: + self.div_style += 'width:%s;' % self.options['chart'].width + if self.options['chart'].height: + if str(self.options['chart'].height)[-1] != '%': + self.div_style += 'height:%spx;' % self.options['chart'].height + else: + self.div_style += 'height:%s;' % self.options['chart'].height + + self.div_name = self.options['chart'].__dict__['renderTo'] # recheck div name + self.container = self.containerheader + \ + '
%s
\n' % (self.div_name, self.div_style, self.loading) + + @property + def htmlcontent(self): + return self.buildhtml() + + @property + def iframe(self): + htmlsrcdoc = html.escape(self.htmlcontent) + htmlsrcdoc = re.sub('\\n', ' ', htmlsrcdoc) + htmlsrcdoc = re.sub(' +', ' ', htmlsrcdoc) + width = int(self.options['chart'].__dict__['width']) if self.options['chart'].__dict__.get('width') else 820 + height = int(self.options['chart'].__dict__['height']) if self.options['chart'].__dict__.get('height') else 520 + + if self.options['chart'].__dict__.get('options3d'): + if len(htmlsrcdoc) < 99965000 : + return '' + else: + return '' + else: + return '' + + + def __str__(self): + """return htmlcontent""" + #self.buildhtml() + return self.htmlcontent + + def save_file(self, filename = 'Chart'): + """ save htmlcontent as .html file """ + filename = filename + '.html' + + with open(filename, 'w') as f: + #self.buildhtml() + f.write(self.htmlcontent) + + f.closed + +class HighchartsEncoder(json.JSONEncoder): + def __init__(self, *args, **kwargs): + json.JSONEncoder.__init__(self, *args, **kwargs) + self._replacement_map = {} + + def default(self, obj): + if isinstance(obj, RawJavaScriptText): + key = uuid.uuid4().hex + self._replacement_map[key] = obj.get_jstext() + return key + elif isinstance(obj, datetime.datetime): + utc = obj.utctimetuple() + obj = (u"Date.UTC({year},{month},{day},{hours},{minutes},{seconds},{millisec})" + .format(year=utc[0], month=utc[1]-1, day=utc[2], hours=utc[3], + minutes=utc[4], seconds=utc[5], millisec=obj.microsecond/1000)) + return RawJavaScriptText(obj) + elif isinstance(obj, BaseOptions) or isinstance(obj, MultiAxis): + return obj.__jsonable__() + elif isinstance(obj, CSSObject) or isinstance(obj, Formatter) or isinstance(obj, JSfunction): + return obj.__jsonable__() + elif isinstance(obj, SeriesOptions) or isinstance(obj, Series): + return obj.__jsonable__() + elif isinstance(obj, CommonObject) or isinstance(obj, ArrayObject) or isinstance(obj, ColorObject): + return obj.__jsonable__() + else: + return json.JSONEncoder.default(self, obj) + + def encode(self, obj): + result = json.JSONEncoder.encode(self, obj) + for k, v in self._replacement_map.items(): + result = result.replace('"{}"'.format(k), v) + return result + + +class OptionTypeError(Exception): + + def __init__(self,*args): + self.args = args diff --git a/build/lib/highcharts/highcharts/options.py b/build/lib/highcharts/highcharts/options.py new file mode 100644 index 0000000..d91b827 --- /dev/null +++ b/build/lib/highcharts/highcharts/options.py @@ -0,0 +1,657 @@ +# -*- coding: UTF-8 -*- +from past.builtins import basestring + +from .highchart_types import OptionTypeError, Series, SeriesOptions +from .common import Formatter, Events, Position, ContextButton, Options3d, ResetZoomButton, \ + DrillUpButton, Labels, PlotBands, PlotLines, Title, Items, Navigation, Background, Breaks, \ + DataClasses, DateTimeLabelFormats, Zones, Levels, Marker, \ + JSfunction, ColorObject, CSSObject, SVGObject, CommonObject, ArrayObject + +import json, datetime + +# Base Option Class +class BaseOptions(object): + + def __init__(self,**kwargs): + self.update_dict(**kwargs) + + def __display_options__(self): + print(json.dumps(self.__dict__, indent=4, sort_keys=True)) + + def __jsonable__(self): + return self.__dict__ + + def __validate_options__(self, k, v, ov): + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,dict) and isinstance(ov,dict): + keys = v.keys() + if len(keys) > 1: + raise NotImplementedError + return isinstance(v[keys[0]],ov[keys[0]]) + return isinstance(v, ov) + + def update_dict(self, **kwargs): + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + # if isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), SeriesOptions): + if k in PlotOptions.ALLOWED_OPTIONS.keys(): + if self.__getattr__(k): + self.__dict__[k].update(series_type=k, **v) + else: + v = SeriesOptions(series_type=k, **v) + self.__dict__.update({k:v}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), CommonObject): + if isinstance(v, dict): + if self.__getattr__(k): + self.__dict__[k].update(v) #update dict + else: # first + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + OptionTypeError("Not An Accepted Input Type: %s, must be dictionary" % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), ArrayObject): + if self.__getattr__(k): #existing attr + if isinstance(v, dict): + self.__dict__[k].update(v) # update array + elif isinstance(v, list): + for item in v: + self.__dict__[k].update(item) # update array + else: + OptionTypeError("Not An Accepted Input Type: %s, must be list or dictionary" + % type(v)) + else: #first + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, list): + if len(v) == 1: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v[0])}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v[0])}) + for item in v[1:]: + self.__dict__[k].update(item) + else: + OptionTypeError("Not An Accepted Input Type: %s, must be list or dictionary" + % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + (isinstance(self.ALLOWED_OPTIONS[k][0](), CSSObject) or isinstance(self.ALLOWED_OPTIONS[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): # check if v has object input + self.__dict__[k].__options__().update({key:value}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and (isinstance(self.ALLOWED_OPTIONS[k][0](), JSfunction) or \ + isinstance(self.ALLOWED_OPTIONS[k][0](), Formatter) or isinstance(self.ALLOWED_OPTIONS[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + + else: + print(self.ALLOWED_OPTIONS) + print(k, v) + raise OptionTypeError("Not An Accepted Option Type: %s" % k) + + + def __getattr__(self, item): + if not item in self.__dict__: + return None # Attribute Not Set + else: + return True + + +class ChartOptions(BaseOptions): + ALLOWED_OPTIONS = { + "alignTicks": bool, + "animation": [bool, dict, basestring], + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": int, + "className": basestring, + "defaultSeriesType": basestring, + "events": (Events, dict), + "height": [int,basestring], + "ignoreHiddenSeries": bool, + "inverted": bool, + "margin": list, + "marginBottom": int, + "marginLeft": int, + "marginRight": int, + "marginTop": int, + "options3d": (Options3d, dict), + "plotBackgroundColor": (ColorObject, basestring, dict), + "plotBackgroundImage": basestring, + "plotBorderColor": (ColorObject, basestring, dict), + "plotBorderWidth": int, + "plotShadow": bool, + "polar": bool, + "reflow": bool, + "renderTo": basestring, + "resetZoomButton": (ResetZoomButton, dict), + "selectionMarkerFill": basestring, + "shadow": bool, + "showAxes": bool, + "spacingBottom": int, + "spacingLeft": int, + "spacingRight": int, + "spacingTop": int, + "style": (CSSObject, dict), + "type": basestring, + "width": [int,basestring], + "zoomType": basestring, + } + + +class ColorAxisOptions(BaseOptions): + ALLOWED_OPTIONS = { + "dataClassColor": basestring, + "dataClasses": (DataClasses, dict), + "endOnTick": bool, + "events": (Events, dict), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineWidth": [float, int], + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": [float, int], + "marker": (Marker, dict), + "max": [float, int], + "maxColor": (ColorObject, basestring, dict), + "maxPadding": [float, int], + "min": [float, int], + "minColor": (ColorObject, basestring, dict), + "minPadding": [float, int], + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "reversed": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "startOfWeek": int, + "startOnTick": bool, + "stops": list, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": JSfunction, + "tickPositions": list, + "tickWidth": int, + "type": basestring, +} + + +class ColorsOptions(BaseOptions): + """ Special Case, this is simply just an array of colours """ + def __init__(self): + self.colors = {} + + def set_colors(self, colors): + if isinstance(colors, basestring): + self.colors = ColorObject(colors) + elif isinstance(colors, list) or isinstance(colors, dict): + self.colors = colors + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(colors)) + + + def __jsonable__(self): + return self.colors + + +class CreditsOptions(BaseOptions): + ALLOWED_OPTIONS = { + "enabled": bool, + "href": basestring, + "position": (Position, dict), + "style": (CSSObject, dict), + "text": basestring, + } + + +class DrilldownOptions(BaseOptions): #not implement yet, need work in jinjia + ALLOWED_OPTIONS = { + "activeAxisLabelStyle": (CSSObject, dict), + "activeDataLabelStyle": (CSSObject, dict), + "animation": NotImplemented, #(bool, dict), #not sure how to implement + "drillUpButton": (DrillUpButton, dict), + "series": (SeriesOptions, dict), + } + + +class ExportingOptions(BaseOptions): + ALLOWED_OPTIONS = { + "buttons": (ContextButton, dict), + "chartOptions": (ChartOptions, dict), + "enabled": bool, + "filename": basestring, + "formAttributes": NotImplemented, + "scale": int, + "sourceHeight": int, + "sourceWidth": int, + "type": basestring, + "url": basestring, + "width": int, + } + + +class GlobalOptions(BaseOptions): + ALLOWED_OPTIONS = { + "Date": NotImplemented, + "VMLRadialGradientURL": basestring, + "canvasToolsURL": basestring, + "getTimezoneOffset": (JSfunction, basestring), + "timezoneOffset": int, + "useUTC": bool, + } + + +class LabelsOptions(BaseOptions): + ALLOWED_OPTIONS = { + "items": (Items, dict), + "style": (CSSObject, dict), + } + + +class LangOptions(BaseOptions): + ALLOWED_OPTIONS = { + "decimalPoint": basestring, + "downloadJPEG": basestring, + "downloadPDF": basestring, + "downloadPNG": basestring, + "donwloadSVG": basestring, + "exportButtonTitle": basestring, + "loading": basestring, + "months": list, + "noData": basestring, + "numericSymbols": list, + "printButtonTitle": basestring, + "resetZoom": basestring, + "resetZoomTitle": basestring, + "shortMonths": list, + "thousandsSep": basestring, + "weekdays": list, + } + + +class LegendOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": int, + "enabled": bool, + "floating": bool, + "itemDistance": int, + "itemHiddenStyle": (CSSObject, dict), + "itemHoverStyle": (CSSObject, dict), + "itemMarginBottom": int, + "itemMarginTop": int, + "itemStyle": (CSSObject, dict), + "itemWidth": int, + "labelFormat": basestring, + "labelFormatter": (Formatter, JSfunction), + "layout": basestring, + "lineHeight": int, + "margin": int, + "maxHeight": int, + "navigation": (Navigation, dict), + "padding": int, + "reversed": bool, + "rtl": bool, + "shadow": bool, + "style": (CSSObject, dict), + "symbolHeight": int, + "symbolPadding": int, + "symbolRadius": int, + "symbolWidth": int, + "title": (Title, dict), + "useHTML": bool, + "verticalAlign": basestring, + "width": int, + "x": int, + "y": int, + } + + +class LoadingOptions(BaseOptions): + ALLOWED_OPTIONS = { + "hideDuration": int, + "labelStyle": (CSSObject, dict), + "showDuration": int, + "style": (CSSObject, dict), + } + + +class NavigationOptions(BaseOptions): + ALLOWED_OPTIONS = { + "buttonOptions": (ContextButton, dict), + "menuItemHoverStyle": (CSSObject, dict), + "menuItemStyle": (CSSObject, dict), + "menuStyle": (CSSObject, dict), + } + + +class PaneOptions(BaseOptions): + ALLOWED_OPTIONS = { + "background": (Background, list), #arrayObject + "center": list, + "endAngle": int, + "size": int, + "startAngle": int, + } + + +class PlotOptions(BaseOptions): + """ Another Special Case: Interface With all the different Highchart Plot Types Here """ + ALLOWED_OPTIONS = { + "area": (SeriesOptions, dict), + "arearange": (SeriesOptions, dict), + "areaspline": (SeriesOptions, dict), + "areasplinerange": (SeriesOptions, dict), + "bar": (SeriesOptions, dict), + "boxplot": (SeriesOptions, dict), + "bubble": (SeriesOptions, dict), + "column": (SeriesOptions, dict), + "columnrange": (SeriesOptions, dict), + "errorbar": (SeriesOptions, dict), + "gauge": (SeriesOptions, dict), + "heatmap": (SeriesOptions, dict), + "line": (SeriesOptions, dict), + "pie": (SeriesOptions, dict), + "scatter": (SeriesOptions, dict), + "series": (SeriesOptions, dict), + "spline": (SeriesOptions, dict), + "treemap": (SeriesOptions, dict), + } + + +class SeriesData(BaseOptions): + """ Another Special Case: Stores Data Series in an array for returning to the chart object """ + def __init__(self): + #self.__dict__.update([]) + self = [] + + +class SubtitleOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "floating": bool, + "style": (CSSObject, dict), + "text": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + } + + +class TitleOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "floating": bool, + "margin": int, + "style": (CSSObject, dict), + "text": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + } + + +class TooltipOptions(BaseOptions): + ALLOWED_OPTIONS = { + "animation": bool, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": int, + "crosshairs": [bool, list, dict], + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "enabled": bool, + "followPointer": bool, + "followTouchMove": bool, + "footerFormat": basestring, + "formatter": (Formatter, JSfunction), + "headerFormat": basestring, + "pointFormat": basestring, + "pointFormatter": (Formatter, JSfunction), + "positioner": (JSfunction, basestring), + "shadow": bool, + "shared": bool, + "snap": int, + "style": (CSSObject, dict), + "useHTML": bool, + "valueDecimals": int, + "valuePrefix": basestring, + "valueSuffix": basestring, + "xDateFormat": basestring, + } + + +class xAxisOptions(BaseOptions): + ALLOWED_OPTIONS = { + "allowDecimals": bool, + "alternateGridColor": (ColorObject, basestring, dict), + "categories": list, + 'crosshair': bool, + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "endOnTick": bool, + "events": (Events, dict), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineWidth": int, + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": int, + "max": [float, int], + "maxPadding": [float, int], + "maxZoom": NotImplemented, + "min": [float, int], + "minPadding": [float, int], + "minRange": int, + "minTickInterval": int, + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "offset": bool, + "opposite": bool, + "plotBands": (PlotBands, list), + "plotLines": (PlotLines, list), + "reversed": bool, + "showEmpty": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "startOfWeek": int, + "startOnTick": bool, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": JSfunction, + "tickPositions": list, + "tickWidth": int, + "tickmarkPlacement": basestring, + "title": (Title, dict), + "type": basestring, + "units": list + } + + +class yAxisOptions(BaseOptions): + ALLOWED_OPTIONS = { + "allowDecimals": bool, + "alternateGridColor": (ColorObject, basestring, dict), + "breaks": (Breaks, dict), + "categories": list, + "ceiling": (int, float), + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "endOnTick": bool, + "events": (Events, dict), + "floor": (int, float), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineInterpolation": basestring, + "gridLineWidth": int, + "gridZIndex": int, + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": int, + "max": [float, int], + "maxColor": (ColorObject, basestring, dict), + "maxPadding": [float, int], + "maxZoom": NotImplemented, + "min": [float, int], + "minColor": (ColorObject, basestring, dict), + "minPadding": [float, int], + "minRange": int, + "minTickInterval": int, + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "offset": bool, + "opposite": bool, + "plotBands": (PlotBands, list), + "plotLines": (PlotLines, list), + "reversed": bool, + "reversedStacks": bool, + "showEmpty": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "stackLabels": (Labels, dict), + "startOfWeek": int, + "startOnTick": bool, + "stops": list, + "tickAmount": int, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": (JSfunction, basestring), + "tickPositions": list, + "tickWidth": int, + "tickmarkPlacement": basestring, + "title": (Title, dict), + "type": basestring, + "units": list + } + +class zAxisOptions(BaseOptions): #only for 3D plots + ALLOWED_OPTIONS = { + "allowDecimals": bool, + "alternateGridColor": (ColorObject, basestring, dict), + "breaks": (Breaks, dict), + "categories": list, + "ceiling": (int, float), + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "endOnTick": bool, + "events": (Events, dict), + "floor": (int, float), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineInterpolation": basestring, + "gridLineWidth": int, + "gridZIndex": int, + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": int, + "max": [float, int], + "maxColor": (ColorObject, basestring, dict), + "maxPadding": [float, int], + "maxZoom": NotImplemented, + "min": [float, int], + "minColor": (ColorObject, basestring, dict), + "minPadding": [float, int], + "minRange": int, + "minTickInterval": int, + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "offset": bool, + "opposite": bool, + "plotBands": (PlotBands, list), + "plotLines": (PlotLines, list), + "reversed": bool, + "reversedStacks": bool, + "showEmpty": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "stackLabels": (Labels, dict), + "startOfWeek": int, + "startOnTick": bool, + "stops": list, + "tickAmount": int, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": (JSfunction, basestring), + "tickPositions": list, + "tickWidth": int, + "tickmarkPlacement": basestring, + "title": (Title, dict), + "type": basestring, + "units": list + } + + +class MultiAxis(object): + + def __init__(self, axis): + AXIS_LIST = { + "xAxis": xAxisOptions, + "yAxis": yAxisOptions + } + self.axis = [] + self.AxisObj = AXIS_LIST[axis] + + def update(self, **kwargs): + self.axis.append(self.AxisObj(**kwargs)) + + def __jsonable__(self): + return self.axis + diff --git a/build/lib/highcharts/highcharts/templates/base.html b/build/lib/highcharts/highcharts/templates/base.html new file mode 100644 index 0000000..401d656 --- /dev/null +++ b/build/lib/highcharts/highcharts/templates/base.html @@ -0,0 +1,21 @@ +{% block container %} +{% endblock %} + +{% block start_script %} + +{% endblock endscript %} \ No newline at end of file diff --git a/build/lib/highcharts/highcharts/templates/content.html b/build/lib/highcharts/highcharts/templates/content.html new file mode 100644 index 0000000..b92181e --- /dev/null +++ b/build/lib/highcharts/highcharts/templates/content.html @@ -0,0 +1,88 @@ +{% extends "base.html" %} +{% block container %} + {{ chart.container }} +{% endblock container %} + +{% block body_head %} + + {% if chart.jscript_head_flag %} + {{chart.jscript_head}} + {% endif %} + +{% endblock body_head %} + +{% block body_content %} + + Highcharts.setOptions({{chart.setoption}}); + var option = {{chart.option}}; + + {% if chart.mapdata_flag %} + var geojson = {{chart.mapdata}} + {% endif %} + + {% if chart.drilldown_flag %} + var drilldowndata = {{chart.drilldown_data}}; + option.drilldown.series = drilldowndata; + {% endif %} + + var chart = new Highcharts.Chart(option); + + {# DEM 2017/07/27: Use a list of JSONP data sources + {# DEM 2017/07/27: This implementation is limited and could easily be improved! #} + {% if chart.jsonp_data_flag %} + {% for data_url in chart.jsonp_data_url_list %} + + $.getJSON({{data_url}}, function ({{chart.jsonp_data}}) + { + chart.addSeries({{chart.data_list[loop.index0]}}); + }); + + {% endfor %} + {% else %} + var data = {{chart.data}}; + var dataLen = data.length; + for (var ix = 0; ix < dataLen; ix++) { + chart.addSeries(data[ix]); + } + {% endif %} + + + {% if chart.jscript_end_flag %} + {{chart.jscript_end}} + {% endif %} + + {% if chart.options3d_rotation_flag %} + $(chart.container).bind('mousedown.hc touchstart.hc', function (e) { + e = chart.pointer.normalize(e); + + var posX = e.pageX, + posY = e.pageY, + alpha = chart.options.chart.options3d.alpha, + beta = chart.options.chart.options3d.beta, + newAlpha, + newBeta, + sensitivity = 5; + + $(document).bind({ + 'mousemove.hc touchdrag.hc': function (e) { + + newBeta = beta + (posX - e.pageX) / sensitivity; + chart.options.chart.options3d.beta = newBeta; + + newAlpha = alpha + (e.pageY - posY) / sensitivity; + chart.options.chart.options3d.alpha = newAlpha; + + chart.redraw(false); + }, + 'mouseup touchend': function () { + $(document).unbind('.hc'); + } + }); + }); + {% endif %} + +{% endblock body_content %} + +{% block body_end %} + +{% endblock body_end %} diff --git a/build/lib/highcharts/highcharts/templates/page.html b/build/lib/highcharts/highcharts/templates/page.html new file mode 100644 index 0000000..3559aaa --- /dev/null +++ b/build/lib/highcharts/highcharts/templates/page.html @@ -0,0 +1,12 @@ + + + + + {% for header_element in chart.header_css+chart.header_js %} + {{ header_element }} + {% endfor %} + + + {{ chart.content }} + + diff --git a/build/lib/highcharts/highcharts/templates/test.html b/build/lib/highcharts/highcharts/templates/test.html new file mode 100644 index 0000000..63eecc5 --- /dev/null +++ b/build/lib/highcharts/highcharts/templates/test.html @@ -0,0 +1,17 @@ + + + + + + {{ chart.title }} + + + + + +
+

Why, hello there!

+
+ + + \ No newline at end of file diff --git a/build/lib/highcharts/highmaps/__init__.py b/build/lib/highcharts/highmaps/__init__.py new file mode 100644 index 0000000..a15132e --- /dev/null +++ b/build/lib/highcharts/highmaps/__init__.py @@ -0,0 +1 @@ +from .highmaps import Highmap \ No newline at end of file diff --git a/build/lib/highcharts/highmaps/common.py b/build/lib/highcharts/highmaps/common.py new file mode 100644 index 0000000..b03309d --- /dev/null +++ b/build/lib/highcharts/highmaps/common.py @@ -0,0 +1,701 @@ +# -*- coding: UTF-8 -*- +""" Python-Highmaps common.py +Common Functions For Highmaps +""" +from past.builtins import basestring +import datetime, re + +FORMATTER_TYPE_MAPPINGS = { + "default": "function() { return this.value }", + "date": "function() { return''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + ': '+ this.y; }", + "pie": "function() { return ''+ this.point.name +': '+ \ + this.percentage +' %'; }", + "pound_yAxis": "function() { '£' + return this.value }", + "pound_tooltip": "function() { return''+ this.x + ': '+ '£' +this.y; }", + "percent": "function() { return this.value + ' %' }", + "default_tooltip": "function () { return''+ this.series.name + ': ' + this.y; }", + "percent_tooltip": "function () { return''+ this.series.name + ': ' + this.y + ' %'; }", + "date_percent_tooltip": "function () { return''+Highcharts.dateFormat('%e. %b %Y',this.x) + '
'+ this.series.name + ': ' + this.y + ' %'; }", + 'filesize': """ +function() { + fileSizeInBytes = this.value; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'date_filesize_tooltip': """ +function() { + fileSizeInBytes = this.y; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return ''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + '
' + this.series.name + ': ' + Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'filesize_tooltip': """ +function() { + fileSizeInBytes = this.y; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return '' + this.series.name + ': ' + Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'duration': """ +function() { + seconds = this.value; + + days = Math.floor(seconds / 86400); + seconds = seconds - (days * 86400); + + hours = Math.floor(seconds / 3600); + seconds = seconds - (hours * 3600); + + mins = Math.floor(seconds / 60); + seconds = seconds - (mins * 60); + + res = ""; + if(days > 0){ + res += days + " d "; + } + if(hours > 0){ + res += hours + ' hr '; + } + if(mins > 0){ + res += mins + ' m '; + } + if(seconds > 0){ + res += seconds + ' s '; + } + return res; +} +""", + 'date_duration_tooltip': """ +function() { + seconds = this.y; + + days = Math.floor(seconds / 86400); + seconds = seconds - (days * 86400); + + hours = Math.floor(seconds / 3600); + seconds = seconds - (hours * 3600); + + mins = Math.floor(seconds / 60); + seconds = seconds - (mins * 60); + + res = ""; + if(days > 0){ + res += days + " d "; + } + if(hours > 0){ + res += hours + ' hr '; + } + if(mins > 0){ + res += mins + ' m '; + } + if(seconds > 0){ + res += seconds + ' s '; + } + return ''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + '
'+ this.series.name + ': ' + res; +} +""", +} + +REGEX_LIST = { + "re_funct" : re.compile(r'.*function\(.*\)\{.*\}', re.I), #for inputs such as function(xxx){xxx} + "re_hcharts" : re.compile(r'.*Highcharts.*', re.I), #for inputs such as highcharts.xxxxx +} + + +class Formatter(object): + """ Base Formatter Class """ + + def __init__(self, format=None): + ### Choose either from default functions in FORMATTER_TYPE_MAPPINGS using format_type + ### or wriet a function in format_string + if format: + if format in FORMATTER_TYPE_MAPPINGS: + self.formatter = RawJavaScriptText(FORMATTER_TYPE_MAPPINGS[format]) + elif isinstance(format, basestring): + self.formatter = RawJavaScriptText(format) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring) + + def __options__(self): + return self.formatter + + def __jsonable__(self): + return self.formatter + +class MapObject(object): + """ Base Map Class """ + + def __init__(self, mapdata=None): + if mapdata: + if isinstance(mapdata, basestring): + self.map = RawJavaScriptText(mapdata) + elif isinstance(mapdata, JSfunction): + self.map = mapdata + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring) + + def __options__(self): + return self.map + + def __jsonable__(self): + return self.map + + +class ColorObject(object): + """ color object """ + + def __init__(self, color=None, **kwargs): + if not color: + color = kwargs + + if color: + if isinstance(color, dict): + tmp = [] + for i, item in enumerate(color['stops']): + tmp.append([RawJavaScriptText(x) if isinstance(x, basestring) and any([REGEX_LIST[key].search(x) for key in REGEX_LIST.keys()]) + else x for x in item ]) + color['stops'] = tmp + self.color = color + elif any([REGEX_LIST[key].search(color) for key in REGEX_LIST.keys()]): + self.color = RawJavaScriptText(color) + elif isinstance(color, basestring): + self.color = color + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % (basestring or dict)) + else: + self.color = None + + def __options__(self): + return self.color + + def __jsonable__(self): + return self.color + + +class CSSObject(object): + """ CSS style class """ + + def __init__(self, **kwargs): + self.css = kwargs + + for k, v in self.css.items(): + if isinstance(v, basestring) and any([REGEX_LIST[key].search(v) for key in REGEX_LIST.keys()]): + v = RawJavaScriptText(v) + self.css.update({k:v}) + + def __options__(self): + return self.css + + def __jsonable__(self): + return self.css + +class SVGObject(object): + """ SVG style class """ + + def __init__(self, **kwargs): + self.svg = kwargs + + for k, v in self.svg.items(): + if isinstance(v, basestring) and any([REGEX_LIST[key].search(v) for key in REGEX_LIST.keys()]): + v = RawJavaScriptText(v) + self.svg.update({k:v}) + + def __options__(self): + return self.svg + + def __jsonable__(self): + return self.svg + +class JSfunction(object): + + def __init__(self, function = None): + if function: + if isinstance(function, basestring): + self.function = RawJavaScriptText(function) + elif isinstance(function, JSfunction): + self.function = function + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring) + + def __options__(self): + return self.function + + def __jsonable__(self): + return self.function + + +class RawJavaScriptText: + + def __init__(self, jstext): + self._jstext = jstext + def get_jstext(self): + return self._jstext + + +class CommonObject(object): + + def __init__(self, **kwargs): + self.process_kwargs(kwargs) + + def __validate_options__(self, k,v,ov): + + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.__dict__ + + def __jsonable__(self): + return self.__dict__ + + def update(self, kwargs): + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), CommonObject): + # re-construct input dict with existing options in objects + if self.__getattr__(k): + if isinstance(v, dict): + self.__options__()[k].update(v) + else: + self.__options__()[k].__options__().update(v) + else: + self.__options__().update({k:allowed_args[k][0](**v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), ArrayObject): + # update array + if isinstance(v, dict): + self.__dict__[k].append(self.ALLOWED_OPTIONS[k][0](**v)) + elif isinstance(v, list): + for item in v: + self.__dict__[k].append(self.ALLOWED_OPTIONS[k][0](**item)) + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + (isinstance(self.ALLOWED_OPTIONS[k][0](), CSSObject) or isinstance(self.ALLOWED_OPTIONS[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): + self.__dict__[k].__options__().update({key:value}) + else: + self.__dict__.update({k:allowed_args[k][0](**v)}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and (isinstance(self.ALLOWED_OPTIONS[k][0](), JSfunction) or \ + isinstance(self.ALLOWED_OPTIONS[k][0](), Formatter) or isinstance(self.ALLOWED_OPTIONS[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + + else: + raise OptionTypeError("Not An Accepted Option Type: %s" % k) + + def process_kwargs(self,kwargs): + + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if self.__validate_options__(k,v,self.ALLOWED_OPTIONS[k]): + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + self.ALLOWED_OPTIONS[k][0] in IDV_OBJECT_LIST: + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % self.ALLOWED_OPTIONS[k]) + else: + raise OptionTypeError("Option: %s Not Allowed For CommonObject:" % k) + + +class Events(CommonObject): + """ Class for event listener """ + + ALLOWED_OPTIONS = { + "addSeries": (JSfunction, basestring), + "afterPrint": (JSfunction, basestring), + "beforePrint": (JSfunction, basestring), + "click": (JSfunction, basestring), + "drilldown": (JSfunction, basestring), + "drillup": (JSfunction, basestring), + "load": (JSfunction, basestring), + "redraw": (JSfunction, basestring), + "selection": (JSfunction, basestring), + "afterAnimate": (JSfunction, basestring), + "checkboxClick": (JSfunction, basestring), + "hide": (JSfunction, basestring), + "legendItemClick": (JSfunction, basestring), + "mouseOut": (JSfunction, basestring), + "mouseOver": (JSfunction, basestring), + "show": (JSfunction, basestring), + "remove": (JSfunction, basestring), + "select": (JSfunction, basestring), + "unselect": (JSfunction, basestring), + "update": (JSfunction, basestring), + "afterBreaks": (JSfunction, basestring), + "afterSetExtremes": (JSfunction, basestring), + "pointBreak": (JSfunction, basestring), + "setExtremes": (JSfunction, basestring) + } + +class Point(CommonObject): + ALLOWED_OPTIONS = { + "events": Events + } + +class Position(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "verticalAlign": basestring, + "x": int, + "y": int, + } + +class ContextButton(CommonObject): + """ Option class for the export button """ + ALLOWED_OPTIONS = { + "align": basestring, + "alignTo": basestring, + "enabled": bool, + "height": int, + "menuItems": NotImplemented, + "onclick": (JSfunction, basestring), + "style": (CSSObject, dict), + "symbol": basestring, + "symbolFill": basestring, + "symbolSize": int, + "symbolStroke": basestring, + "symbolStrokeWidth": int, + "symbolX": [float, int], + "symbolY": [float, int], + "text": basestring, + "theme": NotImplemented,#ThemeObject + "verticalAlign": basestring, + "width": int, + "x": int, + "y": int, + } + +class Button(CommonObject): + ALLOWED_OPTIONS = { + "zoomIn": (ContextButton, dict), + "zoomOut": (ContextButton, dict), + } + +class Options3d(CommonObject): + ALLOWED_OPTIONS = { + "alpha": [float, int], + "beta": [float, int], + "depth": int, + "enabled": bool, + "frame": NotImplemented, # FrameObject + "viewDistance": int + } + + +class ResetZoomButton(CommonObject): + ALLOWED_OPTIONS = { + "position": (Position, dict), + "relativeTo": basestring, + "theme": NotImplemented #ThemeObject + } + +class DrillUpButton(CommonObject): + ALLOWED_OPTIONS = { + "position": (Position, dict), + "relativeTo": basestring, + "theme": NotImplemented #ThemeObject + } + +class Labels(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": [float, int], + "borderWidth": [int, float, basestring], + "color": (ColorObject, basestring, dict), + "connectorColor": (ColorObject, basestring, dict), + "connectorPadding": [float, int], + "connectorWidth": [float, int], + "crop": bool, + "defer": bool, + "distance": int, + "enabled": bool, + "format": [basestring, type(None)], + "formatter": (Formatter, JSfunction, basestring), + "inside": bool, + "overflow": basestring, + "padding": [float, int], + "rotation": int, + "shadow": [bool, dict], #shadow object + "shape": basestring, + "softConnector": bool, + "staggerLines": int, + "step": int, + "style": (CSSObject, dict), + "text": basestring, + "textAlign": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + "zIndex": int, + } + +class Title(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "enabled": bool, + "margin": int, + "offset": int, + "rotation": int, + "style": (CSSObject, dict), + "text": [basestring, type(None)] + } + +class Navigation(CommonObject): + ALLOWED_OPTIONS = { + "activeColor": (ColorObject, basestring, dict), + "animation": NotImplemented, #need animation object + "arrowSize": int, + "inactiveColor": (ColorObject, basestring, dict), + "style": (CSSObject, dict), + } + +class DateTimeLabelFormats(CommonObject): + ALLOWED_OPTIONS = { + "millisecond": basestring, + "second": basestring, + "minute": basestring, + "hour": basestring, + "day": basestring, + "week": basestring, + "month": basestring, + "year": basestring, + } + +class Select(CommonObject): + ALLOWED_OPTIONS = { + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "radius": int, + } + +class States(CommonObject): + ALLOWED_OPTIONS = { + "hover": dict, + "select": dict, + } + +class Marker(CommonObject): + ALLOWED_OPTIONS = { + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "height": int, + "lineWidth": int, + "lineColor": (ColorObject, basestring, dict), + "radius": int, + "states": (States, dict), + "symbol": basestring, + "width": int + } + +class Halo(CommonObject): + ALLOWED_OPTIONS = { + "attributes": (SVGObject, dict), + "opacity": float, + "size": int + } + +class Hover(CommonObject): + ALLOWED_OPTIONS = { + "borderColor": (ColorObject, basestring, dict), + "borderWidth": [int, float, basestring], + "brightness": [int, float], + "color": (ColorObject, basestring, dict), + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "halo": (Halo, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "lineWidthPlus": int, + "marker": (Marker, dict), + "radius": int, + "radiusPlus": int, + } + +class States(CommonObject): + ALLOWED_OPTIONS = { + "hover": (Hover, dict), + "normal": dict, #need animation object + "select": (Select, dict) + } + +class Tooltip(CommonObject): + ALLOWED_OPTIONS = { + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "followPointer": bool, + "followTouchMove": bool, + "footerFormat": basestring, + "headerFormat": basestring, + "hideDelay": int, + "pointFormat": basestring, + "pointFormatter": (Formatter, JSfunction, basestring), + "shape": basestring, + "valueDecimals": int, + "valuePrefix": basestring, + "valueSuffix": basestring, + "xDateFormat": basestring + } + + +class ArrayObject(object): + + def __init__(self, **kwargs): + self.data = [] + self.process_kwargs(kwargs) + + def __validate_options__(self, k,v,ov): + + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.data + + def __jsonable__(self): + return self.data + + def update(self, kwargs): + self.process_kwargs(kwargs) + + def process_kwargs(self,kwargs): + temp = {} + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if self.__validate_options__(k,v,self.ALLOWED_OPTIONS[k]): + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + self.ALLOWED_OPTIONS[k][0] in IDV_OBJECT_LIST: + if isinstance(v, dict): + temp.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + temp.update({k:v}) + else: + temp.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + temp.update({k:v}) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % self.ALLOWED_OPTIONS[k]) + else: + raise OptionTypeError("Option: %s Not Allowed For ArrayObject:" % k) + self.data.append(temp) + +class Items(ArrayObject): + ALLOWED_OPTIONS = { + "html": basestring, + "style": (CSSObject, dict) + } + +class Background(ArrayObject): + ALLOWED_OPTIONS = { + "backgroundColor": (ColorObject, basestring, dict), + "shape": basestring, + "innerWidth": int, + "outerWidth": int, + "borderWidth": [int, float, basestring], + "borderColor": (ColorObject, basestring, dict), + "outerRadius": basestring, + } + +class Breaks(ArrayObject): + ALLOWED_OPTIONS = { + "breakSize": int, + "from": [int, float], + "repeat": int, + "to": [int, float], + } + +class DataClasses(ArrayObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "from": [int, float], + "name": basestring, + "to": [int, float], + } + +class Levels(ArrayObject): + ALLOWED_OPTIONS = { + "borderColor": (ColorObject, basestring, dict), + "borderDashStyle": basestring, + "borderWidth": [int, basestring], + "color": (ColorObject, basestring, dict), + "dataLabels": (Labels, dict), + "layoutAlgorithm": basestring, + "layoutStartingDirection": basestring, + "level": int, + } + + +IDV_OBJECT_LIST = [JSfunction, Formatter, Halo, Marker, Labels, Button, + Position, Hover, Select, Events, States, ContextButton, + CSSObject, SVGObject, ColorObject, + RawJavaScriptText, DateTimeLabelFormats] + + +class OptionTypeError(Exception): + + def __init__(self,*args): + self.args = args diff --git a/build/lib/highcharts/highmaps/highmap_helper.py b/build/lib/highcharts/highmaps/highmap_helper.py new file mode 100644 index 0000000..834662b --- /dev/null +++ b/build/lib/highcharts/highmaps/highmap_helper.py @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- +from future.standard_library import install_aliases +install_aliases() +from past.builtins import basestring + +from urllib.request import urlopen +import urllib + +import json, os, sys +import datetime, re +from datetime import tzinfo + +def jsonp_loader(url, prefix_regex=r'^(.*\()', suffix_regex=r'(\);)$', sub_d=None, sub_by=''): + """Request (JSON) data from a server in a different domain (JSONP) + and covert to python readable data. + 1. url is the url (https) where data is located + 2. "prefix_regex" and "suffix_regex" are regex patterns used to + remove JSONP specific prefix and suffix, such as callback header: "callback(" and end: ");", + 3. "sub_d" is regex patterns for any unwanted string in loaded json data (will be replaced by sub_by). + 4. "sub_by" is the string to replace any unwanted string defined by sub_d + For function coverstion, such as Data.UTC to datetime.datetime, please check JSONPDecoder + """ + + hdr = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7'} + req = urllib.request.Request(url, headers=hdr) + page = urlopen(req) + result = page.read().decode('utf-8') + # replace all the redundant info with sub_by + if sub_d: + result = re.sub(sub_d, sub_by, result) + + prefix = re.search(prefix_regex, result).group() + suffix = re.search(suffix_regex, result).group() + if result.startswith(prefix) and result.endswith(suffix): + result = result[len(prefix):-len(suffix)] + return json.loads(result, encoding='utf8', cls=JSONPDecoder) + +def js_map_loader(url): + """Load map data from a .js source. It is designed for using highcharts' map collection: + https://code.highcharts.com/mapdata/. Map data from other sources are not guaranteed + """ + + hdr = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7'} + req = urllib.request.Request(url, headers=hdr) + page = urlopen(req) + result = page.read().decode('utf-8') + result = result[len(re.search(r'^.* = ', result).group()):] + + return json.loads(result) + +def geojson_handler(geojson, hType='map'): + """Restructure a GeoJSON object in preparation to be added directly by add_map_data or add_data_set methods. + The geojson will be broken down to fit a specific Highcharts (highmaps) type, either map, mapline or mappoint. + Meta data in GeoJSON's properties object will be copied directly over to object['properties'] + 1. geojson is the map data (GeoJSON) to be converted + 2. hType is the type of highmap types. "map" will return GeoJSON polygons and multipolygons. + "mapline" will return GeoJSON linestrings and multilinestrings. + "mappoint" will return GeoJSON points and multipoints. + default: "map" + """ + + hType_dict = { + 'map': ['polygon', 'multipolygon'], + 'mapline': ['linestring', 'multilinestring'], + 'mappoint': ['point', 'multipoint'], + } + + + oldlist = [x for x in geojson['features'] if x['geometry']['type'].lower() in hType_dict[hType]] + newlist = [] + for each_dict in oldlist: + geojson_type = each_dict['geometry']['type'].lower() + + if hType == 'mapline': + newlist.append( + {'name': each_dict['properties'].get('name', None), + 'path': _coordinates_to_path(each_dict['geometry']['coordinates'], hType, geojson_type), + 'properties': each_dict['properties'], + } + ) + elif hType == 'map': + newlist.append( + {'name': each_dict['properties']['name'], + 'path': _coordinates_to_path(each_dict['geometry']['coordinates'], hType, geojson_type), + 'properties': each_dict['properties'], + } + ) + elif hType == 'mappoint': + newlist.append( + {'name': each_dict['properties']['name'], + 'x': each_dict['geometry']['coordinates'][0], + 'y': -each_dict['geometry']['coordinates'][1], + 'properties': each_dict['properties'], + } + ) + + return newlist + +def interpolateRGB(lowRGB, highRGB, fraction): + color = [] + + for i in range(3): + color.append((highRGB[i] - lowRGB[i]) * fraction + lowRGB[i]) + + return 'rgb(' + str(int(round(color[0],0))) + ',' + str(int(round(color[1],0))) + ',' + \ + str(int(round(color[2],0))) + ')' + +def _coordinates_to_path(coordinates_array, hType, geojson_type): + new_array = [] + + def _svglabel(alist): + newlist = [] + for i, item in enumerate(alist): + if i == 0: + item = ['M']+[item[0], -item[1]] + elif i == 1: + item = ['L']+[item[0], -item[1]] + else: + item = [item[0], -item[1]] + newlist += item + return newlist + + if geojson_type == 'multipolygon': + coordinates_array = [item for sublist in coordinates_array for item in sublist] + + if geojson_type in ['polygon', 'multipolygon', 'multilinestring']: + for item in coordinates_array: + new_array += _svglabel(item) + else: + new_array += _svglabel(coordinates_array) + + if hType == 'map': + new_array+=['z'] + + return new_array + + +def _path_to_array(path): + path = path.replace(r'/([A-Za-z])/g', r' $1 ') + path = path.replace(r'/^\s*/', "").replace(r'/\s*$/', "") + path = path.split(" "); + for i, v in enumerate(path): + try: + path[i] = float(v) + except: + pass + return path + +if __name__ == '__main__': + print(path_to_array("M 4687 2398 L 4679 2402 4679 2398 Z")) + +class JSONPDecoder(json.JSONDecoder): + """Customized JSON decoder. It is used to convert everything + that is python non-compatible (usually Javascript functions) + to one that can be read by python. It needs to coordinate with + customized JSON encoder in main library, such as highcharts.py, + to convert back to Javascript-compatiable functions. + For example: in _iterdecode, it checks if queried JSON has Data.UTC + and (if yes)converts it to datetime.datetime + """ + + def decode(self, json_string): + """ + json_string is basicly string that you give to json.loads method + """ + + default_obj = super(JSONPDecoder, self).decode(json_string) + + return list(self._iterdecode(default_obj))[0] + + def _iterdecode_list(self, lst): + new_lst = [] + for item in lst: + for chunk in self._iterdecode(item): + new_lst.append(chunk) + yield new_lst + + def _iterdecode_dict(self, dct): + new_dct = {} + for key, value in dct.items(): + for chunk in self._iterdecode(value): + new_dct[key] = chunk + yield new_dct + + def _iterdecode(self, obj): + if isinstance(obj, (list, tuple)): + for chunk in self._iterdecode_list(obj): + yield chunk + + elif isinstance(obj, dict): + for chunk in self._iterdecode_dict(obj): + yield chunk + + elif isinstance(obj, basestring) and JSONPDecoder.is_js_date_utc(obj): + m = JSONPDecoder.is_js_date_utc(obj) + yield JSONPDecoder.json2datetime(m) + + else: + yield obj + + @staticmethod + def is_js_date_utc(json): + """Check if the string contains Date.UTC function + and return match group(s) if there is + """ + + JS_date_utc_pattern = r'Date\.UTC\(([0-9]+,[0-9]+,[0-9]+)(,[0-9]+,[0-9]+,[0-9]+)?(,[0-9]+)?\)' + re_date = re.compile(JS_date_utc_pattern, re.M) + + if re_date.search(json): + return re_date.search(json).group(0) + else: + return False + + @staticmethod + def json2datetime(json): + """Convert JSON representation to date or datetime object depending on + the argument count. Requires UTC datetime representation. + Raises ValueError if the string cannot be parsed. + """ + + json_m = re.search(r'([0-9]+,[0-9]+,[0-9]+)(,[0-9]+,[0-9]+,[0-9]+)?(,[0-9]+)?', json) + args=json_m.group(0).split(',') + + try: + args=map(int, args) + except ValueError: + raise ValueError('Invalid arguments: %s'%json) + + if len(args)==3: + return datetime.datetime(args[0], args[1]+1, args[2]) + elif len(args)==6: + return datetime.datetime(args[0], args[1]+1, args[2], + args[3], args[4], args[5], tzinfo=UTC()) + elif len(args)==7: + args[6]*=1000 + return datetime.datetime(args[0], args[1]+1, args[2], + args[3], args[4], args[5], args[6], tzinfo=UTC()) + raise ValueError('Invalid number of arguments: %s'%json) + + +class UTC(tzinfo): + """UTC""" + + ZERO=datetime.timedelta(0) + + def utcoffset(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return ZERO + \ No newline at end of file diff --git a/build/lib/highcharts/highmaps/highmap_types.py b/build/lib/highcharts/highmaps/highmap_types.py new file mode 100644 index 0000000..40cbbc9 --- /dev/null +++ b/build/lib/highcharts/highmaps/highmap_types.py @@ -0,0 +1,301 @@ +# -*- coding: UTF-8 -*- +from past.builtins import basestring +import json, datetime +from .common import Formatter, Events, Position, ContextButton, Options3d, ResetZoomButton, DrillUpButton, Labels, \ + Marker, Point, States, Tooltip, Title, JSfunction, MapObject, ColorObject, CSSObject, SVGObject, \ + CommonObject, ArrayObject + +PLOT_OPTION_ALLOWED_ARGS = { + "common": { + "animation": bool, + "color": (ColorObject, basestring, dict), + "cursor": basestring, + "dataLabels": (Labels, dict), + "enableMouseTracking": bool, + "events": (Events, dict), + "id": basestring, + "index": [float, int], + "marker": (Marker, dict), + "name": basestring, + "point": (Point, dict), + "selected": bool, + "showCheckbox": bool, + "showInLegend": bool, + "states": (States, dict), + "stickyTracking": bool, + "tooltip": (Tooltip, dict), + "visible": bool, + "xAxis": [int, basestring], + "yAxis": [int, basestring], + "zIndex": int, + }, "heatmap": { + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderWidth": [int, float, basestring], + "colsize": int, + "legendIndex": [int, float], + "rowsize": int, + "mapData": (MapObject, list, basestring), + "nullColor": (ColorObject, basestring, dict), + "shadow": [bool, dict], #shadow object + }, + "map": { + "allAreas": bool, + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderWidth": [int, float, basestring], + "dashStyle": basestring, + "joinBy": [basestring, list], + "legendIndex": [int, float], + "mapData": (MapObject, list, basestring), + "nullColor": (ColorObject, basestring, dict), + "shadow": [bool, dict], + }, + "mapbubble": { + "allAreas": bool, + "allowPointSelect": bool, + "borderColor": (ColorObject, basestring, dict), + "borderWidth": [int, float, basestring], + "displayNegative": bool, + "joinBy": [basestring, list], + "legendIndex": [int, float], + "mapData": (MapObject, list, basestring), + "maxSize": [basestring, int], + "minSize": [basestring, int], + "negativeColor": (ColorObject, basestring, dict), + "shadow": [bool, dict], + "sizeBy": basestring, + "zMax": int, + "zMin": int, + "zThreshold": [int, float], + }, + "mapline": { + "allAreas": bool, + "allowPointSelect": bool, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "joinBy": [basestring, list], + "legendIndex": [int, float], + "lineWidth": [int, float], + "mapData": (MapObject, list, basestring), + "maxSize": [basestring, int], + "minSize": [basestring, int], + "negativeColor": (ColorObject, basestring, dict), + "shadow": [bool, dict], + "sizeBy": basestring, + "zMax": int, + "zMin": int, + "zThreshold": [int, float], + }, + "mappoint": { + "legendIndex": [int, float], + "mapData": (MapObject, list, basestring), + }, +} + +DATA_SERIES_ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "dataLabels": (Labels, dict), + "dataParser": NotImplemented, + "dataURL": NotImplemented, + "drilldown": basestring, + "events": (Events, dict), + "high": [int, float], + "id": basestring, + "index": int, + "legendIndex": int, + "lat": [float, int], + "lon": [float, int], + "labelrank": [int, float], + "middleX": [int, float], + "middleY": [int, float], + "name": basestring, + "path": basestring, + "value": [int, float, list], + "x": [int, float], + "y": [int, float], + "z": [float, int], + "xAxis": int, + "yAxis": int, +} + +DEFAULT_OPTIONS = { + +} + +class OptionTypeError(Exception): + + def __init__(self, *args): + self.args = args + + +class SeriesOptions(object): + """Class for plotOptions""" + + def __init__(self, series_type="map", supress_errors=False, **kwargs): + self.load_defaults(series_type) + self.process_kwargs(kwargs, series_type=series_type, supress_errors=supress_errors) + + @staticmethod + def __validate_options__(k, v, ov): + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.__dict__ + + def __jsonable__(self): + return self.__dict__ + + def __display_options__(self): + print(json.dumps(self.__options__(),indent=4,sort_keys=True)) + + def update(self,series_type, **kwargs): + allowed_args = PLOT_OPTION_ALLOWED_ARGS[series_type] + allowed_args.update(PLOT_OPTION_ALLOWED_ARGS["common"]) + + for k, v in kwargs.items(): + if k in allowed_args: + if SeriesOptions.__validate_options__(k,v,allowed_args[k]): + if isinstance(allowed_args[k], tuple) and isinstance(allowed_args[k][0](), CommonObject): + # re-construct input dict with existing options in objects + if self.__getattr__(k): + if isinstance(v, dict): + self.__options__()[k].update(v) + else: + self.__options__()[k].__options__().update(v) + else: + self.__options__().update({k:allowed_args[k][0](**v)}) + + elif isinstance(allowed_args[k], tuple) and isinstance(allowed_args[k][0](), ArrayObject): + # update array + if isinstance(v, dict): + self.__dict__[k].append(allowed_args[k][0](**v)) + elif isinstance(v, list): + for item in v: + self.__dict__[k].append(allowed_args[k][0](**item)) + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(v)) + + elif isinstance(allowed_args[k], tuple) and \ + (isinstance(allowed_args[k][0](), CSSObject) or isinstance(allowed_args[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): + self.__dict__[k].__options__().update({key:value}) + else: + self.__dict__.update({k:allowed_args[k][0](**v)}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + + elif isinstance(allowed_args[k], tuple) and (isinstance(allowed_args[k][0](), JSfunction) or \ + isinstance(allowed_args[k][0](), Formatter) or isinstance(allowed_args[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + print(k,v) + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % allowed_args[k]) + + + def process_kwargs(self, kwargs, series_type, supress_errors=False): + allowed_args = PLOT_OPTION_ALLOWED_ARGS[series_type] + allowed_args.update(PLOT_OPTION_ALLOWED_ARGS["common"]) + + for k, v in kwargs.items(): + if k in allowed_args: + if SeriesOptions.__validate_options__(k,v,allowed_args[k]): + if isinstance(allowed_args[k], tuple): + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + elif isinstance(v, list): + if len(v) == 1: + self.__dict__.update({k:allowed_args[k][0](**v[0])}) + else: + self.__dict__.update({k:allowed_args[k][0](**v[0])}) + for item in v[1:]: + self.__dict__[k].update(item) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + print(k,v) + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % allowed_args[k]) + + def load_defaults(self, series_type): + self.process_kwargs(DEFAULT_OPTIONS.get(series_type,{}),series_type) + + def __getattr__(self, item): + if not item in self.__dict__: + return None # Attribute Not Set + else: + return True + + +class Series(object): + """Series class for input data """ + + def __init__(self, data, series_type="line", supress_errors=False, **kwargs): + + # List of dictionaries. Each dict contains data and properties, + # which need to handle before construct the object for series + if isinstance(data, list): + for item in data: + if isinstance(item, dict): + for k, v in item.items(): + if k in DATA_SERIES_ALLOWED_OPTIONS: + if SeriesOptions.__validate_options__(k,v,DATA_SERIES_ALLOWED_OPTIONS[k]): + if isinstance(DATA_SERIES_ALLOWED_OPTIONS[k], tuple): + if isinstance(v, dict): + item.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, datetime.datetime): + item.update({k:v}) + else: + item.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](v)}) + else: + item.update({k:v}) + + self.__dict__.update({ + "data": data, + "type": series_type, + }) + + # Series propertie can be input as kwargs, which is handled here + for k, v in kwargs.items(): + if k in DATA_SERIES_ALLOWED_OPTIONS: + if SeriesOptions.__validate_options__(k,v,DATA_SERIES_ALLOWED_OPTIONS[k]): + if isinstance(DATA_SERIES_ALLOWED_OPTIONS[k], tuple): + if isinstance(v, dict): + self.__dict__.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % DATA_SERIES_ALLOWED_OPTIONS[k]) + + def __jsonable__(self): + return self.__dict__ + + def __options__(self): + return self.__dict__ diff --git a/build/lib/highcharts/highmaps/highmaps.py b/build/lib/highcharts/highmaps/highmaps.py new file mode 100644 index 0000000..f2f7b91 --- /dev/null +++ b/build/lib/highcharts/highmaps/highmaps.py @@ -0,0 +1,499 @@ +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals, absolute_import +from future.standard_library import install_aliases +install_aliases() + +from past.builtins import basestring + +from urllib.request import urlopen +from jinja2 import Environment, PackageLoader + +import json, uuid +import re +import datetime +import html +try: + # works with older (3.9 and below) versions, throws an exception with newer (3.10 and above) versions + from collections import Iterable +#if an import error change to trying 3.10 and above notation +except ImportError: + from collections.abc import Iterable +from .options import BaseOptions, ChartOptions, \ + ColorsOptions, ColorAxisOptions, CreditsOptions, DrilldownOptions, ExportingOptions, \ + GlobalOptions, LabelsOptions, LangOptions, \ + LegendOptions, LoadingOptions, MapNavigationOptions, NavigationOptions, PaneOptions, \ + PlotOptions, SeriesData, SubtitleOptions, TitleOptions, \ + TooltipOptions, xAxisOptions, yAxisOptions + +from .highmap_types import Series, SeriesOptions +from .common import Formatter, CSSObject, SVGObject, MapObject, JSfunction, RawJavaScriptText, \ + CommonObject, ArrayObject, ColorObject + + + +CONTENT_FILENAME = "./content.html" +PAGE_FILENAME = "./page.html" + +pl = PackageLoader('highcharts.highmaps', 'templates') +jinja2_env = Environment(lstrip_blocks=True, trim_blocks=True, loader=pl) + +template_content = jinja2_env.get_template(CONTENT_FILENAME) +template_page = jinja2_env.get_template(PAGE_FILENAME) + +class Highmap(object): + """ + Highcharts Base class. + """ + #: chart count + count = 0 + + # this attribute is overriden by children of this + # class + CHART_FILENAME = None + template_environment = Environment(lstrip_blocks=True, trim_blocks=True, + loader=pl) + + def __init__(self, **kwargs): + """ + This is the base class for all the charts. The following keywords are + accepted: + :keyword: **display_container** - default: ``True`` + """ + # Set the model + self.model = self.__class__.__name__ #: The chart model, + self.div_name = kwargs.get("renderTo", "container") + + # An Instance of Jinja2 template + self.template_page_highcharts = template_page + self.template_content_highcharts = template_content + + # Set Javascript src + self.JSsource = [ + 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js', + 'https://code.highcharts.com/maps/6/highmaps.js', + 'https://code.highcharts.com/6/highcharts.js', + 'https://code.highcharts.com/maps/6/modules/map.js', + 'https://code.highcharts.com/maps/6/modules/data.js', + 'https://code.highcharts.com/maps/6/modules/exporting.js' + ] + + # set CSS src + self.CSSsource = [ + 'https://www.highcharts.com/highslide/highslide.css', + ] + # Set data + self.data = [] + self.data_temp = [] + self.data_is_coordinate = False + # Data from jsonp + self.jsonp_data_flag = False + + # Set drilldown data + self.drilldown_data = [] + self.drilldown_data_temp = [] + + # Map + self.mapdata_flag = False + self.map = None + + # Jsonp map + self.jsonp_map_flag = kwargs.get('jsonp_map_flag', False) + + # Javascript + self.jscript_head_flag = False + self.jscript_head = kwargs.get('jscript_head', None) + self.jscript_end_flag = False + self.jscript_end = kwargs.get('jscript_end', None) + + # Accepted keywords + self.div_style = kwargs.get('style', '') + self.drilldown_flag = kwargs.get('drilldown_flag', False) + + # None keywords attribute that should be modified by methods + # We should change all these to _attr + + self._htmlcontent = '' #: written by buildhtml + self.htmlheader = '' + # Place holder for the graph (the HTML div) + # Written by ``buildcontainer`` + self.container = u'' + # Header for javascript code + self.containerheader = u'' + # Loading message + self.loading = 'Loading....' + + + # Bind Base Classes to self + self.options = { + "chart": ChartOptions(), + #"colorAxis": # cannot input until there is data, do it later + "colors": ColorsOptions(), + "credits": CreditsOptions(), + #"data": #NotImplemented + "drilldown": DrilldownOptions(), + "exporting": ExportingOptions(), + "labels": LabelsOptions(), + "legend": LegendOptions(), + "loading": LoadingOptions(), + "mapNavigation": MapNavigationOptions(), + "navigation": NavigationOptions(), + "plotOptions": PlotOptions(), + "series": SeriesData(), + "subtitle": SubtitleOptions(), + "title": TitleOptions(), + "tooltip": TooltipOptions(), + "xAxis": xAxisOptions(), + "yAxis": yAxisOptions(), + } + + self.setOptions = { + "global": GlobalOptions(), + "lang": LangOptions(), + } + + self.__load_defaults__() + + # Process kwargs + allowed_kwargs = [ + "width", + "height", + "renderTo", + "backgroundColor", + "events", + "marginBottom", + "marginTop", + "marginRight", + "marginLeft" + ] + + for keyword in allowed_kwargs: + if keyword in kwargs: + self.options['chart'].update_dict(**{keyword:kwargs[keyword]}) + # Some Extra Vals to store: + self.data_set_count = 0 + self.drilldown_data_set_count = 0 + + + def __load_defaults__(self): + self.options["chart"].update_dict(renderTo='container') + self.options["title"].update_dict(text='A New Highchart') + self.options["credits"].update_dict(enabled=False) + + + def add_JSsource(self, new_src): + """add additional js script source(s)""" + if isinstance(new_src, list): + for h in new_src: + self.JSsource.append(h) + elif isinstance(new_src, basestring): + self.JSsource.append(new_src) + else: + raise OptionTypeError("Option: %s Not Allowed For Series Type: %s" % type(new_src)) + + + def add_CSSsource(self, new_src): + """add additional css source(s)""" + if isinstance(new_src, list): + for h in new_src: + self.CSSsource.append(h) + elif isinstance(new_src, basestring): + self.CSSsource.append(new_src) + else: + raise OptionTypeError("Option: %s Not Allowed For Series Type: %s" % type(new_src)) + + + def add_data_set(self, data, series_type="map", name=None, is_coordinate = False, **kwargs): + """set data for series option in highmaps """ + + self.data_set_count += 1 + if not name: + name = "Series %d" % self.data_set_count + kwargs.update({'name':name}) + + if is_coordinate: + self.data_is_coordinate = True + self.add_JSsource('https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.6/proj4.js') + if self.map and not self.data_temp: + series_data = Series([], series_type='map', **{'mapData': self.map}) + series_data.__options__().update(SeriesOptions(series_type='map', **{'mapData': self.map}).__options__()) + self.data_temp.append(series_data) + + if self.map and 'mapData' in kwargs.keys(): + kwargs.update({'mapData': self.map}) + + series_data = Series(data, series_type=series_type, **kwargs) + + series_data.__options__().update(SeriesOptions(series_type=series_type, **kwargs).__options__()) + self.data_temp.append(series_data) + + + def add_drilldown_data_set(self, data, series_type, id, **kwargs): + """set data for drilldown option in highmaps + id must be input and corresponding to drilldown arguments in data series + """ + self.drilldown_data_set_count += 1 + if self.drilldown_flag == False: + self.drilldown_flag = True + + kwargs.update({'id':id}) + series_data = Series(data, series_type=series_type, **kwargs) + series_data.__options__().update(SeriesOptions(series_type=series_type, **kwargs).__options__()) + self.drilldown_data_temp.append(series_data) + + + def add_data_from_jsonp(self, data_src, data_name = 'json_data', series_type="map", name=None, **kwargs): + """add data directly from a https source + the data_src is the https link for data using jsonp + """ + self.jsonp_data_flag = True + self.jsonp_data_url = json.dumps(data_src) + if data_name == 'data': + data_name = 'json_'+ data_name + self.jsonp_data = data_name + self.add_data_set(RawJavaScriptText(data_name), series_type, name=name, **kwargs) + + + def add_JSscript(self, js_script, js_loc): + """add (highcharts) javascript in the beginning or at the end of script + use only if necessary + """ + if js_loc == 'head': + self.jscript_head_flag = True + if self.jscript_head: + self.jscript_head = self.jscript_head + '\n' + js_script + else: + self.jscript_head = js_script + elif js_loc == 'end': + self.jscript_end_flag = True + if self.jscript_end: + self.jscript_end = self.jscript_end + '\n' + js_script + else: + self.jscript_end = js_script + else: + raise OptionTypeError("Not An Accepted script location: %s, either 'head' or 'end'" + % js_loc) + + + def add_map_data(self, geojson, **kwargs): + self.mapdata_flag = True + self.map = 'geojson' + self.mapdata = json.dumps(geojson) + + if self.data_is_coordinate: + kwargs.update({'mapData': self.map}) + series_data = Series([], 'map') + series_data.__options__().update(SeriesOptions('map', **kwargs).__options__()) + self.data_temp.append(series_data) + elif kwargs: + kwargs.update({'mapData': self.map}) + series_data = Series([], 'map') + series_data.__options__().update(SeriesOptions('map', **kwargs).__options__()) + self.data_temp.append(series_data) + elif self.data_temp: + self.data_temp[0].__options__().update({'mapData': MapObject(self.map)}) + + + def set_map_source(self, map_src, jsonp_map = False): + """set map data + use if the mapData is loaded directly from a https source + the map_src is the https link for the mapData + geojson (from jsonp) or .js formates are acceptable + default is js script from highcharts' map collection: https://code.highcharts.com/mapdata/ + """ + + if not map_src: + raise OptionTypeError("No map source input, please refer to: https://code.highcharts.com/mapdata/") + + if jsonp_map: + self.jsonp_map_flag = True + self.map = 'geojson' + self.jsonp_map_url = json.dumps(map_src) + else: + self.add_JSsource(map_src) + map_name = self._get_jsmap_name(map_src) + self.map = 'geojson' + self.jsmap = self.map + ' = Highcharts.geojson(' + map_name + ');' + self.add_JSscript('var ' + self.jsmap, 'head') + + if self.data_temp: + self.data_temp[0].__options__().update({'mapData': MapObject(self.map)}) + + def set_options(self, option_type, option_dict, force_options=False): + """set plot options""" + + if force_options: # not to use unless it is really needed + self.options[option_type].update(option_dict) + elif (option_type == 'yAxis' or option_type == 'xAxis') and isinstance(option_dict, list): + self.options[option_type] = MultiAxis(option_type) + for each_dict in option_dict: + self.options[option_type].update(**each_dict) + elif option_type == 'colors': + self.options["colors"].set_colors(option_dict) # option_dict should be a list + elif option_type == 'colorAxis': + self.options.update({'colorAxis': self.options.get('colorAxis', ColorAxisOptions())}) + self.options[option_type].update_dict(**option_dict) + elif option_type in ["global" , "lang"]: + self.setOptions[option_type].update_dict(**option_dict) + else: + self.options[option_type].update_dict(**option_dict) + + def set_dict_options(self, options): + """for dictionary-like inputs (as object in Javascript) + options must be in python dictionary format + """ + if isinstance(options, dict): + for key, option_data in options.items(): + self.set_options(key, option_data) + else: + raise OptionTypeError("Not An Accepted Input Format: %s. Must be Dictionary" %type(options)) + + + def _get_jsmap_name(self, url): + """return 'name' of the map in .js format""" + + ret = urlopen(url) + return ret.read().decode('utf-8').split('=')[0].replace(" ", "") #return the name of map file, Ex. 'Highcharts.maps["xxx/xxx"]' + + + def buildcontent(self): + """build HTML content only, no header or body tags""" + + self.buildcontainer() + self.option = json.dumps(self.options, cls = HighchartsEncoder) + self.setoption = json.dumps(self.setOptions, cls = HighchartsEncoder) + self.data = json.dumps(self.data_temp, cls = HighchartsEncoder) + + if self.drilldown_flag: + self.drilldown_data = json.dumps(self.drilldown_data_temp, cls = HighchartsEncoder) + self._htmlcontent = self.template_content_highcharts.render(chart=self).encode('utf-8') + + + def buildhtml(self): + """Build the HTML page + Create the htmlheader with css / js + Create html page + """ + self.buildcontent() + self.buildhtmlheader() + self.content = self._htmlcontent.decode('utf-8') # need to ensure unicode + self._htmlcontent = self.template_page_highcharts.render(chart=self) + return self._htmlcontent + + def buildhtmlheader(self): + """generate HTML header content""" + #Highcharts lib/ needs to make sure it's up to date + + if self.drilldown_flag: + self.add_JSsource('https://code.highcharts.com/maps/modules/drilldown.js') + + self.header_css = [ + '' % h for h in self.CSSsource + ] + + self.header_js = [ + '' % h for h in self.JSsource + ] + + self.htmlheader = '' + for css in self.header_css: + self.htmlheader += css + for js in self.header_js: + self.htmlheader += js + + + def buildcontainer(self): + """generate HTML div""" + if self.container: + return + # Create HTML div with style + if self.options['chart'].width: + if str(self.options['chart'].width)[-1] != '%': + self.div_style += 'width:%spx;' % self.options['chart'].width + else: + self.div_style += 'width:%s;' % self.options['chart'].width + if self.options['chart'].height: + if str(self.options['chart'].height)[-1] != '%': + self.div_style += 'height:%spx;' % self.options['chart'].height + else: + self.div_style += 'height:%s;' % self.options['chart'].height + + self.div_name = self.options['chart'].__dict__['renderTo'] # recheck div name + self.container = self.containerheader + \ + '
%s
\n' % (self.div_name, self.div_style, self.loading) + + @property + def htmlcontent(self): + return self.buildhtml() + + @property + def iframe(self): + htmlsrcdoc = html.escape(self.htmlcontent) + htmlsrcdoc = re.sub('\\n', ' ', htmlsrcdoc) + htmlsrcdoc = re.sub(' +', ' ', htmlsrcdoc) + width = int(self.options['chart'].__dict__['width']) if self.options['chart'].__dict__.get('width') else 820 + height = int(self.options['chart'].__dict__['height']) if self.options['chart'].__dict__.get('height') else 520 + + if self.options['chart'].__dict__.get('options3d'): + if len(htmlsrcdoc) < 99965000 : + return '' + else: + return '' + else: + return '' + + def __str__(self): + """return htmlcontent""" + #self.buildhtml() + return self.htmlcontent + + def save_file(self, filename = 'Map'): + """ save htmlcontent as .html file """ + filename = filename + '.html' + + with open(filename, 'w') as f: + #self.buildhtml() + f.write(self.htmlcontent) + + f.closed + +class HighchartsEncoder(json.JSONEncoder): + def __init__(self, *args, **kwargs): + json.JSONEncoder.__init__(self, *args, **kwargs) + self._replacement_map = {} + + def default(self, obj): + if isinstance(obj, RawJavaScriptText): + key = uuid.uuid4().hex + self._replacement_map[key] = obj.get_jstext() + return key + elif isinstance(obj, datetime.datetime): + utc = obj.utctimetuple() + obj = (u"Date.UTC({year},{month},{day},{hours},{minutes},{seconds},{millisec})" + .format(year=utc[0], month=utc[1]-1, day=utc[2], hours=utc[3], + minutes=utc[4], seconds=utc[5], millisec=obj.microsecond/1000)) + return RawJavaScriptText(obj) + elif isinstance(obj, BaseOptions): + return obj.__jsonable__() + elif isinstance(obj, CSSObject) or isinstance(obj, Formatter) or isinstance(obj, JSfunction) \ + or isinstance(obj, MapObject): + return obj.__jsonable__() + elif isinstance(obj, SeriesOptions) or isinstance(obj, Series): + return obj.__jsonable__() + elif isinstance(obj, CommonObject) or isinstance(obj, ArrayObject) or isinstance(obj, ColorObject): + return obj.__jsonable__() + else: + return json.JSONEncoder.default(self, obj) + + def encode(self, obj): + result = json.JSONEncoder.encode(self, obj) + for k, v in self._replacement_map.items(): + result = result.replace('"%s"' % (k,), v) + return result + + +class OptionTypeError(Exception): + + def __init__(self,*args): + self.args = args diff --git a/build/lib/highcharts/highmaps/options.py b/build/lib/highcharts/highmaps/options.py new file mode 100644 index 0000000..471fdd8 --- /dev/null +++ b/build/lib/highcharts/highmaps/options.py @@ -0,0 +1,534 @@ +# -*- coding: UTF-8 -*- +from past.builtins import basestring + +from .highmap_types import OptionTypeError, Series, SeriesOptions +from .common import Formatter, Events, Position, ContextButton, Button, Options3d, ResetZoomButton, \ + DrillUpButton, Labels, DataClasses, Title, Items, Navigation, Background, Breaks, Marker, \ + DateTimeLabelFormats, JSfunction, ColorObject, CSSObject, SVGObject, CommonObject, ArrayObject + +import json, datetime + +# Base Option Class +class BaseOptions(object): + + def __init__(self, **kwargs): + self.update_dict(**kwargs) + + def __display_options__(self): + print(json.dumps(self.__dict__, indent=4, sort_keys=True)) + + def __jsonable__(self): + return self.__dict__ + + def __validate_options__(self, k, v, ov): + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,dict) and isinstance(ov,dict): + keys = v.keys() + if len(keys) > 1: + raise NotImplementedError + return isinstance(v[keys[0]],ov[keys[0]]) + return isinstance(v, ov) + + def update_dict(self, **kwargs): + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + # if isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), SeriesOptions): + if k in PlotOptions.ALLOWED_OPTIONS.keys(): + if self.__getattr__(k): + self.__dict__[k].update(series_type=k, **v) + else: + v = SeriesOptions(series_type=k, **v) + self.__dict__.update({k:v}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), CommonObject): + if isinstance(v, dict): + if self.__getattr__(k): + self.__dict__[k].update(v) #update dict + else: # first + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + OptionTypeError("Not An Accepted Input Type: %s, must be dictionary" % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), ArrayObject): + if self.__getattr__(k): #existing attr + if isinstance(v, dict): + self.__dict__[k].update(v) # update array + elif isinstance(v, list): + for item in v: + self.__dict__[k].update(item) # update array + else: + OptionTypeError("Not An Accepted Input Type: %s, must be list or dictionary" + % type(v)) + else: #first + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, list): + if len(v) == 1: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v[0])}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v[0])}) + for item in v[1:]: + self.__dict__[k].update(item) + else: + OptionTypeError("Not An Accepted Input Type: %s, must be list or dictionary" + % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + (isinstance(self.ALLOWED_OPTIONS[k][0](), CSSObject) or isinstance(self.ALLOWED_OPTIONS[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): # check if v has object input + self.__dict__[k].__options__().update({key:value}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and (isinstance(self.ALLOWED_OPTIONS[k][0](), JSfunction) or \ + isinstance(self.ALLOWED_OPTIONS[k][0](), Formatter) or isinstance(self.ALLOWED_OPTIONS[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + + else: + print(self.ALLOWED_OPTIONS) + print(k, v) + raise OptionTypeError("Not An Accepted Option Type: %s" % k) + + + def __getattr__(self, item): + if not item in self.__dict__: + return None # Attribute Not Set + else: + return True + + +class ChartOptions(BaseOptions): + ALLOWED_OPTIONS = { + "animation": [bool, dict, basestring], + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": [int, float], + "borderWidth": [int, float], + "className": basestring, + "events": (Events, dict), + "height": [int,basestring], + "margin": list, + "marginBottom": int, + "marginLeft": int, + "marginRight": int, + "marginTop": int, + "plotBackgroundColor": (ColorObject, basestring, dict), + "plotBackgroundImage": basestring, + "plotBorderColor": (ColorObject, basestring, dict), + "plotBorderWidth": [int, float], + "plotShadow": bool, + "reflow": bool, + "renderTo": basestring, + "resetZoomButton": (ResetZoomButton, dict), + "selectionMarkerFill": basestring, + "shadow": bool, + "spacing": list, + "spacingBottom": int, + "spacingLeft": int, + "spacingRight": int, + "spacingTop": int, + "style": (CSSObject, dict), + "type": basestring, + "width": [int,basestring], + } + +class ColorAxisOptions(BaseOptions): + ALLOWED_OPTIONS = { + "dataClassColor": basestring, + "dataClasses": (DataClasses, dict), + "endOnTick": bool, + "events": (Events, dict), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineWidth": [float, int], + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": [float, int], + "marker": (Marker, dict), + "max": [float, int], + "maxColor": (ColorObject, basestring, dict), + "maxPadding": [float, int], + "min": [float, int], + "minColor": (ColorObject, basestring, dict), + "minPadding": [float, int], + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "reversed": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "startOfWeek": int, + "startOnTick": bool, + "stops": list, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": JSfunction, + "tickPositions": list, + "tickWidth": int, + "type": basestring, +} + + +class ColorsOptions(BaseOptions): + """ Special Case, this is simply just an array of colours """ + def __init__(self): + self.colors = {} + + def set_colors(self, colors): + if isinstance(colors, basestring): + self.colors = ColorObject(colors) + elif isinstance(colors, list) or isinstance(colors, dict): + self.colors = colors + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(colors)) + + def __jsonable__(self): + return self.colors + + +class CreditsOptions(BaseOptions): + ALLOWED_OPTIONS = { + "enabled": bool, + "href": basestring, + "position": (Position, dict), + "style": (CSSObject, dict), + "text": basestring, + } + + +class DrilldownOptions(BaseOptions): #not implement yet, need work in jinjia + ALLOWED_OPTIONS = { + "activeAxisLabelStyle": (CSSObject, dict), + "activeDataLabelStyle": (CSSObject, dict), + "animation": NotImplemented, #(bool, dict), #not sure how to implement + "drillUpButton": (DrillUpButton, dict), + "series": (Series, dict), + } + + +class ExportingOptions(BaseOptions): + ALLOWED_OPTIONS = { + "buttons": (ContextButton, dict), + "chartOptions": (ChartOptions, dict), + "enabled": bool, + "filename": basestring, + "formAttributes": NotImplemented, + "scale": int, + "sourceHeight": int, + "sourceWidth": int, + "type": basestring, + "url": basestring, + "width": int, + } + + +class GlobalOptions(BaseOptions): + ALLOWED_OPTIONS = { + "Date": NotImplemented, + "VMLRadialGradientURL": basestring, + "canvasToolsURL": basestring, + "getTimezoneOffset": (JSfunction, basestring), + "timezoneOffset": int, + "useUTC": bool, + } + + +class LabelsOptions(BaseOptions): + ALLOWED_OPTIONS = { + "items": (Items, dict), + "style": (CSSObject, dict), + } + + +class LangOptions(BaseOptions): + ALLOWED_OPTIONS = { + "decimalPoint": basestring, + "downloadJPEG": basestring, + "downloadPDF": basestring, + "downloadPNG": basestring, + "donwloadSVG": basestring, + "exportButtonTitle": basestring, + "loading": basestring, + "months": list, + "noData": basestring, + "numericSymbols": list, + "printButtonTitle": basestring, + "resetZoom": basestring, + "resetZoomTitle": basestring, + "shortMonths": list, + "thousandsSep": basestring, + "weekdays": list, + } + + +class LegendOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": [int, float], + "borderWidth": [int, float], + "enabled": bool, + "floating": bool, + "itemDistance": int, + "itemHiddenStyle": (CSSObject, dict), + "itemHoverStyle": (CSSObject, dict), + "itemMarginBottom": int, + "itemMarginTop": int, + "itemStyle": (CSSObject, dict), + "itemWidth": int, + "labelFormat": basestring, + "labelFormatter": (Formatter, JSfunction), + "layout": basestring, + "lineHeight": int, + "margin": int, + "maxHeight": int, + "navigation": (Navigation, dict), + "padding": int, + "reversed": bool, + "rtl": bool, + "shadow": bool, + "style": (CSSObject, dict), + "symbolHeight": int, + "symbolPadding": int, + "symbolRadius": int, + "symbolWidth": int, + "title": (Title, dict), + "useHTML": bool, + "valueDecimals": int, + "valueSuffix": basestring, + "verticalAlign": basestring, + "width": int, + "x": int, + "y": int, + + } + + +class LoadingOptions(BaseOptions): + ALLOWED_OPTIONS = { + "hideDuration": int, + "labelStyle": (CSSObject, dict), + "showDuration": int, + "style": (CSSObject, dict), + } + + +class MapNavigationOptions(BaseOptions): + ALLOWED_OPTIONS = { + "buttonOptions": (ContextButton, dict), + "buttons": (Button, dict), + "enableButtons": bool, + "enableDoubleClickZoom": bool, + "enableDoubleClickZoomTo": bool, + "enableMouseWheelZoom": bool, + "enableTouchZoom": bool, + "enabled": bool, + } + +class NavigationOptions(BaseOptions): + ALLOWED_OPTIONS = { + "buttonOptions": (ContextButton, dict), + "menuItemHoverStyle": (CSSObject, dict), + "menuItemStyle": (CSSObject, dict), + "menuStyle": (CSSObject, dict), + } + + +class PaneOptions(BaseOptions): + ALLOWED_OPTIONS = { + "background": (Background, list), #arrayObject + "center": list, + "endAngle": int, + "size": int, + "startAngle": int, + } + + +class PlotOptions(BaseOptions): + """ Another Special Case: Interface With all the different Highchart Plot Types Here """ + ALLOWED_OPTIONS = { + "heatmap": (SeriesOptions, dict), + "map": (SeriesOptions, dict), + "mapbubble": (SeriesOptions, dict), + "mapline": (SeriesOptions, dict), + "mappoint": (SeriesOptions, dict), + "series": (SeriesOptions, dict), + } + + +class SeriesData(BaseOptions): + """ Another Special Case: Stores Data Series in an array for returning to the chart object """ + def __init__(self): + #self.__dict__.update([]) + self = [] + + +class SubtitleOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "floating": bool, + "style": (CSSObject, dict), + "text": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + } + + +class TitleOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "floating": bool, + "margin": int, + "style": (CSSObject, dict), + "text": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + } + + +class TooltipOptions(BaseOptions): + ALLOWED_OPTIONS = { + "animation": bool, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": [int, float], + "borderWidth": [int, float], + "crosshairs": [bool, list, dict], + "enabled": bool, + "followPointer": bool, + "followTouchMove": bool, + "footerFormat": basestring, + "formatter": (Formatter, JSfunction), + "headerFormat": basestring, + "pointFormat": basestring, + "positioner": (JSfunction, basestring), + "shadow": bool, + "style": (CSSObject, dict), + "useHTML": bool, + "valueDecimals": int, + "valuePrefix": basestring, + "valueSuffix": basestring, + } + + +class xAxisOptions(BaseOptions): + ALLOWED_OPTIONS = { + "allowDecimals": bool, + "alternateGridColor": (ColorObject, basestring, dict), + "endOnTick": bool, + "events": (Events, dict), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineWidth": int, + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": int, + "max": [float, int], + "maxPadding": [float, int], + "min": [float, int], + "minPadding": [float, int], + "minRange": int, + "minTickInterval": int, + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "offset": bool, + "opposite": bool, + "reversed": bool, + "showEmpty": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "startOnTick": bool, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": JSfunction, + "tickPositions": list, + "tickWidth": int, + "title": (Title, dict), + } + + +class yAxisOptions(BaseOptions): + ALLOWED_OPTIONS = { + "allowDecimals": bool, + "alternateGridColor": (ColorObject, basestring, dict), + "endOnTick": bool, + "events": (Events, dict), + "floor": (int, float), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineWidth": int, + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "max": [float, int], + "maxColor": (ColorObject, basestring, dict), + "maxPadding": [float, int], + "min": [float, int], + "minColor": (ColorObject, basestring, dict), + "minPadding": [float, int], + "minRange": int, + "minTickInterval": int, + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "offset": bool, + "opposite": bool, + "reversed": bool, + "showEmpty": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "startOnTick": bool, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": (JSfunction, basestring), + "tickPositions": list, + "tickWidth": int, + "title": (Title, dict), + } + \ No newline at end of file diff --git a/build/lib/highcharts/highmaps/templates/base.html b/build/lib/highcharts/highmaps/templates/base.html new file mode 100644 index 0000000..af25a2b --- /dev/null +++ b/build/lib/highcharts/highmaps/templates/base.html @@ -0,0 +1,21 @@ +{% block container %} +{% endblock %} + +{% block start_script %} + +{% endblock endscript %} diff --git a/build/lib/highcharts/highmaps/templates/content.html b/build/lib/highcharts/highmaps/templates/content.html new file mode 100644 index 0000000..f0f6a95 --- /dev/null +++ b/build/lib/highcharts/highmaps/templates/content.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} +{% block container %} + {{ chart.container }} +{% endblock container %} + +{% block body_head %} + + {% if chart.jsonp_map_flag %} + $.getJSON({{chart.jsonp_map_url}}, function ({{chart.map}}) + { + {% endif %} + {% if chart.jsonp_data_flag %} + $.getJSON({{chart.jsonp_data_url}}, function ({{chart.jsonp_data}}) + { + {% endif %} + +{% endblock body_head %} + +{% block body_content %} + + {% if chart.jscript_head_flag %} + {{chart.jscript_head}} + {% endif %} + + Highcharts.setOptions({{chart.setoption}}); + var option = {{chart.option}}; + + {% if chart.mapdata_flag %} + var geojson = {{chart.mapdata}} + {% endif %} + + var data = {{chart.data}}; + option.series = data; + + {% if chart.drilldown_flag %} + var drilldowndata = {{chart.drilldown_data}}; + option.drilldown.series = drilldowndata; + {% endif %} + + var chart = new Highcharts.Map(option); + + {% if chart.jscript_end_flag %} + {{chart.jscript_end}} + {% endif %} + +{% endblock body_content %} + +{% block body_end %} + + {% if chart.jsonp_map_flag %} + }); + {% endif %} + {% if chart.jsonp_data_flag %} + }); + {% endif %} + +{% endblock body_end %} diff --git a/build/lib/highcharts/highmaps/templates/page.html b/build/lib/highcharts/highmaps/templates/page.html new file mode 100644 index 0000000..3559aaa --- /dev/null +++ b/build/lib/highcharts/highmaps/templates/page.html @@ -0,0 +1,12 @@ + + + + + {% for header_element in chart.header_css+chart.header_js %} + {{ header_element }} + {% endfor %} + + + {{ chart.content }} + + diff --git a/build/lib/highcharts/highmaps/templates/test.html b/build/lib/highcharts/highmaps/templates/test.html new file mode 100644 index 0000000..63eecc5 --- /dev/null +++ b/build/lib/highcharts/highmaps/templates/test.html @@ -0,0 +1,17 @@ + + + + + + {{ chart.title }} + + + + + +
+

Why, hello there!

+
+ + + \ No newline at end of file diff --git a/build/lib/highcharts/highstock/__init__.py b/build/lib/highcharts/highstock/__init__.py new file mode 100644 index 0000000..1fa7336 --- /dev/null +++ b/build/lib/highcharts/highstock/__init__.py @@ -0,0 +1 @@ +from .highstock import Highstock \ No newline at end of file diff --git a/build/lib/highcharts/highstock/common.py b/build/lib/highcharts/highstock/common.py new file mode 100644 index 0000000..ea43dc7 --- /dev/null +++ b/build/lib/highcharts/highstock/common.py @@ -0,0 +1,729 @@ +# -*- coding: UTF-8 -*- +""" Python-Highstock common.py +Common Functions For Highstock +""" +from past.builtins import basestring +import datetime, re + +FORMATTER_TYPE_MAPPINGS = { + "default": "function() { return this.value }", + "date": "function() { return''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + ': '+ this.y; }", + "pie": "function() { return ''+ this.point.name +': '+ \ + this.percentage +' %'; }", + "pound_yAxis": "function() { '£' + return this.value }", + "pound_tooltip": "function() { return''+ this.x + ': '+ '£' +this.y; }", + "percent": "function() { return this.value + ' %' }", + "default_tooltip": "function () { return''+ this.series.name + ': ' + this.y; }", + "percent_tooltip": "function () { return''+ this.series.name + ': ' + this.y + ' %'; }", + "date_percent_tooltip": "function () { return''+Highcharts.dateFormat('%e. %b %Y',this.x) + '
'+ this.series.name + ': ' + this.y + ' %'; }", + 'filesize': """ +function() { + fileSizeInBytes = this.value; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'date_filesize_tooltip': """ +function() { + fileSizeInBytes = this.y; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return ''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + '
' + this.series.name + ': ' + Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'filesize_tooltip': """ +function() { + fileSizeInBytes = this.y; + var i = -1; + var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB']; + do { + fileSizeInBytes = fileSizeInBytes / 1024; + i++; + } while (fileSizeInBytes > 1024); + + return '' + this.series.name + ': ' + Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]; +} +""", + 'duration': """ +function() { + seconds = this.value; + + days = Math.floor(seconds / 86400); + seconds = seconds - (days * 86400); + + hours = Math.floor(seconds / 3600); + seconds = seconds - (hours * 3600); + + mins = Math.floor(seconds / 60); + seconds = seconds - (mins * 60); + + res = ""; + if(days > 0){ + res += days + " d "; + } + if(hours > 0){ + res += hours + ' hr '; + } + if(mins > 0){ + res += mins + ' m '; + } + if(seconds > 0){ + res += seconds + ' s '; + } + return res; +} +""", + 'date_duration_tooltip': """ +function() { + seconds = this.y; + + days = Math.floor(seconds / 86400); + seconds = seconds - (days * 86400); + + hours = Math.floor(seconds / 3600); + seconds = seconds - (hours * 3600); + + mins = Math.floor(seconds / 60); + seconds = seconds - (mins * 60); + + res = ""; + if(days > 0){ + res += days + " d "; + } + if(hours > 0){ + res += hours + ' hr '; + } + if(mins > 0){ + res += mins + ' m '; + } + if(seconds > 0){ + res += seconds + ' s '; + } + return ''+Highcharts.dateFormat('%e. %b %Y %H:%M:%S',this.x) + '
'+ this.series.name + ': ' + res; +} +""", +} + +REGEX_LIST = { + "re_funct" : re.compile(r'.*function\(.*\)\{.*\}', re.I), #for inputs such as function(xxx){xxx} + "re_hcharts" : re.compile(r'.*Highcharts.*', re.I), #for inputs such as highcharts.xxxxx +} + + +class Formatter(object): + """ Base Formatter Class """ + + def __init__(self, format=None): + ### Choose either from default functions in FORMATTER_TYPE_MAPPINGS using format_type + ### or wriet a function in format_string + if format: + if format in FORMATTER_TYPE_MAPPINGS: + self.formatter = RawJavaScriptText(FORMATTER_TYPE_MAPPINGS[format]) + elif isinstance(format, basestring): + self.formatter = RawJavaScriptText(format) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring) + + def __options__(self): + return self.formatter + + def __jsonable__(self): + return self.formatter + +class ColorObject(object): + """ color object """ + + def __init__(self, color = None, **kwargs): + if not color: + color = kwargs + + if color: + if isinstance(color, dict): + tmp = [] + for i, item in enumerate(color['stops']): + tmp.append([RawJavaScriptText(x) if isinstance(x, basestring) and any([REGEX_LIST[key].search(x) for key in REGEX_LIST.keys()]) + else x for x in item ]) + color['stops'] = tmp + self.color = color + elif any([REGEX_LIST[key].search(color) for key in REGEX_LIST.keys()]): + self.color = RawJavaScriptText(color) + elif isinstance(color, basestring): + self.color = color + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % (basestring or dict)) + else: + self.color = None + + def __options__(self): + return self.color + + def __jsonable__(self): + return self.color + + +class CSSObject(object): + """ CSS style class """ + ALLOWED_OPTIONS = {} + def __init__(self, **kwargs): + self.css = kwargs + + for k, v in self.css.items(): + if isinstance(v, basestring) and any([REGEX_LIST[key].search(v) for key in REGEX_LIST.keys()]): + v = RawJavaScriptText(v) + self.css.update({k:v}) + + def __options__(self): + return self.css + + def __jsonable__(self): + return self.css + + +class SVGObject(object): + """ SVG style class """ + + def __init__(self, **kwargs): + self.svg = kwargs + + for k, v in self.svg.items(): + if isinstance(v, basestring) and any([REGEX_LIST[key].search(v) for key in REGEX_LIST.keys()]): + v = RawJavaScriptText(v) + self.svg.update({k:v}) + + def __options__(self): + return self.svg + + def __jsonable__(self): + return self.svg + + +class JSfunction(object): + + def __init__(self, function = None): + if function: + if isinstance(function, basestring): + self.function = RawJavaScriptText(function) + elif isinstance(function, JSfunction): + self.function = function + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring) + + + def __options__(self): + return self.function + + def __jsonable__(self): + return self.function + + +class RawJavaScriptText: + + def __init__(self, jstext): + self._jstext = jstext + def get_jstext(self): + return self._jstext + + +class CommonObject(object): + + def __init__(self, **kwargs): + self.process_kwargs(kwargs) + + def __validate_options__(self, k, v, ov): + + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.__dict__ + + def __jsonable__(self): + return self.__dict__ + + def update(self, kwargs): + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), CommonObject): + # re-construct input dict with existing options in objects + if self.__getattr__(k): + if isinstance(v, dict): + self.__options__()[k].update(v) + else: + self.__options__()[k].__options__().update(v) + else: + self.__options__().update({k:allowed_args[k][0](**v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), ArrayObject): + # update array + if isinstance(v, dict): + self.__dict__[k].append(self.ALLOWED_OPTIONS[k][0](**v)) + elif isinstance(v, list): + for item in v: + self.__dict__[k].append(self.ALLOWED_OPTIONS[k][0](**item)) + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + (isinstance(self.ALLOWED_OPTIONS[k][0](), CSSObject) or isinstance(self.ALLOWED_OPTIONS[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): + self.__dict__[k].__options__().update({key:value}) + else: + self.__dict__.update({k:allowed_args[k][0](**v)}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and (isinstance(self.ALLOWED_OPTIONS[k][0](), JSfunction) or + isinstance(self.ALLOWED_OPTIONS[k][0](), Formatter) or isinstance(self.ALLOWED_OPTIONS[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + + else: + raise OptionTypeError("Not An Accepted Option Type: %s" % k) + + def process_kwargs(self, kwargs): + + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if self.__validate_options__(k,v,self.ALLOWED_OPTIONS[k]): + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + self.ALLOWED_OPTIONS[k][0] in IDV_OBJECT_LIST: + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % self.ALLOWED_OPTIONS[k]) + else: + raise OptionTypeError("Option: %s Not Allowed For Event Class:" % k) + + def __getattr__(self, item): + if not item in self.__dict__: + return None # Attribute Not Set + else: + return True + + +class Events(CommonObject): + """ Class for event listener """ + + ALLOWED_OPTIONS = { + "addSeries": (JSfunction, basestring), + "afterPrint": (JSfunction, basestring), + "afterBreaks": (JSfunction, basestring), + "afterSetExtremes": (JSfunction, basestring), + "afterAnimate": (JSfunction, basestring), + "beforePrint": (JSfunction, basestring), + "checkboxClick": (JSfunction, basestring), + "click": (JSfunction, basestring), + "drilldown": (JSfunction, basestring), + "drillup": (JSfunction, basestring), + "load": (JSfunction, basestring), + "hide": (JSfunction, basestring), + "legendItemClick": (JSfunction, basestring), + "mouseOut": (JSfunction, basestring), + "mouseOver": (JSfunction, basestring), + "pointBreak": (JSfunction, basestring), + "remove": (JSfunction, basestring), + "redraw": (JSfunction, basestring), + "selection": (JSfunction, basestring), + "show": (JSfunction, basestring), + "select": (JSfunction, basestring), + "unselect": (JSfunction, basestring), + "update": (JSfunction, basestring), + "setExtremes": (JSfunction, basestring) + } + +class Point(CommonObject): + ALLOWED_OPTIONS = { + "events": Events + } + +class Position(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "verticalAlign": basestring, + "x": int, + "y": int, + } + +class ContextButton(CommonObject): + """ Option class for the export button """ + ALLOWED_OPTIONS = { + "align": basestring, + "enabled": bool, + "height": int, + "menuItems": NotImplemented, + "onclick": (JSfunction, basestring), + "symbol": basestring, + "symbolFill": basestring, + "symbolSize": int, + "symbolStroke": basestring, + "symbolStrokeWidth": int, + "symbolX": [float, int], + "symbolY": [float, int], + "text": basestring, + "theme": NotImplemented,#ThemeObject + "verticalAlign": basestring, + "width": int, + "x": int, + "y": int, + } + +class Options3d(CommonObject): + ALLOWED_OPTIONS = { + "alpha": [float, int], + "beta": [float, int], + "depth": int, + "enabled": bool, + "frame": NotImplemented, # FrameObject + "viewDistance": int + } + + +class ResetZoomButton(CommonObject): + ALLOWED_OPTIONS = { + "position": (Position, dict), + "relativeTo": basestring, + "theme": NotImplemented #ThemeObject + } + +class Labels(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": [float, int], + "borderWidth": [int, basestring], + "color": (ColorObject, basestring, dict), + "connectorColor": (ColorObject, basestring, dict), + "connectorPadding": [float, int], + "connectorWidth": [float, int], + "crop": bool, + "defer": bool, + "distance": int, + "enabled": bool, + "format": basestring, + "formatter": (Formatter, JSfunction, basestring), + "inside": bool, + "overflow": basestring, + "padding": [float, int], + "rotation": int, + "shadow": [bool, dict], #shadow object + "shape": basestring, + "softConnector": bool, + "staggerLines": int, + "step": int, + "style": (CSSObject, dict), + "text": basestring, + "textAlign": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + "zIndex": int, + } + +class Title(CommonObject): + ALLOWED_OPTIONS = { + "align": basestring, + "enabled": bool, + "margin": int, + "offset": int, + "rotation": int, + "style": (CSSObject, dict), + "text": [basestring, type(None)] + } + +class Navigation(CommonObject): + ALLOWED_OPTIONS = { + "activeColor": (ColorObject, basestring, dict), + "animation": NotImplemented, + "arrowSize": int, + "inactiveColor": (ColorObject, basestring, dict), + "style": (CSSObject, dict), + } + +class Handles(CommonObject): + ALLOWED_OPTIONS = { + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + } + +class DateTimeLabelFormats(CommonObject): + ALLOWED_OPTIONS = { + "millisecond": basestring, + "second": basestring, + "minute": basestring, + "hour": basestring, + "day": basestring, + "week": basestring, + "month": basestring, + "year": basestring, + } + +class DataGrouping(CommonObject): + ALLOWED_OPTIONS = { + "approximation": (JSfunction, basestring), + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "enabled": bool, + "forced": bool, + "groupPixelWidth": [int, float], + "smoothed": bool, + "units": list, + } + +class Select(CommonObject): + ALLOWED_OPTIONS = { + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "radius": int, + } + +class States(CommonObject): + ALLOWED_OPTIONS = { + "hover": dict, + "select": dict, + } + +class Marker(CommonObject): + ALLOWED_OPTIONS = { + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "height": int, + "lineWidth": int, + "lineColor": (ColorObject, basestring, dict), + "radius": int, + "states": (States, dict), + "symbol": basestring, + "width": int + } + +class Halo(CommonObject): + ALLOWED_OPTIONS = { + "attributes": (SVGObject, dict), + "opacity": float, + "size": int + } + +class Hover(CommonObject): + ALLOWED_OPTIONS = { + "enabled": bool, + "fillColor": (ColorObject, basestring, dict), + "halo": (Halo, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "lineWidthPlus": int, + "marker": (Marker, dict), + "radius": int, + "radiusPlus": int, + } + +class States(CommonObject): + ALLOWED_OPTIONS = { + "hover": (Hover, dict), + "select": (Select, dict) + } + +class Tooltip(CommonObject): + ALLOWED_OPTIONS = { + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "followPointer": bool, + "followTouchMove": bool, + "footerFormat": basestring, + "headerFormat": basestring, + "hideDelay": int, + "pointFormat": basestring, + "pointFormatter": (Formatter, JSfunction, basestring), + "shape": basestring, + "valueDecimals": int, + "valuePrefix": basestring, + "valueSuffix": basestring, + "xDateFormat": basestring + } + +class Shadow(CommonObject): + # not in use yet... not sure hot wo implement + ALLOWED_OPTIONS = { + "color": (ColorObject, dict), + "offsetX": [int, float], + "offsetY": [int, float], + "opacity": [int, float], + "width": [int, float], + } + +class ArrayObject(object): + + def __init__(self, **kwargs): + self.data =[] + self.process_kwargs(kwargs) + + def __validate_options__(self, k,v,ov): + + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.data + + def __jsonable__(self): + return self.data + + def update(self, kwargs): + self.process_kwargs(kwargs) + + def process_kwargs(self, kwargs): + temp = {} + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + if self.__validate_options__(k,v,self.ALLOWED_OPTIONS[k]): + if isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + self.ALLOWED_OPTIONS[k][0] in IDV_OBJECT_LIST: + if isinstance(v, dict): + temp.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + temp.update({k:v}) + else: + temp.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + temp.update({k:v}) + else: + raise OptionTypeError("Option Type Mismatch: Expected: %s" % self.ALLOWED_OPTIONS[k]) + else: + raise OptionTypeError("Option: %s Not Allowed For Event Class:" % k) + self.data.append(temp) + +class PlotBands(ArrayObject): + ALLOWED_OPTIONS = { + "borderColor": (ColorObject, basestring, dict), + "borderWidth": [int, basestring], + "color": (ColorObject, basestring, dict), + "events": (Events, dict), + "from": [int, float, datetime.datetime], + "id": basestring, + "label": (Labels, dict), + "to": [int, float, datetime.datetime], + "zIndex": int + } + +class PlotLines(ArrayObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "dashStyle": int, + "events": (Events, dict), + "id": basestring, + "label": (Labels, dict), + "value": [int, float], + "width": int, + "zIndex": int + } + +class Items(ArrayObject): + ALLOWED_OPTIONS = { + "html": basestring, + "style": (CSSObject, dict) + } + +class Background(ArrayObject): + ALLOWED_OPTIONS = { + "backgroundColor": (ColorObject, basestring, dict), + "borderWidth": [int, basestring], + "borderColor": (ColorObject, basestring, dict), + "innerWidth": int, + "outerWidth": int, + "outerRadius": basestring, + "shape": basestring, + } + +class Breaks(ArrayObject): + ALLOWED_OPTIONS = { + "breakSize": int, + "from": [int, float, datetime.datetime], + "repeat": int, + "to": [int, float, datetime.datetime], + } + +class Buttons(ArrayObject): + ALLOWED_OPTIONS = { + "type": basestring, + "count": [int, float], + "text": basestring, + } + +class Zones(ArrayObject): + ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "value": [int, float], + } + +class Levels(ArrayObject): + ALLOWED_OPTIONS = { + "borderColor": (ColorObject, basestring, dict), + "borderDashStyle": basestring, + "borderWidth": [int, basestring], + "color": (ColorObject, basestring, dict), + "dataLabels": (Labels, dict), + "layoutAlgorithm": basestring, + "layoutStartingDirection": basestring, + "level": int, + } + + +IDV_OBJECT_LIST = [JSfunction, Formatter, Halo, Marker, Labels, + Position, Hover, Select, Events, States, ContextButton, + CSSObject, SVGObject, ColorObject, + RawJavaScriptText, DateTimeLabelFormats] + + +class OptionTypeError(Exception): + + def __init__(self, *args): + self.args = args diff --git a/build/lib/highcharts/highstock/highstock.py b/build/lib/highcharts/highstock/highstock.py new file mode 100644 index 0000000..9b38c2f --- /dev/null +++ b/build/lib/highcharts/highstock/highstock.py @@ -0,0 +1,440 @@ +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals, absolute_import +from future.standard_library import install_aliases +install_aliases() + +from jinja2 import Environment, PackageLoader + +import json, uuid +import re +import datetime +import html +try: + # works with older (3.9 and below) versions, throws an exception with newer (3.10 and above) versions + from collections import Iterable +#if an import error change to trying 3.10 and above notation +except ImportError: + from collections.abc import Iterable +from .options import BaseOptions, ChartOptions, \ + ColorsOptions, CreditsOptions, ExportingOptions, \ + GlobalOptions, LabelsOptions, LangOptions, \ + LegendOptions, LoadingOptions, NavigatorOptions, NavigationOptions, \ + PlotOptions, RangeSelectorOptions, ScrollbarOptions, SeriesData, SubtitleOptions, TitleOptions, \ + TooltipOptions, xAxisOptions, yAxisOptions, MultiAxis + +from .highstock_types import Series, SeriesOptions +from .common import Levels, Formatter, CSSObject, SVGObject, JSfunction, RawJavaScriptText, \ + CommonObject, ArrayObject, ColorObject + +CONTENT_FILENAME = "./content.html" +PAGE_FILENAME = "./page.html" + +pl = PackageLoader('highcharts.highstock', 'templates') +jinja2_env = Environment(lstrip_blocks=True, trim_blocks=True, loader=pl) + +template_content = jinja2_env.get_template(CONTENT_FILENAME) +template_page = jinja2_env.get_template(PAGE_FILENAME) + + +class Highstock(object): + """ + Highstock Base class. + """ + #: chart count + count = 0 + + # this attribute is overriden by children of this + # class + CHART_FILENAME = None + template_environment = Environment(lstrip_blocks=True, trim_blocks=True, + loader=pl) + + def __init__(self, **kwargs): + """ + This is the base class for all the charts. The following keywords are + accepted: + :keyword: **display_container** - default: ``True`` + """ + # set the model + self.model = self.__class__.__name__ #: The chart model, + self.div_name = kwargs.get("renderTo", "container") + + # an Instance of Jinja2 template + self.template_page_highcharts = template_page + self.template_content_highcharts = template_content + + # set Javascript src, Highcharts lib needs to make sure it's up to date + self.JSsource = [ + 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js', + 'https://code.highcharts.com/stock/6/highstock.js', + 'https://code.highcharts.com/stock/6/modules/exporting.js', + 'https://code.highcharts.com/6/highcharts-more.js', + ] + + # set CSS src + self.CSSsource = [ + 'https://www.highcharts.com/highslide/highslide.css', + + ] + # set data + self.data = [] + self.data_temp = [] + + # set navigator series + self.navi_seri_flag = False + self.navi_seri = {} + self.navi_seri_temp = {} + + # Data from jsonp + self.jsonp_data_flag = False + self.jsonp_data_url_list = [] # DEM 2017/04/25: List of JSON data sources + + # javascript + self.jscript_head_flag = False + self.jscript_head = kwargs.get('jscript_head', None) + self.jscript_end_flag = False + self.jscript_end = kwargs.get('jscript_end', None) + + # accepted keywords + self.div_style = kwargs.get('style', '') + self.date_flag = kwargs.get('date_flag', False) + + # None keywords attribute that should be modified by methods + # We should change all these to _attr + + self._htmlcontent = '' #: written by buildhtml + self.htmlheader = '' + #: Place holder for the graph (the HTML div) + #: Written by ``buildcontainer`` + self.container = u'' + #: Header for javascript code + self.containerheader = u'' + # Loading message + self.loading = 'Loading....' + + # Bind Base Classes to self + self.options = { + "chart": ChartOptions(), + "colors": ColorsOptions(), + "credits": CreditsOptions(), + #"data": #NotImplemented + "exporting": ExportingOptions(), + "labels": LabelsOptions(), + "legend": LegendOptions(), + "loading": LoadingOptions(), + "navigation": NavigationOptions(), + "navigator": NavigatorOptions(), + "plotOptions": PlotOptions(), + "rangeSelector": RangeSelectorOptions(), + "scrollbar": ScrollbarOptions(), + "series": SeriesData(), + "subtitle": SubtitleOptions(), + "title": TitleOptions(), + "tooltip": TooltipOptions(), + "xAxis": xAxisOptions(), + "yAxis": yAxisOptions(), + } + + self.setOptions = { + "global": GlobalOptions(), + "lang": LangOptions(), + } + + self.__load_defaults__() + + # Process kwargs + allowed_kwargs = [ + "width", + "height", + "renderTo", + "backgroundColor", + "events", + "marginBottom", + "marginTop", + "marginRight", + "marginLeft" + ] + + for keyword in allowed_kwargs: + if keyword in kwargs: + self.options['chart'].update_dict(**{keyword:kwargs[keyword]}) + # Some Extra Vals to store: + self.data_set_count = 0 + self.drilldown_data_set_count = 0 + + + def __load_defaults__(self): + self.options["chart"].update_dict(renderTo='container') + self.options["title"].update_dict(text='A New Highchart') + self.options["credits"].update_dict(enabled=False) + + + def add_JSsource(self, new_src): + """add additional js script source(s)""" + if isinstance(new_src, list): + for h in new_src: + self.JSsource.append(h) + elif isinstance(new_src, basestring): + self.JSsource.append(new_src) + else: + raise OptionTypeError("Option: %s Not Allowed For Series Type: %s" % type(new_src)) + + + def add_CSSsource(self, new_src): + """add additional css source(s)""" + if isinstance(new_src, list): + for h in new_src: + self.CSSsource.append(h) + elif isinstance(new_src, basestring): + self.CSSsource.append(new_src) + else: + raise OptionTypeError("Option: %s Not Allowed For Series Type: %s" % type(new_src)) + + + def add_data_set(self, data, series_type="line", name=None, **kwargs): + """set data for series option in highstocks""" + + self.data_set_count += 1 + if not name: + name = "Series %d" % self.data_set_count + kwargs.update({'name':name}) + + series_data = Series(data, series_type=series_type, **kwargs) + series_data.__options__().update(SeriesOptions(series_type=series_type, **kwargs).__options__()) + self.data_temp.append(series_data) + + + def add_data_from_jsonp(self, data_src, data_name='json_data', series_type="line", name=None, **kwargs): + """set map data directly from a https source + the data_src is the https link for data + and it must be in jsonp format + """ + if not self.jsonp_data_flag: + self.jsonp_data_flag = True + + if data_name == 'data': + data_name = 'json_'+ data_name + + self.jsonp_data = data_name + self.add_data_set(RawJavaScriptText(self.jsonp_data), series_type, name=name, **kwargs) + # DEM 2017/04/25: Append new JSON data source to a list instead of + # replacing whatever already exists + self.jsonp_data_url_list.append(json.dumps(data_src)) + + + def add_navi_series(self, data, series_type="line", **kwargs): + """set series for navigator option in highstocks""" + + self.navi_seri_flag = True + series_data = Series(data, series_type=series_type, **kwargs) + series_data.__options__().update(SeriesOptions(series_type=series_type, **kwargs).__options__()) + self.navi_seri_temp = series_data + + def add_navi_series_from_jsonp(self, data_src=None, data_name='json_data', series_type="line", **kwargs): + """set series for navigator option in highstocks""" + + if not self.jsonp_data_flag: + self.jsonp_data_flag = True + self.jsonp_data_url = json.dumps(data_src) + + if data_name == 'data': + data_name = 'json_'+ data_name + + self.jsonp_data = data_name + + self.add_navi_series(RawJavaScriptText(self.jsonp_data), series_type, **kwargs) + + + def add_JSscript(self, js_script, js_loc): + """add (highcharts) javascript in the beginning or at the end of script + use only if necessary + """ + if js_loc == 'head': + self.jscript_head_flag = True + if self.jscript_head: + self.jscript_head = self.jscript_head + '\n' + js_script + else: + self.jscript_head = js_script + elif js_loc == 'end': + self.jscript_end_flag = True + if self.jscript_end: + self.jscript_end = self.jscript_end + '\n' + js_script + else: + self.jscript_end = js_script + else: + raise OptionTypeError("Not An Accepted script location: %s, either 'head' or 'end'" + % js_loc) + + + def set_options(self, option_type, option_dict, force_options=False): + """set plot options """ + if force_options: + self.options[option_type].update(option_dict) + elif (option_type == 'yAxis' or option_type == 'xAxis') and isinstance(option_dict, list): + # For multi-Axis + self.options[option_type] = MultiAxis(option_type) + for each_dict in option_dict: + self.options[option_type].update(**each_dict) + elif option_type == 'colors': + self.options["colors"].set_colors(option_dict) # option_dict should be a list + elif option_type in ["global" , "lang"]: #Highcharts.setOptions: + self.setOptions[option_type].update_dict(**option_dict) + else: + self.options[option_type].update_dict(**option_dict) + + + def set_dict_options(self, options): + """for dictionary-like inputs (as object in Javascript) + options must be in python dictionary format + """ + if isinstance(options, dict): + for key, option_data in options.items(): + self.set_options(key, option_data) + else: + raise OptionTypeError("Not An Accepted Input Format: %s. Must be Dictionary" %type(options)) + + + def buildcontent(self): + """build HTML content only, no header or body tags""" + + self.buildcontainer() + self.option = json.dumps(self.options, cls = HighchartsEncoder) + self.setoption = json.dumps(self.setOptions, cls = HighchartsEncoder) + self.data = json.dumps(self.data_temp, cls = HighchartsEncoder) + + # DEM 2017/04/25: Make 'data' available as an array + # ... this permits jinja2 array access to each data definition + # ... which is useful for looping over multiple data sources + self.data_list = [json.dumps(x, cls = HighchartsEncoder) for x in self.data_temp] + + if self.navi_seri_flag: + self.navi_seri = json.dumps(self.navi_seri_temp, cls = HighchartsEncoder) + + self._htmlcontent = self.template_content_highcharts.render(chart=self).encode('utf-8') + + + def buildhtml(self): + """build the HTML page + create the htmlheader with css / js + create html page + """ + self.buildcontent() + self.buildhtmlheader() + self.content = self._htmlcontent.decode('utf-8') # need to ensure unicode + self._htmlcontent = self.template_page_highcharts.render(chart=self) + return self._htmlcontent + + + def buildhtmlheader(self): + """generate HTML header content""" + + self.header_css = [ + '' % h for h in self.CSSsource + ] + + self.header_js = [ + '' % h for h in self.JSsource + ] + + self.htmlheader = '' + for css in self.header_css: + self.htmlheader += css + for js in self.header_js: + self.htmlheader += js + + + def buildcontainer(self): + """generate HTML div""" + if self.container: + return + # Create HTML div with style + if self.options['chart'].width: + if str(self.options['chart'].width)[-1] != '%': + self.div_style += 'width:%spx;' % self.options['chart'].width + else: + self.div_style += 'width:%s;' % self.options['chart'].width + if self.options['chart'].height: + if str(self.options['chart'].height)[-1] != '%': + self.div_style += 'height:%spx;' % self.options['chart'].height + else: + self.div_style += 'height:%s;' % self.options['chart'].height + + self.div_name = self.options['chart'].__dict__['renderTo'] # recheck div name + self.container = self.containerheader + \ + '
%s
\n' % (self.div_name, self.div_style, self.loading) + + @property + def htmlcontent(self): + return self.buildhtml() + + @property + def iframe(self): + htmlsrcdoc = html.escape(self.htmlcontent) + htmlsrcdoc = re.sub('\\n', ' ', htmlsrcdoc) + htmlsrcdoc = re.sub(' +', ' ', htmlsrcdoc) + width = int(self.options['chart'].__dict__['width']) if self.options['chart'].__dict__.get('width') else 820 + height = int(self.options['chart'].__dict__['height']) if self.options['chart'].__dict__.get('height') else 520 + + if self.options['chart'].__dict__.get('options3d'): + if len(htmlsrcdoc) < 99965000 : + return '' + else: + return '' + else: + return '' + + def __str__(self): + """return htmlcontent""" + #self.buildhtml() + return self.htmlcontent + + def save_file(self, filename = 'StockChart'): + """ save htmlcontent as .html file """ + filename = filename + '.html' + + with open(filename, 'w') as f: + #self.buildhtml() + f.write(self.htmlcontent) + + f.closed + +class HighchartsEncoder(json.JSONEncoder): + def __init__(self, *args, **kwargs): + json.JSONEncoder.__init__(self, *args, **kwargs) + self._replacement_map = {} + + def default(self, obj): + if isinstance(obj, RawJavaScriptText): + key = uuid.uuid4().hex + self._replacement_map[key] = obj.get_jstext() + return key + elif isinstance(obj, datetime.datetime): + utc = obj.utctimetuple() + obj = (u"Date.UTC({year},{month},{day},{hours},{minutes},{seconds},{millisec})" + .format(year=utc[0], month=utc[1]-1, day=utc[2], hours=utc[3], + minutes=utc[4], seconds=utc[5], millisec=obj.microsecond/1000)) + return RawJavaScriptText(obj) + elif isinstance(obj, BaseOptions) or isinstance(obj, MultiAxis): + return obj.__jsonable__() + elif isinstance(obj, CSSObject) or isinstance(obj, Formatter) or isinstance(obj, JSfunction): + return obj.__jsonable__() + elif isinstance(obj, SeriesOptions) or isinstance(obj, Series): + return obj.__jsonable__() + elif isinstance(obj, CommonObject) or isinstance(obj, ArrayObject) or isinstance(obj, ColorObject): + return obj.__jsonable__() + else: + return json.JSONEncoder.default(self, obj) + + def encode(self, obj): + result = json.JSONEncoder.encode(self, obj) + for k, v in self._replacement_map.items(): + result = result.replace('"%s"' % (k,), v) + return result + + +class OptionTypeError(Exception): + + def __init__(self,*args): + self.args = args diff --git a/build/lib/highcharts/highstock/highstock_helper.py b/build/lib/highcharts/highstock/highstock_helper.py new file mode 100644 index 0000000..029d885 --- /dev/null +++ b/build/lib/highcharts/highstock/highstock_helper.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +from future.standard_library import install_aliases +install_aliases() +from past.builtins import basestring + +from urllib.request import urlopen +import urllib + +import json, os, sys +import datetime, re +from datetime import tzinfo + + +def jsonp_loader(url, prefix_regex=r'^(.*\()', suffix_regex=r'(\);)$', sub_d=None, sub_by=''): + """jsonp_loader is to request (JSON) data from a server in a different domain (JSONP) + and covert to python readable data. + 1. url is the url (https) where data is located + 2. "prefix_regex" and "suffix_regex" are regex patterns used to + remove JSONP specific prefix and suffix, such as callback header: "callback(" and end: ");", + 3. "sub_d" is regex patterns for any unwanted string in loaded json data (will be replaced by sub_by). + 4. "sub_by" is the string to replace any unwanted string defined by sub_d + For function coverstion, such as Data.UTC to datetime.datetime, please check JSONPDecoder + """ + + hdr = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7'} + req = urllib.request.Request(url, headers=hdr) + page = urlopen(req) + result = page.read().decode("utf-8") + # replace all the redundant info with sub_by + if sub_d: + result = re.sub(sub_d, sub_by, result) + + prefix = re.search(prefix_regex, result).group() + suffix = re.search(suffix_regex, result).group() + if result.startswith(prefix) and result.endswith(suffix): + result = result[len(prefix):-len(suffix)] + return json.loads(result, encoding='utf8', cls=JSONPDecoder) + +def interpolateRGB(lowRGB, highRGB, fraction): + color = [] + + for i in range(3): + color.append((highRGB[i] - lowRGB[i]) * fraction + lowRGB[i]) + + return 'rgb(' + str(int(round(color[0],0))) + ',' + str(int(round(color[1],0))) + ',' + \ + str(int(round(color[2],0))) + ')' + + +class JSONPDecoder(json.JSONDecoder): + """Customized JSON decoder. It is used to convert everything + that is python non-compatible (usually Javascript functions) + to one that can be read by python. It needs to coordinate with + customized JSON encoder in main library, such as highcharts.py, + to convert back to Javascript-compatiable functions. + For example: in _iterdecode, it checks if queried JSON has Data.UTC + and (if yes)converts it to datetime.datetime + """ + + def decode(self, json_string): + """ + json_string is basicly string that you give to json.loads method + """ + + default_obj = super(JSONPDecoder, self).decode(json_string) + + return list(self._iterdecode(default_obj))[0] + + def _iterdecode_list(self, lst): + new_lst = [] + for item in lst: + for chunk in self._iterdecode(item): + new_lst.append(chunk) + yield new_lst + + def _iterdecode_dict(self, dct): + new_dct = {} + for key, value in dct.items(): + for chunk in self._iterdecode(value): + new_dct[key] = chunk + yield new_dct + + def _iterdecode(self, obj): + if isinstance(obj, (list, tuple)): + for chunk in self._iterdecode_list(obj): + yield chunk + + elif isinstance(obj, dict): + for chunk in self._iterdecode_dict(obj): + yield chunk + + elif isinstance(obj, basestring) and JSONPDecoder.is_js_date_utc(obj): + m = JSONPDecoder.is_js_date_utc(obj) + yield JSONPDecoder.json2datetime(m) + + else: + yield obj + + @staticmethod + def is_js_date_utc(json): + """Check if the string contains Date.UTC function + and return match group(s) if there is + """ + + JS_date_utc_pattern = r'Date\.UTC\(([0-9]+,[0-9]+,[0-9]+)(,[0-9]+,[0-9]+,[0-9]+)?(,[0-9]+)?\)' + re_date = re.compile(JS_date_utc_pattern, re.M) + + if re_date.search(json): + return re_date.search(json).group(0) + else: + return False + + @staticmethod + def json2datetime(json): + """Convert JSON representation to date or datetime object depending on + the argument count. Requires UTC datetime representation. + Raises ValueError if the string cannot be parsed. + """ + + json_m = re.search(r'([0-9]+,[0-9]+,[0-9]+)(,[0-9]+,[0-9]+,[0-9]+)?(,[0-9]+)?', json) + args=json_m.group(0).split(',') + + try: + args = list(map(int, args)) + except ValueError: + raise ValueError('Invalid arguments: %s'%json) + + if len(args)==3: + return datetime.datetime(args[0], args[1]+1, args[2]) + elif len(args)==6: + return datetime.datetime(args[0], args[1]+1, args[2], + args[3], args[4], args[5], tzinfo=UTC()) + elif len(args)==7: + args[6]*=1000 + return datetime.datetime(args[0], args[1]+1, args[2], + args[3], args[4], args[5], args[6], tzinfo=UTC()) + raise ValueError('Invalid number of arguments: %s'%json) + + +class UTC(tzinfo): + """UTC""" + + ZERO=datetime.timedelta(0) + + def utcoffset(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return ZERO + + diff --git a/build/lib/highcharts/highstock/highstock_types.py b/build/lib/highcharts/highstock/highstock_types.py new file mode 100644 index 0000000..83e76c6 --- /dev/null +++ b/build/lib/highcharts/highstock/highstock_types.py @@ -0,0 +1,559 @@ +# -*- coding: UTF-8 -*- +from past.builtins import basestring + +import json, datetime +from .common import Formatter, Events, Position, ContextButton, Options3d, ResetZoomButton, DataGrouping, \ + Labels, Marker, Point, PlotBands, States, Tooltip, Title, Zones, Levels, Shadow, \ + JSfunction, ColorObject, CSSObject, SVGObject, \ + CommonObject, ArrayObject + +PLOT_OPTION_ALLOWED_ARGS = { + "common": { + "allowPointSelect": bool, + "animation": bool, + "color": (ColorObject, basestring, dict), + "cursor": basestring, + "dataGrouping": (DataGrouping, dict), + "dataLabels": (Labels, dict), + "enableMouseTracking": bool, + "events": (Events, dict), + "id": basestring, + "index": [float, int], + "name": basestring, + "point": (Point, dict), + "selected": bool, + "showCheckbox": bool, + "showInLegend": bool, + "states": (States, dict), + "stickyTracking": bool, + "tooltip": (Tooltip, dict), + "visible": bool, + "xAxis": [int, basestring], + "yAxis": [int, basestring], + "zIndex": int, + "zoneAxis": basestring, + "zones": (Zones, dict), + }, + "area": { + "compare": basestring, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "gapSize": [int, float], + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": (int,basestring, datetime.datetime), + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "step": bool, + "threshold": [int, type(None)], + "trackByArea": bool, + "turboThreshold": int, + }, + "arearange": { + "compare": basestring, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "gapSize": [int, float], + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "trackByArea": bool, + "turboThreshold": int, + }, + "areaspline": { + "cropThreshold": int, + "compare": basestring, + "connectNulls": bool, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "gapSize": [int, float], + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "threshold": [int, type(None)], + "turboThreshold": int, + "trackByArea": bool, + }, + "areasplinerange": { + "cropThreshold": int, + "compare": basestring, + "connectNulls": bool, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "gapSize": [int, float], + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "threshold": [int, type(None)], + "turboThreshold": int, + "trackByArea": bool, + }, + "candlestick": { + "colors": list, + "cropThreshold": int, + "connectNulls": bool, + "dashStyle": basestring, + "fillColor": (ColorObject, basestring, dict), + "fillOpacity": float, + "groupPadding": [int, float], + "grouping": bool, + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "minPointLength": [int, float], + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointRange": [int, float], + "pointStart": [int,basestring,datetime.datetime], + "pointWidth": [int, float], + "shadow": [bool, dict], + "stack": basestring, + "upColor": (ColorObject, basestring, dict), + "upLineColor": (ColorObject, basestring, dict), + "turboThreshold": int, + "trackByArea": bool, + }, + "column": { + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": [int, basestring], + "colorByPoint": bool, + "colors": list, + "compare": basestring, + "cropThreshold": int, + "groupPadding": [float, int], + "grouping": bool, + "keys": list, + "linkedTo": basestring, + "minPointLength": int, + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPadding": [float, int], + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "pointWidth": [int, float], + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "turboThreshold": int, + }, + "columnrange": { + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": [int, basestring], + "colorByPoint": bool, + "colors": list, + "compare": basestring, + "cropThreshold": int, + "groupPadding": [float, int], + "grouping": bool, + "keys": list, + "linkedTo": basestring, + "minPointLength": int, + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPadding": [float, int], + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "pointWidth": [int, float], + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "turboThreshold": int, + }, + "flags": { + "colors": list, + "cropThreshold": int, + "keys": list, + "legendIndex": [int, float], + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": basestring, + "onSeries": basestring, + "pointIntervalUnit": basestring, + "shadow": [bool, dict], + "shape": basestring, + "stack": basestring, + "stackDistance": [int, float], + "style": (CSSObject, dict), + "y": [int, float], + "useHTML": bool, + }, + "line": { + "compare": basestring, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "gapSize": [int, float], + "keys": list, + "legendIndex": [int, float], + "lineWidth": int, + "linecap": basestring, + "linkedTo": basestring, + "marker": (Marker, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "step": basestring, + "turboThreshold": int, + }, + "ohlc": { + "colorByPoint": bool, + "colors": list, + "compare": basestring, + "cropThreshold": int, + "groupPadding": [float, int], + "grouping": bool, + "keys": list, + "legendIndex": [int, float], + "lineWidth": int, + "linkedTo": basestring, + "minPointLength": int, + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPadding": [float, int], + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "pointWidth": [int, float], + "shadow": [bool, dict], + "stack": basestring, + "stacking": basestring, + "turboThreshold": int, + }, + "polygon": { + "compare": basestring, + "cropThreshold": int, + "dashStyle": basestring, + "keys": list, + "legendIndex": [int, float], + "lineWidth": int, + "linkedTo": basestring, + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stacking": basestring, + "turboThreshold": int, + }, + "scatter": { + "compare": basestring, + "cropThreshold": int, + "dashStyle": basestring, + "keys": list, + "legendIndex": [int, float], + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "negativeColor": (ColorObject, basestring, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stacking": basestring, + "turboThreshold": int, + }, + "series": { + "compare": basestring, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "gapSize": [int, float], + "keys": list, + "legendIndex": [int, float], + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stacking": basestring, + "turboThreshold": int, + }, + "spline": { + "compare": basestring, + "connectNulls": bool, + "cropThreshold": int, + "dashStyle": basestring, + "gapSize": [int, float], + "keys": list, + "legendIndex": [int, float], + "lineWidth": int, + "linkedTo": basestring, + "marker": (Marker, dict), + "pointInterval": int, + "pointIntervalUnit": basestring, + "pointPlacement": [basestring, int, float], + "pointRange": int, + "pointStart": [int,basestring,datetime.datetime], + "shadow": [bool, dict], + "stacking": basestring, + "turboThreshold": int, + }, +} + +DATA_SERIES_ALLOWED_OPTIONS = { + "color": (ColorObject, basestring, dict), + "connectEnds": bool, + "connectNulls": bool, + "dataLabels": (Labels, dict), + "dataParser": NotImplemented, + "dataURL": NotImplemented, + "drilldown": basestring, + "events": (Events, dict), + "high": [int, float], + "id": basestring, + "index": int, + "legendIndex": int, + "name": basestring, + "marker": (Marker, dict), + "selected": bool, + "sliced": bool, + "showInLegend": bool, + "type": basestring, + "visible": bool, + "x": [int, float, datetime.datetime], + "xAxis": int, + "yAxis": int, +} + +DEFAULT_OPTIONS = { + +} + +class OptionTypeError(Exception): + + def __init__(self,*args): + self.args = args + + +class SeriesOptions(object): + """Class for plotOptions""" + + def __init__(self,series_type="line",supress_errors=False,**kwargs): + self.load_defaults(series_type) + self.process_kwargs(kwargs,series_type=series_type,supress_errors=supress_errors) + + @staticmethod + def __validate_options__(k,v,ov): + if isinstance(ov,list): + if isinstance(v,tuple(ov)): return True + else: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + else: + if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,ov): return True + else: return False + + def __options__(self): + return self.__dict__ + + def __jsonable__(self): + return self.__dict__ + + def update(self,series_type, **kwargs): + allowed_args = PLOT_OPTION_ALLOWED_ARGS[series_type] + allowed_args.update(PLOT_OPTION_ALLOWED_ARGS["common"]) + for k, v in kwargs.items(): + if k in allowed_args: + if SeriesOptions.__validate_options__(k,v,allowed_args[k]): + if isinstance(allowed_args[k], tuple) and isinstance(allowed_args[k][0](), CommonObject): + # re-construct input dict with existing options in objects + if self.__getattr__(k): + if isinstance(v, dict): + self.__options__()[k].update(v) + else: + self.__options__()[k].__options__().update(v) + else: + self.__options__().update({k:allowed_args[k][0](**v)}) + + elif isinstance(allowed_args[k], tuple) and isinstance(allowed_args[k][0](), ArrayObject): + # update array + if isinstance(v, dict): + self.__dict__[k].append(allowed_args[k][0](**v)) + elif isinstance(v, list): + for item in v: + self.__dict__[k].append(allowed_args[k][0](**item)) + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(v)) + + elif isinstance(allowed_args[k], tuple) and \ + (isinstance(allowed_args[k][0](), CSSObject) or isinstance(allowed_args[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): + self.__dict__[k].__options__().update({key:value}) + else: + self.__dict__.update({k:allowed_args[k][0](**v)}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + + elif isinstance(allowed_args[k], tuple) and (isinstance(allowed_args[k][0](), JSfunction) or + isinstance(allowed_args[k][0](), Formatter) or isinstance(allowed_args[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + print(k,v) + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % allowed_args[k]) + + + def process_kwargs(self,kwargs,series_type,supress_errors=False): + allowed_args = PLOT_OPTION_ALLOWED_ARGS[series_type] + allowed_args.update(PLOT_OPTION_ALLOWED_ARGS["common"]) + + for k, v in kwargs.items(): + if k in allowed_args: + if SeriesOptions.__validate_options__(k,v,allowed_args[k]): + if isinstance(allowed_args[k], tuple): + if isinstance(v, dict): + self.__dict__.update({k:allowed_args[k][0](**v)}) + elif isinstance(v, list): + if len(v) == 1: + self.__dict__.update({k:allowed_args[k][0](**v[0])}) + else: + self.__dict__.update({k:allowed_args[k][0](**v[0])}) + for item in v[1:]: + self.__dict__[k].update(item) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:allowed_args[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + print(k,v) + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % allowed_args[k]) + + + def load_defaults(self,series_type): # not in use + self.process_kwargs(DEFAULT_OPTIONS.get(series_type,{}),series_type) + + def __getattr__(self,item): + if not item in self.__dict__: + return None # Attribute Not Set + else: + return True + + +class Series(object): + """Series class for input data """ + + def __init__(self,data,series_type="line",supress_errors=False,**kwargs): + + # List of dictionaries. Each dict contains data and properties, + # which need to handle before construct the object for series + if isinstance(data, list): + for item in data: + if isinstance(item, dict): + for k, v in item.items(): + if k in DATA_SERIES_ALLOWED_OPTIONS: + if SeriesOptions.__validate_options__(k,v,DATA_SERIES_ALLOWED_OPTIONS[k]): + if isinstance(DATA_SERIES_ALLOWED_OPTIONS[k], tuple): + if isinstance(v, dict): + item.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, datetime.datetime): + item.update({k:v}) + else: + item.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](v)}) + else: + item.update({k:v}) + + self.__dict__.update({ + "data": data, + "type": series_type, + }) + + # Series propertie can be input as kwargs, which is handled here + for k, v in kwargs.items(): + if k in DATA_SERIES_ALLOWED_OPTIONS: + if SeriesOptions.__validate_options__(k,v,DATA_SERIES_ALLOWED_OPTIONS[k]): + if isinstance(DATA_SERIES_ALLOWED_OPTIONS[k], tuple): + if isinstance(v, dict): + self.__dict__.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \ + isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \ + isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime): + self.__dict__.update({k:v}) + else: + self.__dict__.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + else: + if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % DATA_SERIES_ALLOWED_OPTIONS[k]) + + + def __options__(self): + return self.__dict__ + + def __jsonable__(self): + return self.__dict__ diff --git a/build/lib/highcharts/highstock/options.py b/build/lib/highcharts/highstock/options.py new file mode 100644 index 0000000..4a21318 --- /dev/null +++ b/build/lib/highcharts/highstock/options.py @@ -0,0 +1,587 @@ +# -*- coding: UTF-8 -*- +from past.builtins import basestring + +from .highstock_types import OptionTypeError, Series, SeriesOptions +from .common import Formatter, Events, Position, ContextButton, Options3d, ResetZoomButton, \ + Labels, PlotBands, PlotLines, Title, Items, Navigation, Handles, Background, Breaks, \ + DateTimeLabelFormats, Zones, Levels, Buttons, \ + JSfunction, ColorObject, CSSObject, SVGObject, CommonObject, ArrayObject + +import json, datetime + +# Base Option Class +class BaseOptions(object): + + def __init__(self,**kwargs): + self.update_dict(**kwargs) + + def __display_options__(self): + print(json.dumps(self.__dict__, indent=4, sort_keys=True)) + + def __jsonable__(self): + return self.__dict__ + + def __validate_options__(self, k, v, ov): + if ov == NotImplemented: + raise OptionTypeError("Option Type Currently Not Supported: %s" % k) + if isinstance(v,dict) and isinstance(ov,dict): + keys = v.keys() + if len(keys) > 1: + raise NotImplementedError + return isinstance(v[keys[0]],ov[keys[0]]) + return isinstance(v, ov) + + def update_dict(self, **kwargs): + for k, v in kwargs.items(): + if k in self.ALLOWED_OPTIONS: + #if isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), SeriesOptions): + if k in PlotOptions.ALLOWED_OPTIONS.keys(): + if self.__getattr__(k): + self.__dict__[k].update(series_type=k, **v) + else: + v = SeriesOptions(series_type=k, **v) + self.__dict__.update({k:v}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), CommonObject): + if isinstance(v, dict): + if self.__getattr__(k): + self.__dict__[k].update(v) #update dict + else: # first + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + OptionTypeError("Not An Accepted Input Type: %s, must be dictionary" % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), ArrayObject): + if self.__getattr__(k): #existing attr + if isinstance(v, dict): + self.__dict__[k].update(v) # update array + elif isinstance(v, list): + for item in v: + self.__dict__[k].update(item) # update array + else: + OptionTypeError("Not An Accepted Input Type: %s, must be list or dictionary" + % type(v)) + else: #first + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + elif isinstance(v, list): + if len(v) == 1: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v[0])}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v[0])}) + for item in v[1:]: + self.__dict__[k].update(item) + else: + OptionTypeError("Not An Accepted Input Type: %s, must be list or dictionary" + % type(v)) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and \ + (isinstance(self.ALLOWED_OPTIONS[k][0](), CSSObject) or isinstance(self.ALLOWED_OPTIONS[k][0](), SVGObject)): + if self.__getattr__(k): + for key, value in v.items(): # check if v has object input + self.__dict__[k].__options__().update({key:value}) + + v = self.__dict__[k].__options__() + # upating object + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + + elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and (isinstance(self.ALLOWED_OPTIONS[k][0](), JSfunction) or \ + isinstance(self.ALLOWED_OPTIONS[k][0](), Formatter) or isinstance(self.ALLOWED_OPTIONS[k][0](), ColorObject)): + if isinstance(v, dict): + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)}) + else: + self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)}) + else: + self.__dict__.update({k:v}) + + else: + print(self.ALLOWED_OPTIONS) + print(k, v) + raise OptionTypeError("Not An Accepted Option Type: %s" % k) + + + def __getattr__(self, item): + if not item in self.__dict__: + return None # Attribute Not Set + else: + return True + + +class ChartOptions(BaseOptions): + ALLOWED_OPTIONS = { + "alignTicks": bool, + "animation": [bool, dict, basestring], + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": int, + "className": basestring, + "defaultSeriesType": basestring, + "events": (Events, dict), + "height": [int,basestring], + "ignoreHiddenSeries": bool, + "inverted": bool, + "margin": list, + "marginBottom": int, + "marginLeft": int, + "marginRight": int, + "marginTop": int, + "options3d": (Options3d, dict), + "plotBackgroundColor": (ColorObject, basestring, dict), + "plotBackgroundImage": basestring, + "plotBorderColor": (ColorObject, basestring, dict), + "plotBorderWidth": int, + "plotShadow": bool, + "polar": bool, + "reflow": bool, + "renderTo": basestring, + "resetZoomButton": (ResetZoomButton, dict), + "selectionMarkerFill": basestring, + "shadow": bool, + "showAxes": bool, + "spacingBottom": int, + "spacingLeft": int, + "spacingRight": int, + "spacingTop": int, + "style": (CSSObject, dict), + "type": basestring, + "width": [int,basestring], + "zoomType": basestring, + } + + +class ColorsOptions(BaseOptions): + """ Special Case, this is simply just an array of colours """ + def __init__(self): + self.colors = {} + + def set_colors(self, colors): + if isinstance(colors, basestring): + self.colors = ColorObject(colors) + elif isinstance(colors, list) or isinstance(colors, dict): + self.colors = colors + else: + OptionTypeError("Not An Accepted Input Type: %s" % type(colors)) + + def __jsonable__(self): + return self.colors + + +class CreditsOptions(BaseOptions): + ALLOWED_OPTIONS = { + "enabled": bool, + "href": basestring, + "position": (Position, dict), + "style": (CSSObject, dict), + "text": basestring, + } + + +class ExportingOptions(BaseOptions): + ALLOWED_OPTIONS = { + "buttons": (ContextButton, dict), + "chartOptions": (ChartOptions, dict), + "enabled": bool, + "filename": basestring, + "formAttributes": NotImplemented, + "scale": int, + "sourceHeight": int, + "sourceWidth": int, + "type": basestring, + "url": basestring, + "width": int, + } + + +class GlobalOptions(BaseOptions): + ALLOWED_OPTIONS = { + "Date": NotImplemented, + "VMLRadialGradientURL": basestring, + "canvasToolsURL": basestring, + "getTimezoneOffset": (JSfunction, basestring), + "timezoneOffset": int, + "useUTC": bool, + } + + +class LabelsOptions(BaseOptions): + ALLOWED_OPTIONS = { + "items": (Items, dict), + "style": (CSSObject, dict), + } + + +class LangOptions(BaseOptions): + ALLOWED_OPTIONS = { + "decimalPoint": basestring, + "downloadJPEG": basestring, + "downloadPDF": basestring, + "downloadPNG": basestring, + "donwloadSVG": basestring, + "exportButtonTitle": basestring, + "loading": basestring, + "months": list, + "noData": basestring, + "numericSymbols": list, + "printButtonTitle": basestring, + "resetZoom": basestring, + "resetZoomTitle": basestring, + "shortMonths": list, + "thousandsSep": basestring, + "weekdays": list, + } + + +class LegendOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": int, + "enabled": bool, + "floating": bool, + "itemDistance": int, + "itemHiddenStyle": (CSSObject, dict), + "itemHoverStyle": (CSSObject, dict), + "itemMarginBottom": int, + "itemMarginTop": int, + "itemStyle": (CSSObject, dict), + "itemWidth": int, + "labelFormat": basestring, + "labelFormatter": (Formatter, JSfunction), + "layout": basestring, + "lineHeight": int, + "margin": int, + "maxHeight": int, + "navigation": (Navigation, dict), + "padding": int, + "reversed": bool, + "rtl": bool, + "shadow": bool, + "style": (CSSObject, dict), + "symbolHeight": int, + "symbolPadding": int, + "symbolRadius": int, + "symbolWidth": int, + "title": (Title, dict), + "useHTML": bool, + "verticalAlign": basestring, + "width": int, + "x": int, + "y": int, + } + + +class LoadingOptions(BaseOptions): + ALLOWED_OPTIONS = { + "hideDuration": int, + "labelStyle": (CSSObject, dict), + "showDuration": int, + "style": (CSSObject, dict), + } + + +class NavigationOptions(BaseOptions): + ALLOWED_OPTIONS = { + "buttonOptions": (ContextButton, dict), + "menuItemHoverStyle": (CSSObject, dict), + "menuItemStyle": (CSSObject, dict), + "menuStyle": (CSSObject, dict), + } + + +class PlotOptions(BaseOptions): + """ Another Special Case: Interface With all the different Highchart Plot Types Here """ + ALLOWED_OPTIONS = { + "area": (SeriesOptions, dict), + "arearange": (SeriesOptions, dict), + "areaspline": (SeriesOptions, dict), + "areasplinerange": (SeriesOptions, dict), + "candlestick": (SeriesOptions, dict), + "column": (SeriesOptions, dict), + "columnrange": (SeriesOptions, dict), + "flags": (SeriesOptions, dict), + "line": (SeriesOptions, dict), + "ohlc": (SeriesOptions, dict), + "polygon": (SeriesOptions, dict), + "scatter": (SeriesOptions, dict), + "series": (SeriesOptions, dict), + "spline": (SeriesOptions, dict), + } + +class RangeSelectorOptions(BaseOptions): + ALLOWED_OPTIONS = { + "allButtonsEnabled": bool, + "buttonSpacing": [int, float], + "buttonTheme": (SVGObject, dict), + "buttons": (Buttons, list), + "enabled": bool, + "inputBoxBorderColor": (ColorObject, basestring, dict), + "inputBoxHeight": [int, float], + "inputBoxWidth": [int, float], + "inputDateFormat": basestring, + "inputDateParser": (JSfunction, basestring), + "inputEditDateFormat": basestring, + "inputEnabled": bool, + "inputPosition": (Position, dict), + "inputStyle": (CSSObject, dict), + "labelStyle": (CSSObject, dict), + "selected": [int, float], + } + +class ScrollbarOptions(BaseOptions): + ALLOWED_OPTIONS = { + "barBackgroundColor": (ColorObject, basestring, dict), + "barBorderColor": (ColorObject, basestring, dict), + "barBorderRadius": [int, float], + "barBorderWidth": [int, float], + "buttonArrowColor": (ColorObject, basestring, dict), + "buttonBackgroundColor": (ColorObject, basestring, dict), + "buttonBorderColor": (ColorObject, basestring, dict), + "buttonBorderRadius": [int, float], + "buttonBorderWidth": [int, float], + "enabled": bool, + "height": [int, float], + "liveRedraw": bool, + "minWidth": [int, float], + "rifleColor": (ColorObject, basestring, dict), + "trackBackgroundColor": (ColorObject, basestring, dict), + "trackBorderColor": (ColorObject, basestring, dict), + "trackBorderRadius": [int, float], + "trackBorderWidth": [int, float], + } + +class SeriesData(BaseOptions): + """ Another Special Case: Stores Data Series in an array for returning to the chart object """ + def __init__(self): + #self.__dict__.update([]) + self = [] + + +class SubtitleOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "floating": bool, + "style": (CSSObject, dict), + "text": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + } + + +class TitleOptions(BaseOptions): + ALLOWED_OPTIONS = { + "align": basestring, + "floating": bool, + "margin": int, + "style": (CSSObject, dict), + "text": basestring, + "useHTML": bool, + "verticalAlign": basestring, + "x": int, + "y": int, + } + + +class TooltipOptions(BaseOptions): + ALLOWED_OPTIONS = { + "animation": bool, + "backgroundColor": (ColorObject, basestring, dict), + "borderColor": (ColorObject, basestring, dict), + "borderRadius": int, + "borderWidth": int, + "crosshairs": [bool, list, dict], + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "enabled": bool, + "followPointer": bool, + "followTouchMove": bool, + "footerFormat": basestring, + "formatter": (Formatter, JSfunction), + "headerFormat": basestring, + "pointFormat": basestring, + "positioner": (JSfunction, basestring), + "shadow": bool, + "shared": bool, + "snap": int, + "style": (CSSObject, dict), + "useHTML": bool, + "valueDecimals": int, + "valuePrefix": basestring, + "valueSuffix": basestring, + "xDateFormat": basestring, + } + + +class xAxisOptions(BaseOptions): + ALLOWED_OPTIONS = { + "allowDecimals": bool, + "alternateGridColor": (ColorObject, basestring, dict), + "breaks":(Breaks, list), + "categories": list, + 'crosshair': bool, + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "endOnTick": bool, + "events": (Events, dict), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineWidth": int, + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": int, + "max": [float, int], + "maxPadding": [float, int], + "maxZoom": NotImplemented, + "min": [float, int], + "minPadding": [float, int], + "minRange": int, + "minTickInterval": int, + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "offset": bool, + "opposite": bool, + "ordinal": bool, + "plotBands": (PlotBands, list), + "plotLines": (PlotLines, list), + "reversed": bool, + "showEmpty": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "startOfWeek": int, + "startOnTick": bool, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": (JSfunction, basestring), + "tickPositions": list, + "tickWidth": int, + "tickmarkPlacement": basestring, + "title": (Title, dict), + "type": basestring, + "units": list + } + + +class yAxisOptions(BaseOptions): + ALLOWED_OPTIONS = { + "allowDecimals": bool, + "alternateGridColor": (ColorObject, basestring, dict), + "breaks": (Breaks, list), + "categories": list, + "ceiling": (int, float), + "dateTimeLabelFormats": (DateTimeLabelFormats, dict), + "endOnTick": bool, + "events": (Events, dict), + "floor": (int, float), + "gridLineColor": (ColorObject, basestring, dict), + "gridLineDashStyle": basestring, + "gridLineInterpolation": basestring, + "gridLineWidth": int, + "gridZIndex": int, + "height": [int, float, basestring], + "id": basestring, + "labels": (Labels, dict), + "lineColor": (ColorObject, basestring, dict), + "lineWidth": int, + "linkedTo": int, + "max": [float, int], + "maxColor": (ColorObject, basestring, dict), + "maxPadding": [float, int], + "maxZoom": NotImplemented, + "min": [float, int], + "minColor": (ColorObject, basestring, dict), + "minPadding": [float, int], + "minRange": int, + "minTickInterval": int, + "minorGridLineColor": (ColorObject, basestring, dict), + "minorGridLineDashStyle": basestring, + "minorGridLineWidth": int, + "minorTickColor": (ColorObject, basestring, dict), + "minorTickInterval": int, + "minorTickLength": int, + "minorTickPosition": basestring, + "minorTickWidth": int, + "offset": bool, + "opposite": bool, + "ordinal": bool, + "plotBands": (PlotBands, list), + "plotLines": (PlotLines, list), + "reversed": bool, + "reversedStacks": bool, + "showEmpty": bool, + "showFirstLabel": bool, + "showLastLabel": bool, + "stackLabels": (Labels, dict), + "startOfWeek": int, + "startOnTick": bool, + "stops": list, + "tickAmount": int, + "tickColor": (ColorObject, basestring, dict), + "tickInterval": int, + "tickLength": int, + "tickPixelInterval": int, + "tickPosition": basestring, + "tickPositioner": (JSfunction, basestring), + "tickPositions": list, + "tickWidth": int, + "tickmarkPlacement": basestring, + "title": (Title, dict), + "top": [int, float, basestring], + # DEM 2017/11/16: Note that the 'type' keyword for highstock is + # undocumented yet appears to be supported, likely because of underlying + # shared code. This permits logarithmic Y-Axis scale which is + # frequently useful in stock charts. + "type": basestring, + "units": list + } + + +class NavigatorOptions(BaseOptions): + ALLOWED_OPTIONS = { + "adaptToUpdatedData": bool, + "baseSeries": [int, basestring], + "enabled": bool, + "handles": (Handles, dict), # need handles object + "height": [int, float], + "margin": [int, float], + "maskFill": (ColorObject, dict), + "maskInside": bool, + "outlineColor": (ColorObject, dict), + "outlineWidth": [int, float], + "series": dict, + "xAxis": (xAxisOptions, dict), + "yAxis": (yAxisOptions, dict), + } + + +class MultiAxis(object): + + def __init__(self, axis): + AXIS_LIST = { + "xAxis": xAxisOptions, + "yAxis": yAxisOptions + } + self.axis = [] + self.AxisObj = AXIS_LIST[axis] + + def update(self, **kwargs): + self.axis.append(self.AxisObj(**kwargs)) + + def __jsonable__(self): + return self.axis + diff --git a/build/lib/highcharts/highstock/templates/base.html b/build/lib/highcharts/highstock/templates/base.html new file mode 100644 index 0000000..401d656 --- /dev/null +++ b/build/lib/highcharts/highstock/templates/base.html @@ -0,0 +1,21 @@ +{% block container %} +{% endblock %} + +{% block start_script %} + +{% endblock endscript %} \ No newline at end of file diff --git a/build/lib/highcharts/highstock/templates/content.html b/build/lib/highcharts/highstock/templates/content.html new file mode 100644 index 0000000..54615ba --- /dev/null +++ b/build/lib/highcharts/highstock/templates/content.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} +{% block container %} + {{ chart.container }} +{% endblock container %} + +{% block body_head %} + + {% if chart.jscript_head_flag %} + {{chart.jscript_head}} + {% endif %} + +{% endblock body_head %} + +{% block body_content %} + + Highcharts.setOptions({{chart.setoption}}); + var option = {{chart.option}}; + + {% if chart.navi_seri_flag %} + var navi_data = {{chart.navi_seri}} + option.navigator.series = navi_data; + {% endif %} + + var chart = new Highcharts.StockChart(option); + + + {# DEM 2017/04/25: Use a list of JSONP data sources + {# DEM 2017/07/27: This implementation is limited and could easily be improved! #} + {% if chart.jsonp_data_flag %} + {% for data_url in chart.jsonp_data_url_list %} + + $.getJSON({{data_url}}, function ({{chart.jsonp_data}}) + { + chart.addSeries({{chart.data_list[loop.index0]}}); + }); + + {% endfor %} + {% else %} + + var data = {{chart.data}}; + var dataLen = data.length; + for (var ix = 0; ix < dataLen; ix++) { + chart.addSeries(data[ix]); + } + + {% endif %} + +{% endblock body_content %} + +{% block body_end %} + + {% if chart.jscript_end_flag %} + {{chart.jscript_end}} + {% endif %} + +{% endblock body_end %} diff --git a/build/lib/highcharts/highstock/templates/page.html b/build/lib/highcharts/highstock/templates/page.html new file mode 100644 index 0000000..dd722fe --- /dev/null +++ b/build/lib/highcharts/highstock/templates/page.html @@ -0,0 +1,13 @@ + + + + + {% for header_element in chart.header_css+chart.header_js %} + {{ header_element }} + {% endfor %} + + + {{ chart.content }} + {{ chart.other }} + + diff --git a/build/lib/highcharts/highstock/templates/test.html b/build/lib/highcharts/highstock/templates/test.html new file mode 100644 index 0000000..63eecc5 --- /dev/null +++ b/build/lib/highcharts/highstock/templates/test.html @@ -0,0 +1,17 @@ + + + + + + {{ chart.title }} + + + + + +
+

Why, hello there!

+
+ + + \ No newline at end of file diff --git a/build/lib/highcharts/ipynb.py b/build/lib/highcharts/ipynb.py new file mode 100644 index 0000000..20b4e3a --- /dev/null +++ b/build/lib/highcharts/ipynb.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +''' +IPython notebook compatability module for highcharts-python + +Adapted from python-nvd3: https://github.com/areski/python-nvd3/blob/develop/nvd3/ipynb.py +''' + +try: + _ip = get_ipython() +except: + _ip = None +if _ip and (_ip.__module__.startswith('IPython') or _ip.__module__.startswith('ipykernel')): + + def _print_html(chart): + '''Function to return the HTML code for the div container plus the javascript + to generate the chart. This function is bound to the ipython formatter so that + charts are displayed inline.''' + + return chart.iframe + + def _setup_ipython_formatter(ip): + ''' Set up the ipython formatter to display HTML formatted output inline''' + from IPython import __version__ as IPython_version + from .highcharts.highcharts import Highchart + from .highmaps.highmaps import Highmap + from .highstock.highstock import Highstock + + if IPython_version >= '0.11': + html_formatter = ip.display_formatter.formatters['text/html'] + + for chart_type in [Highchart, Highmap, Highstock]: + html_formatter.for_type(chart_type, _print_html) + + _setup_ipython_formatter(_ip) diff --git a/build/lib/highcharts/version.py b/build/lib/highcharts/version.py new file mode 100644 index 0000000..1531aeb --- /dev/null +++ b/build/lib/highcharts/version.py @@ -0,0 +1,7 @@ +version_info = ( + "0", + "4", + "3" +) + +__version__ = '.'.join(map(str, version_info)) diff --git a/highcharts/highcharts/highcharts.py b/highcharts/highcharts/highcharts.py index 59e093e..6262c42 100644 --- a/highcharts/highcharts/highcharts.py +++ b/highcharts/highcharts/highcharts.py @@ -12,7 +12,12 @@ import datetime import urllib.request, urllib.error, urllib.parse import html -from collections import Iterable +try: + # works with older (3.9 and below) versions, throws an exception with newer (3.10 and above) versions + from collections import Iterable +#if an import error change to trying 3.10 and above notation +except ImportError: + from collections.abc import Iterable from .options import BaseOptions, ChartOptions, ColorAxisOptions, \ ColorsOptions, CreditsOptions, DrilldownOptions, ExportingOptions, \ GlobalOptions, LabelsOptions, LangOptions, \ diff --git a/highcharts/highmaps/highmaps.py b/highcharts/highmaps/highmaps.py index 0d73afc..f2f7b91 100644 --- a/highcharts/highmaps/highmaps.py +++ b/highcharts/highmaps/highmaps.py @@ -13,7 +13,12 @@ import re import datetime import html -from collections import Iterable +try: + # works with older (3.9 and below) versions, throws an exception with newer (3.10 and above) versions + from collections import Iterable +#if an import error change to trying 3.10 and above notation +except ImportError: + from collections.abc import Iterable from .options import BaseOptions, ChartOptions, \ ColorsOptions, ColorAxisOptions, CreditsOptions, DrilldownOptions, ExportingOptions, \ GlobalOptions, LabelsOptions, LangOptions, \ diff --git a/highcharts/highstock/highstock.py b/highcharts/highstock/highstock.py index ef8610d..9b38c2f 100644 --- a/highcharts/highstock/highstock.py +++ b/highcharts/highstock/highstock.py @@ -10,7 +10,12 @@ import re import datetime import html -from collections import Iterable +try: + # works with older (3.9 and below) versions, throws an exception with newer (3.10 and above) versions + from collections import Iterable +#if an import error change to trying 3.10 and above notation +except ImportError: + from collections.abc import Iterable from .options import BaseOptions, ChartOptions, \ ColorsOptions, CreditsOptions, ExportingOptions, \ GlobalOptions, LabelsOptions, LangOptions, \ diff --git a/highcharts/version.py b/highcharts/version.py index c8472d3..1531aeb 100644 --- a/highcharts/version.py +++ b/highcharts/version.py @@ -1,7 +1,7 @@ version_info = ( "0", "4", - "2" + "3" ) __version__ = '.'.join(map(str, version_info))