├── easycharts
├── __init__.py
├── exceptions.py
├── frontend.py
└── charts.py
├── requirements.txt
├── images
├── logo.png
├── resource-mon.png
├── get-started-apis.png
├── get-started-test.png
├── get-started-test-1.png
├── get-started-update.png
└── get-started-test-1-bar.png
├── nextbuild.py
├── setup.py
├── LICENSE
├── .github
└── workflows
│ └── package.yaml
├── .gitignore
└── README.md
/easycharts/__init__.py:
--------------------------------------------------------------------------------
1 | from .charts import ChartServer
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | easyrpc>=0.241
2 | aiopyql>=0.359
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codemation/easycharts/HEAD/images/logo.png
--------------------------------------------------------------------------------
/images/resource-mon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codemation/easycharts/HEAD/images/resource-mon.png
--------------------------------------------------------------------------------
/images/get-started-apis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codemation/easycharts/HEAD/images/get-started-apis.png
--------------------------------------------------------------------------------
/images/get-started-test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codemation/easycharts/HEAD/images/get-started-test.png
--------------------------------------------------------------------------------
/images/get-started-test-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codemation/easycharts/HEAD/images/get-started-test-1.png
--------------------------------------------------------------------------------
/images/get-started-update.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codemation/easycharts/HEAD/images/get-started-update.png
--------------------------------------------------------------------------------
/images/get-started-test-1-bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codemation/easycharts/HEAD/images/get-started-test-1-bar.png
--------------------------------------------------------------------------------
/easycharts/exceptions.py:
--------------------------------------------------------------------------------
1 | from fastapi import HTTPException
2 |
3 | class DuplicateDatasetError(HTTPException):
4 | def __init__(self, dataset: str):
5 | super().__init__(
6 | status_code = 422,
7 | detail = f"A dataset with name {dataset} already exists"
8 | )
9 | class MissingDatasetError(HTTPException):
10 | def __init__(self, dataset: str):
11 | super().__init__(
12 | status_code = 404,
13 | detail = f"No dataset with name {dataset} exists"
14 | )
--------------------------------------------------------------------------------
/nextbuild.py:
--------------------------------------------------------------------------------
1 | """
2 | Purpose:
3 | Increments current Pypi version by .001
4 |
5 | Usage:
6 | pip3 download easycharts && ls easycharts*.whl | sed 's/-/" "/g' | awk '{print "(" $2 ")"}' | python3 python/easyauth/easyauth/nextbuild.py
7 | """
8 | if __name__=='__main__':
9 | import sys
10 | version = sys.stdin.readline().rstrip()
11 | if '(' in version and ')' in version:
12 | right_i = version.index('(')
13 | left_i = version.index(')')
14 | version = version[right_i+2:left_i-1]
15 | print(f"{float(version)+0.001:.3f}")
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | BASE_REQUIREMENTS = [
4 | 'easyrpc>=0.241',
5 | 'aiopyql>=0.359'
6 | ]
7 |
8 | with open("README.md", "r") as fh:
9 | long_description = fh.read()
10 | setuptools.setup(
11 | name='easycharts',
12 | version='NEXTVERSION',
13 | packages=setuptools.find_packages(include=['easycharts'], exclude=['build']),
14 | author="Joshua Jamison",
15 | author_email="joshjamison1@gmail.com",
16 | description="Easily create data visualization of static or streaming data",
17 | long_description=long_description,
18 | long_description_content_type="text/markdown",
19 | url="https://github.com/codemation/easycharts",
20 | classifiers=[
21 | "Programming Language :: Python :: 3",
22 | "License :: OSI Approved :: MIT License",
23 | "Operating System :: OS Independent",
24 | ],
25 | python_requires='>=3.7, <4',
26 | install_requires=BASE_REQUIREMENTS,
27 | extras_require={}
28 | )
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Joshua Jamison
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.github/workflows/package.yaml:
--------------------------------------------------------------------------------
1 | name: Package and Push to PyPI
2 | on:
3 | push:
4 | tags:
5 | - "*"
6 | jobs:
7 | package:
8 | name: Package easycharts for PyPI
9 | runs-on: ubuntu-latest
10 | steps:
11 | # Downloads a copy of the code in your repository before running CI tests
12 | - name: Check out repository code
13 | uses: actions/checkout@v2
14 | - name: Setup Python
15 | uses: actions/setup-python@v2
16 | with:
17 | python-version: 3.8
18 | - name: Install Packaging dependencies
19 | run: |
20 | pip install wheel twine
21 |
22 | - name: Package & Test PyPI Installation
23 | run: |
24 | export NEXTVERSION=$(pip -qqq download easycharts && ls easycharts*.whl | sed 's/-/" "/g' | awk '{print "(" $2 ")"}' | python nextbuild.py)
25 | sed -i 's/NEXTVERSION/'$NEXTVERSION'/g' setup.py
26 | python setup.py bdist_wheel
27 | export PYQL_PACKAGE=$(pwd)/dist/easycharts-$NEXTVERSION-py3-none-any.whl
28 | pip install $(echo -n $PYQL_PACKAGE)
29 |
30 | - name: Upload to PyPi
31 | env: # Or as an environment variable
32 | PYPI: ${{ secrets.PYPI }}
33 | run: |
34 | export NEXTVERSION=$(pip -qqq download easycharts && ls easycharts*.whl | sed 's/-/" "/g' | awk '{print "(" $2 ")"}' | python nextbuild.py)
35 | export PYQL_PACKAGE=$(pwd)/dist/easycharts-$NEXTVERSION-py3-none-any.whl
36 | python -m twine upload $(pwd)/dist/easycharts-$NEXTVERSION-py3-none-any.whl -u codemation -p $PYPI
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ##
4 |
5 | Easily create data visualization of static or streaming data
6 |
7 |
8 | ## Get Started
9 |
10 | ```python
11 | pip install easycharts
12 | ```
13 |
14 | ## Create EasyCharts Server
15 |
16 | ```python
17 | # charts.py
18 | from fastapi import FastAPI
19 | from easycharts import ChartServer
20 |
21 | server = FastAPI()
22 |
23 | @server.on_event('startup')
24 | async def setup():
25 | server.charts = await ChartServer.create(
26 | server,
27 | charts_db="test"
28 | )
29 |
30 | await server.charts.create_dataset(
31 | "test",
32 | labels=['a', 'b', 'c', 'd'],
33 | dataset=[1,2,3,4]
34 | )
35 | ```
36 | ## Start Server
37 |
38 | ```bash
39 | uvicorn --host 0.0.0.0 --port 0.0.0.0 charts:server
40 |
41 | ```
42 |
43 | 
44 |
45 | ## Update Data via API
46 |
47 | In a separate window, access the OpenAPI docs to demonstrate dynanimc updates to the graph
48 |
49 | ```
50 | http://0.0.0.0:8220/docs
51 | ```
52 |
53 | 
54 |
55 | ## Line
56 | 
57 |
58 |
59 | ## Bar
60 | 
61 |
62 | ## APIS
63 |
64 | 
65 |
66 | ## Real World Usage - Resource Monitoring
67 |
68 |
69 | ```python
70 | import datetime, psutil
71 | import asyncio
72 | from fastapi import FastAPI
73 | from easycharts import ChartServer
74 | from easyschedule import EasyScheduler
75 |
76 | scheduler = EasyScheduler()
77 | server = FastAPI()
78 |
79 | every_minute = '* * * * *'
80 |
81 | @server.on_event('startup')
82 | async def setup():
83 | asyncio.create_task(scheduler.start())
84 | server.charts = await ChartServer.create(
85 | server,
86 | charts_db="charts_database",
87 | chart_prefix = '/mycharts'
88 | )
89 |
90 | await server.charts.create_dataset(
91 | "test",
92 | labels=['a', 'b', 'c', 'd'],
93 | dataset=[1,2,3,4]
94 | )
95 |
96 | # set initial sync time
97 | label=datetime.datetime.now().isoformat()[11:19]
98 | await server.charts.create_dataset(
99 | 'cpu',
100 | labels=[label],
101 | dataset=[psutil.cpu_percent()]
102 | )
103 | await server.charts.create_dataset(
104 | 'mem',
105 | labels=[label],
106 | dataset=[psutil.virtual_memory().percent]
107 | )
108 |
109 | @scheduler(schedule=every_minute)
110 | async def resource_monitor():
111 | time_now=datetime.datetime.now().isoformat()[11:19]
112 |
113 | # updates CPU & MEM datasets with current time
114 | await server.charts.update_dataset(
115 | 'cpu',
116 | label=time_now,
117 | data=psutil.cpu_percent()
118 | )
119 | await server.charts.update_dataset(
120 | 'mem',
121 | label=time_now,
122 | data=psutil.virtual_memory().percent
123 | )
124 | ```
125 |
126 | 
--------------------------------------------------------------------------------
/easycharts/frontend.py:
--------------------------------------------------------------------------------
1 | def get_chart_body(names: list, creds: str, chart_type: str):
2 | names_joined = '__and__'.join(names)
3 | names = [f'"{name}"' for name in names ]
4 | names_csv = ', '.join(names)
5 | return f"""
6 |
7 |
8 |
145 |