From a953f3ba48bdf0468351e1407a85e0d24766d1f0 Mon Sep 17 00:00:00 2001 From: Yufei Li Date: Fri, 27 May 2016 11:19:38 +0800 Subject: [PATCH 1/9] Update CI, documentation and deployment. 1. Update document reference. 2. Deploy to pypi by hand. 3. Correct travis for codecov. --- .travis.yml | 2 +- README.rst | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 10d8208..8f26fe4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ python: - 'pypy' install: pip install -U nose . script: - - nosetests --verbose + - nosetests --cover-html --verbose after_success: - pip install codecov - codecov diff --git a/README.rst b/README.rst index 25d3b93..6485303 100644 --- a/README.rst +++ b/README.rst @@ -3,8 +3,8 @@ Echarts for Python An unofficial Echarts options generator with Python. -.. image:: https://img.shields.io/pypi/v/Echarts.svg - :target: https://pypi.python.org/pypi/Echarts/ +.. image:: https://img.shields.io/pypi/v/echarts-python.svg + :target: https://pypi.python.org/pypi/echarts-python/ :alt: Latest Version .. image:: https://travis-ci.org/yufeiminds/echarts-python.svg?branch=develop :target: https://travis-ci.org/yufeiminds/echarts-python @@ -17,8 +17,10 @@ An unofficial Echarts options generator with Python. :alt: Doc Status - Free software: MIT license -- Documentation: https://echarts-python.readthedocs.com/en/ -- Online demo: https://yufeiminds.github.io/echarts-python/ +- Documentation: http://echarts-python.readthedocs.io/en/stable/ (NOT Ready) +- Online demo: https://yufeiminds.github.io/echarts-python/ (NOT Ready) + +This repo still on developing (ALPHA), DON'T USE IT IN PRODUCTION. Installation ------------ @@ -70,6 +72,12 @@ The `chart.json` property will be "yAxis": [] } +on Mac OSX, you also can execute :: + + chart.plot() + +and invoke a browser to display the chart. + Contribution ------------ From 78fe133bcbbee2aa12ff2a69d9ca37f9c395bdb2 Mon Sep 17 00:00:00 2001 From: Yufei Li Date: Sat, 4 Jun 2016 00:15:34 +0800 Subject: [PATCH 2/9] Repackaging for plot template. 1. Add cover report to travis. 2. Fix `template not found`. 3. Update document usage. --- .gitignore | 1 + .travis.yml | 2 +- MANIFEST.in | 2 ++ README.rst | 33 ++++++++++++++++++++++++--------- echarts/__init__.py | 2 +- setup.py | 2 +- 6 files changed, 30 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 691224a..d3d852f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ cover/ venv/ .cache/ *.swp +*.rdb diff --git a/.travis.yml b/.travis.yml index 8f26fe4..8bb754a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ python: - 'pypy' install: pip install -U nose . script: - - nosetests --cover-html --verbose + - nosetests --with-coverage --cover-package=echarts --verbose after_success: - pip install codecov - codecov diff --git a/MANIFEST.in b/MANIFEST.in index a03dd87..c7f305f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,3 +6,5 @@ recursive-exclude *.egg-info * recursive-exclude * *.py[co] recursive-include docs *.rst +recursive-include echarts *.j2 + diff --git a/README.rst b/README.rst index 6485303..d3775bc 100644 --- a/README.rst +++ b/README.rst @@ -48,11 +48,19 @@ The `chart.json` property will be .. code-block:: javascript { + "title": { + "text": "GDP", + "subtext": "This is a fake chart" + }, "series": [ { "type": "bar", - "zlevel": 0, - "data": [2, 3, 4, 5], + "data": [ + 2, + 3, + 4, + 5 + ], "name": "China" } ], @@ -60,16 +68,23 @@ The `chart.json` property will be "y": "top", "x": "center", "data": [ - "China" + "GDP" ], "orient": "horizontal" }, - "title": { - "text": "GDP", - "subtext": "This is a fake chart" - }, - "xAxis": [], - "yAxis": [] + "xAxis": [ + { + "position": "bottom", + "data": [ + "Nov", + "Dec", + "Jan", + "Feb" + ], + "type": "category" + } + ], + "yAxis": {} } on Mac OSX, you also can execute :: diff --git a/echarts/__init__.py b/echarts/__init__.py index 08900b5..92b8547 100644 --- a/echarts/__init__.py +++ b/echarts/__init__.py @@ -20,7 +20,7 @@ from .datastructure import * __version__ = '0.1' -__release__ = '0.1.1' +__release__ = '0.1.2' __author__ = 'Hsiaoming Yang ' diff --git a/setup.py b/setup.py index 66bde96..5e54aa1 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def fread(filename): setup( name='echarts-python', - version=echarts.__version__, + version=echarts.__release__, author=author, author_email=author_email, url='https://github.com/yufeiminds/echarts-python', From 1a59c55379c9e676d29c4745f79f55cb11dc835c Mon Sep 17 00:00:00 2001 From: bonfy Date: Wed, 21 Dec 2016 20:57:57 +0800 Subject: [PATCH 3/9] add axis flag to json function --- echarts/__init__.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/echarts/__init__.py b/echarts/__init__.py index 92b8547..5951193 100644 --- a/echarts/__init__.py +++ b/echarts/__init__.py @@ -61,15 +61,21 @@ def data(self): return self.series @property - def json(self): - """JSON format data.""" + def json(self, axis=True): + """JSON format data. + + Keyword Arguments: + axis -- type boolean, axis need or not (defautl: 'True') + """ json = { 'title': self.title, - 'xAxis': list(map(dict, self.x_axis)) or {}, - 'yAxis': list(map(dict, self.y_axis)) or {}, 'series': list(map(dict, self.series)), } + if axis: + json['xAxis'] = list(map(dict, self.x_axis)) or {} + json['yAxis'] = list(map(dict, self.y_axis)) or {} + if not hasattr(self, 'legend'): self.legend = Legend(list(map(lambda o: o.name, self.data))) From e8ebc255eefbc28cb22541244c21341bb10b9be4 Mon Sep 17 00:00:00 2001 From: bonfy Date: Wed, 21 Dec 2016 21:57:40 +0800 Subject: [PATCH 4/9] change axis flag to __init__ func --- echarts/__init__.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/echarts/__init__.py b/echarts/__init__.py index 5951193..093e79f 100644 --- a/echarts/__init__.py +++ b/echarts/__init__.py @@ -25,14 +25,17 @@ class Echart(Base): - def __init__(self, title, description=None, **kwargs): + def __init__(self, title, description=None, axis=True, **kwargs): self.title = { 'text': title, 'subtext': description, } - self.x_axis = [] - self.y_axis = [] + self.axis = axis + if self.axis: + self.x_axis = [] + self.y_axis = [] + self.series = [] self.logger = logging.getLogger(__name__) @@ -61,21 +64,16 @@ def data(self): return self.series @property - def json(self, axis=True): - """JSON format data. - - Keyword Arguments: - axis -- type boolean, axis need or not (defautl: 'True') - """ + def json(self): + """JSON format data.""" json = { 'title': self.title, 'series': list(map(dict, self.series)), } - if axis: + if self.axis: json['xAxis'] = list(map(dict, self.x_axis)) or {} json['yAxis'] = list(map(dict, self.y_axis)) or {} - if not hasattr(self, 'legend'): self.legend = Legend(list(map(lambda o: o.name, self.data))) From 361ecffed3c5ddabf7f294a1f4b2e4e2eb0267d0 Mon Sep 17 00:00:00 2001 From: Yufei Li Date: Fri, 23 Dec 2016 00:24:12 +0800 Subject: [PATCH 5/9] Add sample doc and fix axis. --- .gitignore | 2 ++ echarts/__init__.py | 34 ++++++++++++++++++------- samples/README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++ samples/index.py | 1 + 4 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 samples/README.md diff --git a/.gitignore b/.gitignore index d3d852f..e02f70a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ venv/ .cache/ *.swp *.rdb +coverage.xml +htmlcov/ diff --git a/echarts/__init__.py b/echarts/__init__.py index 093e79f..879ba8d 100644 --- a/echarts/__init__.py +++ b/echarts/__init__.py @@ -20,7 +20,7 @@ from .datastructure import * __version__ = '0.1' -__release__ = '0.1.2' +__release__ = '0.1.3' __author__ = 'Hsiaoming Yang ' @@ -37,6 +37,7 @@ def __init__(self, title, description=None, axis=True, **kwargs): self.y_axis = [] self.series = [] + self.kwargs = kwargs self.logger = logging.getLogger(__name__) @@ -72,25 +73,40 @@ def json(self): } if self.axis: - json['xAxis'] = list(map(dict, self.x_axis)) or {} - json['yAxis'] = list(map(dict, self.y_axis)) or {} + json['xAxis'] = list(map(dict, self.x_axis)) or [{}] + json['yAxis'] = list(map(dict, self.y_axis)) or [{}] + if not hasattr(self, 'legend'): self.legend = Legend(list(map(lambda o: o.name, self.data))) json['legend'] = self.legend.json + if self.axis: + json['xAxis'] = map(dict, self.x_axis) or [{}] + json['yAxis'] = map(dict, self.y_axis) or [{}] + if hasattr(self, 'legend'): + json['legend'] = self.legend.json if hasattr(self, 'tooltip'): json['tooltip'] = self.tooltip.json if hasattr(self, 'toolbox'): json['toolbox'] = self.toolbox.json + json.update(self.kwargs) return json - def plot(self): - html = tempfile.NamedTemporaryFile(suffix='.html', delete=False) + def _html(self): with open(os.path.join(os.path.dirname(__file__), 'plot.j2')) as f: template = f.read() - content = template.replace('{{ opt }}', json.dumps(self.json, indent=4)) - html.write(content) - webbrowser.open('file://' + os.path.realpath(html.name)) - html.close() + return template.replace('{{ opt }}', json.dumps(self.json, indent=4)) + + def plot(self, persist=True): + """ + Plot into html file + + :param persist: persist output html to disk + """ + with tempfile.NamedTemporaryFile(suffix='.html', delete=not persist) as fobj: + fobj.write(self._html()) + fobj.flush() + webbrowser.open('file://' + os.path.realpath(fobj.name)) + persist or raw_input('Press enter for continue') diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 0000000..5db050a --- /dev/null +++ b/samples/README.md @@ -0,0 +1,62 @@ +# Echarts Python Sample + +A simple flask server && A simple html with webpack + +## Code + +### Python + +```python +@app.route('/opt/bar') +def bar(): + chart = Echart('GDP', 'This is a fake chart') + chart.use(Bar('China', [2, 3, 4, 5])) + chart.use(Legend(['GDP'])) + chart.use(Axis('category', 'bottom', data=['Nov', 'Dec', 'Jan', 'Feb'])) + return jsonify(chart.json) +``` + +### ES5 + +```javascript +// XHR callback +function callback () { + var chart = echarts.init(document.getElementById('main')); + chart.setOption(response.data) +} +``` + +### ES6 + +```javascript +fetch('http://127.0.0.1:5000/opt/bar').then(resp => { + var chart = echarts.init(document.getElementById('main')); + chart.setOption(response.data) +}) +``` + +## Run Example + +### Server + +``` +pip install -r requirements.txt + +python -m index +``` + +### Frontend + +``` +sudo npm install + +webpack +``` + +### Open Page + +``` +python -m SimpleHTTPServer + +open http://localhost:8000/ +``` diff --git a/samples/index.py b/samples/index.py index 4d0aebb..8011761 100644 --- a/samples/index.py +++ b/samples/index.py @@ -41,5 +41,6 @@ def bar(): if __name__ == '__main__': app = create_app() + print('Serve on http://localhost:5000') http_server = WSGIServer(('', 5000), app) http_server.serve_forever() From 0cc20c43e9d4fe0bab8542664e61c75994ea41e0 Mon Sep 17 00:00:00 2001 From: Yufei Li Date: Fri, 23 Dec 2016 00:29:08 +0800 Subject: [PATCH 6/9] amend test fix. --- tests/test_echart.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_echart.py b/tests/test_echart.py index cd60b7a..6d452a0 100644 --- a/tests/test_echart.py +++ b/tests/test_echart.py @@ -9,7 +9,8 @@ def test_axis(): chart = Echart('Axis', 'Proportion of Browser') - assert not chart.json['xAxis'] and not chart.json['yAxis'] + assert len(chart.json['xAxis'][0]) == 0 + assert len(chart.json['yAxis'][0]) == 0 chart.use(Axis('category', 'bottom', 'proportion', inverse=True)) assert chart.json['xAxis'] chart.use(Axis('category', 'left', 'proportion', inverse=True)) From 0ca1a1ee59a62617acc3ee0dbc2e4a0cc573553e Mon Sep 17 00:00:00 2001 From: Yufei Li Date: Fri, 23 Dec 2016 00:37:57 +0800 Subject: [PATCH 7/9] Resolve merge conflict. --- echarts/__init__.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/echarts/__init__.py b/echarts/__init__.py index 879ba8d..8fb216a 100644 --- a/echarts/__init__.py +++ b/echarts/__init__.py @@ -76,14 +76,6 @@ def json(self): json['xAxis'] = list(map(dict, self.x_axis)) or [{}] json['yAxis'] = list(map(dict, self.y_axis)) or [{}] - if not hasattr(self, 'legend'): - self.legend = Legend(list(map(lambda o: o.name, self.data))) - - json['legend'] = self.legend.json - if self.axis: - json['xAxis'] = map(dict, self.x_axis) or [{}] - json['yAxis'] = map(dict, self.y_axis) or [{}] - if hasattr(self, 'legend'): json['legend'] = self.legend.json if hasattr(self, 'tooltip'): From 20556e3d659e3860b44d90c5b25087ff72a17fa3 Mon Sep 17 00:00:00 2001 From: mars <861931389@qq.com> Date: Sun, 25 Dec 2016 11:22:46 +0800 Subject: [PATCH 8/9] Update option.py --- echarts/option.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/echarts/option.py b/echarts/option.py index 5796572..eb96e80 100644 --- a/echarts/option.py +++ b/echarts/option.py @@ -156,3 +156,26 @@ def json(self): if self._kwargs: json.update(self._kwargs) return json + + +class VisualMap(Base): + """maps data to visual channels""" + + def __init__(self, type, min, max, **kwargs): + assert type in ("continuous", "piecewise") + self.type = type + self.min = min + self.max = max + self._kwargs = kwargs + + @property + def json(self): + """JSON format data""" + json = { + "type": self.type, + 'min': self.min, + 'max': self.max + } + if self._kwargs: + json.update(self._kwargs) + return json From a8d22dc0d2489efcd7dc8599f3fc5db2ff394a97 Mon Sep 17 00:00:00 2001 From: mars <861931389@qq.com> Date: Sun, 25 Dec 2016 11:32:32 +0800 Subject: [PATCH 9/9] Update __init__.py --- echarts/__init__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/echarts/__init__.py b/echarts/__init__.py index 8fb216a..4d1683b 100644 --- a/echarts/__init__.py +++ b/echarts/__init__.py @@ -16,7 +16,7 @@ import tempfile import webbrowser from .option import Base -from .option import Axis, Legend, Series, Tooltip, Toolbox +from .option import Axis, Legend, Series, Tooltip, Toolbox, VisualMap from .datastructure import * __version__ = '0.1' @@ -57,6 +57,8 @@ def use(self, obj): self.series.append(obj) elif isinstance(obj, Toolbox): self.toolbox = obj + elif isinstance(obj, VisualMap): + self.visualMap = obj return self @@ -82,6 +84,8 @@ def json(self): json['tooltip'] = self.tooltip.json if hasattr(self, 'toolbox'): json['toolbox'] = self.toolbox.json + if hasattr(self, 'visualMap'): + json['visualMap'] = self.visualMap.json json.update(self.kwargs) return json @@ -102,3 +106,14 @@ def plot(self, persist=True): fobj.flush() webbrowser.open('file://' + os.path.realpath(fobj.name)) persist or raw_input('Press enter for continue') + + def save(self, path, name): + """ + Save html file into project dir + :param path: project dir + :param name: html file name + """ + if not os.path.exists(path): + os.makedirs(path) + with open(path+str(name)+".html", "w") as html_file: + html_file.write(self._html())