diff --git a/.gitignore b/.gitignore index 691224a..e02f70a 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ cover/ venv/ .cache/ *.swp +*.rdb +coverage.xml +htmlcov/ diff --git a/.travis.yml b/.travis.yml index 10d8208..8bb754a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ python: - 'pypy' install: pip install -U nose . script: - - nosetests --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 25d3b93..d3775bc 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 ------------ @@ -46,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" } ], @@ -58,18 +68,31 @@ 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 :: + + chart.plot() + +and invoke a browser to display the chart. + Contribution ------------ diff --git a/echarts/__init__.py b/echarts/__init__.py index 08900b5..4d1683b 100644 --- a/echarts/__init__.py +++ b/echarts/__init__.py @@ -16,24 +16,28 @@ 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' -__release__ = '0.1.1' +__release__ = '0.1.3' __author__ = 'Hsiaoming Yang ' 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.kwargs = kwargs self.logger = logging.getLogger(__name__) @@ -53,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 @@ -65,28 +71,49 @@ def json(self): """JSON format data.""" 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 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'] = list(map(dict, self.x_axis)) or [{}] + json['yAxis'] = list(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 + if hasattr(self, 'visualMap'): + json['visualMap'] = self.visualMap.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') + + 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()) 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 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() 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', 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))