ideonate / cdsdashboards Goto Github PK
View Code? Open in Web Editor NEWJupyterHub extension for ContainDS Dashboards
Home Page: https://cdsdashboards.readthedocs.io/
License: Other
JupyterHub extension for ContainDS Dashboards
Home Page: https://cdsdashboards.readthedocs.io/
License: Other
Is your feature request related to a problem? Please describe.
N/A
Describe the solution you'd like
Follow-up of #28
It could be interesting to get the auth_state
too as part of the user information obtain from hub-info/user.
Describe alternatives you've considered
N/A
Background context
User information obtained from the authenticator could be useful inside a dashboard to tweak request to internal data server or services.
Configuration
JHub: 1.2
cdsdashboard: 0.4.1
Once authenticated into a dashboard, provide a way for dashboard apps to obtain the viewer's username from JupyterHub.
This would most likely be via a cookie.
Env vars aren't going to help here because each dashboard can have multiple visitors at the same time.
From @ricky-lim on issue 24 : With relation to the singleuser docker image, I have a question regarding how could streamlit uses the authenticated state of a user ?
Within the singleuser image, the NB_USER env is set into jovyan and JUPYTERHUB_USER is set to the creator of the dashboard, instead of the authenticated user.
I was wondering what is your advice for a dashboard creator to use the authenticated state, such as a username following the dashboard authentication?
Is your feature request related to a problem? Please describe.
N/A
Describe the solution you'd like
It would be nice to switch to the dashboard once the server is started instead of requesting the user to click on "Go to dashboard" button.
This will be similar to the behavior of the notebook/jupyterlab server.
Describe alternatives you've considered
N/A
Background context
N/A
Configuration
N/A
Is your feature request related to a problem? Please describe.
Nope
Describe the solution you'd like
A dashboard creation form that could show user-specific conda environments that runs a streamlit dashboarding server.
Describe alternatives you've considered
Using the global conda environments, however streamlit does not run within a specified environment.
It stills runs with base python, instead of the specified python environment.
Below is the error, in which I tried to use a conda env that installed bqplot
package.
File "/opt/conda/lib/python3.8/site-packages/streamlit/ScriptRunner.py", line 319, in _run_script
exec(code, module.__dict__)
File "/home/jovyan/test_streamlit.py", line 2, in <module>
import bqplot as bq
The solution, that I thought that a user could create her/his own environment, for instance with miniconda to run streamlit server.
Background context
I like to explore streamlit as a dashboarding framework for a project and every project has a specific set of dependencies.
Configuration
The setup is using zero to kubernetes.
Thanks in advance. I look forward to hearing from you.
Describe the bug
Dashboard logout doesn't happen when JH logout does
To Reproduce
OAuth into a dashboard, click logout back on JH main page. Dashboard still logged in.
Allow admin to manage the dashboards from others, such as delete and edit the dashboards via UI interface.
I was wondering if this is already feasible with the dashboards/api ?
Thanks in advance.
Cheers
Dear community,
is it possible to disable the authorise message when accessing dashboards of other users?
Many thanks
Tom
Hi! Thank you for this great project!
I'm not 100% sure, but it seems that when you share a dashboard, other users will connect to the same server.
It can/will cause bad experiences when building interactive dashboards for example: user1 changes a slider and user2 sees an unexpected update!
It is quite the same behaviour you have when multiple users are connecting a single-user JupyterLab
instance.
Spawn a per user dashboard server.
Be sure that all the dashboarding solutions handled (such as Voilà
) use input files as read-only to avoid conflicts?
You can not garantee that the code inside the notebook isn't reading or writing files in the current path during execution, also causing collisions..
As suggested here, dashboard creators might want to create their own local conda envs, and specify those when creating a dashboard. At the moment, they can only choose from a list of admin-provided global conda env names.
A new config option should allow the dashboard creator to type any conda env name, and/or ideally a full path to a conda env.
Alternative workaround: it is possible for a local conda env to work for the dashboard creator if the global list happens to contain the name of a local conda env instead of a global env.
Applies to any type of spawner/config.
Is it planned to support ACL? e.g. listing JupyerHub users that are allowed to open which dashboards.
Hi Dan,
I was testing a basic voila application using cdsdashboards on z2h, with similar setup as in issue #43
The voila application repo: https://github.com/ricky-lim/wealth-nederland
The application was also deployed at heroku (as a test) here: http://wealth-of-nederland.herokuapp.com/
Upon first launch, the application was served properly, however for the subsequent requests (next users), unfortunately it gave TimeoutError
Further digging into the log, I found this error from jhsingle_native_proxy
.
Still puzzled what went wrong. I would be very much grateful to hear your advice to solve this issue, please.
Cheers and thanks in advance for your time and support.
future: <Task finished coro=<SuperviseAndProxyHandler.ensure_process.<locals>.pipe_output() done, defined at /opt/conda/lib/python3.7/site-packages/jhsingle_native_proxy/proxyhandlers.py:645> exception=ValueError('Separator is found, but chunk is longer than limit')>
Traceback (most recent call last):
File "/opt/conda/lib/python3.7/asyncio/streams.py", line 496, in readline
line = await self.readuntil(sep)
File "/opt/conda/lib/python3.7/asyncio/streams.py", line 592, in readuntil
'Separator is found, but chunk is longer than limit', isep)
asyncio.streams.LimitOverrunError: Separator is found, but chunk is longer than limit
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/opt/conda/lib/python3.7/site-packages/jhsingle_native_proxy/proxyhandlers.py", line 650, in pipe_output
line = await stream.readline()
File "/opt/conda/lib/python3.7/asyncio/streams.py", line 505, in readline
raise ValueError(e.args[0])
ValueError: Separator is found, but chunk is longer than limit
Hi
Whenever I want to find this repo I have to navigate to pypi https://pypi.org/project/cdsdashboards/ to find https://github.com/ideonate/cdsdashboards.
Please add GitHub link to
Describe the bug
Relative path for git root is not relative
FileNotFoundError: [Errno 2] No such file or directory: '/home/jupyter-xxx/github-com-ideonate-cdsdashboards/./github-com-ideonate-cdsdashboards/examples/sample-source-code/plotlydash'
To Reproduce
User guide example produces the error.
Configuration
Using The Littlest JupyterHub
Describe the bug
If Spawner.default_url is set, e.g. to '/lab' as a default landing page for notebooks, this can break dashboards.
To Reproduce
Set c.Spawner.default_url = '/lab' and spawn any dashboards.
Applies to any spawner type I think.
Is your feature request related to a problem? Please describe.
Dashboard can be seen as enhanced static report. And as for the latter, it would make sense to parametrize them. For example, if a dashboard is presenting a summary of events on a certain time window, it would be nice if the time window could be provided as query argument. So the dashboard is preset with those query arguments.
Describe the solution you'd like
For notebook based dashboard, a potential solution would be to use papermill. The workflow would be:
Describe alternatives you've considered
Background context
Configuration
Describe the bug
My Panel dashboard does not render (blank page) when published as a dashboard. console shows bokeh scripts are loaded. No errors in the browser.
The Kubernetes logs for the dashboard server pod show this log:
+ '[' '' ']'
+ '[' -e /opt/app/environment.yml ']'
+ echo 'no environment.yml'
+ '[' '' ']'
+ '[' '' ']'
+ exec start.sh python3 -m jhsingle_native_proxy.main --destport=0 python3 '{-}m' bokeh_root_cmd.main '{presentation_path}' '{--}port={port}' '{--}allow-websocket-origin={origin_host}' --ready-check-path=/ready-check --presentation-path=./panel_test.py --ip=0.0.0.0 --port=8888
no environment.yml
Executing the command: python3 -m jhsingle_native_proxy.main --destport=0 python3 {-}m bokeh_root_cmd.main {presentation_path} {--}port={port} {--}allow-websocket-origin={origin_host} --ready-check-path=/ready-check --presentation-path=./panel_test.py --ip=0.0.0.0 --port=8888
INFO:tornado.application:SuperviseAndProxyHandler http_get 37007
ERROR:tornado.application:Uncaught exception GET /user/matthiasdv/dash-pane/ (10.0.57.3)
HTTPServerRequest(protocol='http', host='10.0.57.17:8888', method='GET', uri='/user/matthiasdv/dash-pane/', version='HTTP/1.1', remote_ip='10.0.57.3')
Traceback (most recent call last):
File "/home/jovyan/.local/lib/python3.8/site-packages/tornado/web.py", line 1704, in _execute
result = await result
File "/opt/conda/lib/python3.8/site-packages/jhsingle_native_proxy/websocket.py", line 103, in get
return await self.http_get(*args, **kwargs)
File "/opt/conda/lib/python3.8/site-packages/jhsingle_native_proxy/proxyhandlers.py", line 724, in http_get
return await self.proxy(self.port, path)
File "/opt/conda/lib/python3.8/site-packages/jhsingle_native_proxy/proxyhandlers.py", line 718, in proxy
return await self.oauth_proxy(port, path)
TypeError: object NoneType can't be used in 'await' expression
INFO:tornado.application:SuperviseAndProxyHandler http_get 37007
ERROR:tornado.application:Uncaught exception GET /user/matthiasdv/dash-pane/ (10.132.0.46)
HTTPServerRequest(protocol='https', host='nest-dev.kondensator.io', method='GET', uri='/user/matthiasdv/dash-pane/', version='HTTP/1.1', remote_ip='10.132.0.46')
Traceback (most recent call last):
File "/home/jovyan/.local/lib/python3.8/site-packages/tornado/web.py", line 1704, in _execute
result = await result
File "/opt/conda/lib/python3.8/site-packages/jhsingle_native_proxy/websocket.py", line 103, in get
return await self.http_get(*args, **kwargs)
File "/opt/conda/lib/python3.8/site-packages/jhsingle_native_proxy/proxyhandlers.py", line 724, in http_get
return await self.proxy(self.port, path)
File "/opt/conda/lib/python3.8/site-packages/jhsingle_native_proxy/proxyhandlers.py", line 718, in proxy
return await self.oauth_proxy(port, path)
TypeError: object NoneType can't be used in 'await' expression
INFO:tornado.application:Logged-in user {'kind': 'user', 'name': 'matthiasdv', 'admin': True, 'groups': [], 'server': '/user/matthiasdv/', 'pending': None, 'created': '2020-07-15T18:18:39.366713Z', 'last_activity': '2021-01-15T20:06:45.256425Z', 'servers': None}
INFO:tornado.application:SuperviseAndProxyHandler http_get 37007
INFO:tornado.application:['python3', '-m', 'bokeh_root_cmd.main', '/home/jovyan/./panel_test.py', '--port=37007', '--allow-websocket-origin=nest-dev.kondensator.io']
INFO:tornado.application:SuperviseAndProxyHandler http_get 37007 static/js/bokeh.min.js
INFO:tornado.application:SuperviseAndProxyHandler http_get 37007 static/js/bokeh-tables.min.js
INFO:tornado.application:SuperviseAndProxyHandler http_get 37007 static/js/bokeh-widgets.min.js
INFO:tornado.application:SuperviseAndProxyHandler http_get 37007 static/extensions/panel/panel.min.js
INFO:tornado.application:Trying to establish websocket connection to ws://localhost:37007/ws
INFO:tornado.application:Websocket connection established to ws://localhost:37007/ws
INFO:tornado.application:Client sent subprotocols: ['bokeh', 'eyJzZXNzaW9uX2lkIjogIjVLZ1Z2WERudWNlNmc1Q2MxSXdOc0RNb3NjWVJmUWxKeXp1eUoyT2lmVWhlIiwgInNlc3Npb25fZXhwaXJ5IjogMTYxMDc0MTUwNiwgImhlYWRlcnMiOiB7IlgtU2NoZW1lIjogImh0dHBzIiwgIlgtUmVhbC1JcCI6ICIxMC4xMzIuMC40NiIsICJYLUZvcndhcmRlZC1TZXJ2ZXIiOiAiYXV0b2h0dHBzLTU1OWJjZjRmODctbDZ2eG4iLCAiWC1Gb3J3YXJkZWQtUHJvdG8iOiAiaHR0cHMsaHR0cCIsICJYLUZvcndhcmRlZC1Qb3J0IjogIjQ0Myw4MCIsICJYLUZvcndhcmRlZC1Ib3N0IjogIm5lc3QtZGV2LmtvbmRlbnNhdG9yLmlvIiwgIlgtRm9yd2FyZGVkLUZvciI6ICIxMC4xMzIuMC40NiwxMC4wLjQ3LjIiLCAiVXBncmFkZS1JbnNlY3VyZS1SZXF1ZXN0cyI6ICIxIiwgIlNlYy1GZXRjaC1Vc2VyIjogIj8xIiwgIlNlYy1GZXRjaC1TaXRlIjogInNhbWUtb3JpZ2luIiwgIlNlYy1GZXRjaC1Nb2RlIjogIm5hdmlnYXRlIiwgIlNlYy1GZXRjaC1EZXN0IjogImRvY3VtZW50IiwgIlNlYy1DaC1VYS1Nb2JpbGUiOiAiPzAiLCAiU2VjLUNoLVVhIjogIlwiR29vZ2xlIENocm9tZVwiO3Y9XCI4N1wiLCBcIiBOb3Q7QSBCcmFuZFwiO3Y9XCI5OVwiLCBcIkNocm9taXVtXCI7dj1cIjg3XCIiLCAiUmVmZXJlciI6ICJodHRwczovL25lc3QtZGV2LmtvbmRlbnNhdG9yLmlvL2h1Yi9kYXNoYm9hcmRzL3BhbmUiLCAiQ29va2llIjogImp1cHl0ZXJodWItdXNlci1tYXR0aGlhc2R2LWRhc2gtcGFuZT0yfDE6MHwxMDoxNjEwNzQxMjA1fDM2Omp1cHl0ZXJodWItdXNlci1tYXR0aGlhc2R2LWRhc2gtcGFuZXw0MDpXakJNWmxOVWEzcHZSWGRzUjNWRGFFRkxURFJxV1dOdFpYaFFXRTVvfDAyYmU4MjEwODAwNjViNDU5YzcxNjA0ZjBlNWQzOGU1YTE5MTk1MmZiOGI5NzExZTFhOGZjMTQ4ZGM4MTE2ZGQ7IGp1cHl0ZXJodWItdXNlci1tYXR0aGlhc2R2PVwiMnwxOjB8MTA6MTYxMDc0MTE4NnwyNjpqdXB5dGVyaHViLXVzZXItbWF0dGhpYXNkdnw0NDpOamd3TVdFNU9XWXpNelk0TkRGaVpqaGxaV0pqWlRoaVptVTNZVEkyWW1ZPXw1Y2M1MDRlZDIyYjM5ZTYyMGM1YmI1N2EzMzdjYzU1MmZlOTYyYTkyZGY0MWUxZWE4MDQ4MTAzYzU5NTcwYTBjXCI7IGp1cHl0ZXJodWItc2Vzc2lvbi1pZD1mMjEyNDg1NWRiZjc0NzEwYjI3NTgzZDBhZGI3NzQ5ODsgX3hzcmY9Mnw5NzRiOTMwN3w0Y2Y5ZWYxN2EzOTA3MmUwY2EyNWFiZmU5NjQ5MjRhOHwxNjEwMzgyMDgxIiwgIkFjY2VwdC1MYW5ndWFnZSI6ICJlbi1HQixlbi1VUztxPTAuOSxlbjtxPTAuOCIsICJBY2NlcHQtRW5jb2RpbmciOiAiZ3ppcCIsICJBY2NlcHQiOiAidGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksaW1hZ2UvYXZpZixpbWFnZS93ZWJwLGltYWdlL2FwbmcsKi8qO3E9MC44LGFwcGxpY2F0aW9uL3NpZ25lZC1leGNoYW5nZTt2PWIzO3E9MC45IiwgIlVzZXItQWdlbnQiOiAiTW96aWxsYS81LjAgKE1hY2ludG9zaDsgSW50ZWwgTWFjIE9TIFggMTBfMTVfNCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzg3LjAuNDI4MC44OCBTYWZhcmkvNTM3LjM2IiwgIkhvc3QiOiAibmVzdC1kZXYua29uZGVuc2F0b3IuaW8iLCAiQ29ubmVjdGlvbiI6ICJjbG9zZSIsICJYLUZvcndhcmRlZC1Db250ZXh0IjogIi91c2VyL21hdHRoaWFzZHYvZGFzaC1wYW5lL21haW5wcm9jZXNzIiwgIlgtUHJveHljb250ZXh0cGF0aCI6ICIvdXNlci9tYXR0aGlhc2R2L2Rhc2gtcGFuZS9tYWlucHJvY2VzcyJ9LCAiY29va2llcyI6IHsianVweXRlcmh1Yi11c2VyLW1hdHRoaWFzZHYtZGFzaC1wYW5lIjogIjJ8MTowfDEwOjE2MTA3NDEyMDV8MzY6anVweXRlcmh1Yi11c2VyLW1hdHRoaWFzZHYtZGFzaC1wYW5lfDQwOldqQk1abE5VYTNwdlJYZHNSM1ZEYUVGTFREUnFXV050WlhoUVdFNW98MDJiZTgyMTA4MDA2NWI0NTljNzE2MDRmMGU1ZDM4ZTVhMTkxOTUyZmI4Yjk3MTFlMWE4ZmMxNDhkYzgxMTZkZCIsICJqdXB5dGVyaHViLXVzZXItbWF0dGhpYXNkdiI6ICIyfDE6MHwxMDoxNjEwNzQxMTg2fDI2Omp1cHl0ZXJodWItdXNlci1tYXR0aGlhc2R2fDQ0Ok5qZ3dNV0U1T1dZek16WTROREZpWmpobFpXSmpaVGhpWm1VM1lUSTJZbVk9fDVjYzUwNGVkMjJiMzllNjIwYzViYjU3YTMzN2NjNTUyZmU5NjJhOTJkZjQxZTFlYTgwNDgxMDNjNTk1NzBhMGMiLCAianVweXRlcmh1Yi1zZXNzaW9uLWlkIjogImYyMTI0ODU1ZGJmNzQ3MTBiMjc1ODNkMGFkYjc3NDk4IiwgIl94c3JmIjogIjJ8OTc0YjkzMDd8NGNmOWVmMTdhMzkwNzJlMGNhMjVhYmZlOTY0OTI0YTh8MTYxMDM4MjA4MSJ9fQ']
INFO:tornado.application:SuperviseAndProxyHandler http_get 37007
INFO:tornado.application:Trying to establish websocket connection to ws://localhost:37007/ws
INFO:tornado.application:Websocket connection established to ws://localhost:37007/ws
To Reproduce
This is the .py file that produces the error:
https://gist.github.com/matthiasdv/03430b5d1609857d7b482c6e26386516
pip freeze output:
aiobotocore==1.1.2
aiohttp==3.7.3
aioitertools==0.7.1
alembic==1.4.2
appdirs==1.4.4
async-generator==1.10
async-timeout==3.0.1
attrs==20.3.0
backcall==0.2.0
bleach @ file:///home/conda/feedstock_root/build_artifacts/bleach_1588608214987/work
blinker==1.4
blosc==1.9.1
bokeh==2.2.3
bokeh-root-cmd==0.0.5
botocore==1.17.44
brotlipy==0.7.0
cachetools==4.2.0
cdsdashboards==0.4.3
certifi==2020.6.20
certipy==0.1.3
cffi==1.14.0
chardet==3.0.4
click==7.1.2
cloudpickle==1.6.0
coiled==0.0.30
conda==4.8.3
conda-package-handling==1.6.0
cryptography==2.9.2
cycler==0.10.0
cytoolz==0.10.1
dask==2020.12.0
dask-glm==0.2.0
dask-kubernetes==0.10.1
dask-labextension @ file:///home/conda/feedstock_root/build_artifacts/dask-labextension_1588177406011/work
dask-ml==1.6.0
decorator==4.4.2
defusedxml==0.6.0
distributed==2020.12.0
docutils==0.15.2
entrypoints==0.3
fastparquet==0.4.0
fsspec==0.8.4
gcsfs==0.6.2
google-auth==1.24.0
google-auth-oauthlib==0.4.2
graphviz==0.14
HeapDict==1.0.1
idna==2.10
importlib-metadata @ file:///home/conda/feedstock_root/build_artifacts/importlib-metadata_1593211370946/work
intake==0.6.0
intake-parquet==0.2.3
ipykernel @ file:///home/conda/feedstock_root/build_artifacts/ipykernel_1590020202605/work/dist/ipykernel-5.3.0-py3-none-any.whl
ipython==7.19.0
ipython-genutils==0.2.0
ipywidgets==7.5.1
jedi==0.17.2
jhsingle-native-proxy==0.6.1
Jinja2==2.11.2
jmespath==0.10.0
joblib==0.16.0
json5 @ file:///home/conda/feedstock_root/build_artifacts/json5_1591810480056/work
jsonschema==3.2.0
jupyter-client @ file:///home/conda/feedstock_root/build_artifacts/jupyter_client_1593562235049/work
jupyter-core==4.6.3
jupyter-server-proxy @ file:///home/conda/feedstock_root/build_artifacts/jupyter-server-proxy_1590048206892/work
jupyter-telemetry==0.0.5
jupyterhub==1.1.0
jupyterlab==2.1.3
jupyterlab-server @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_server_1590229434073/work
kiwisolver==1.3.1
kubernetes==12.0.1
kubernetes-asyncio==12.0.1
llvmlite==0.35.0
locket==0.2.0
lz4 @ file:///home/conda/feedstock_root/build_artifacts/lz4_1591768133689/work
Mako==1.1.0
Markdown==3.3.3
MarkupSafe==1.1.1
matplotlib==3.3.3
mistune==0.8.4
msgpack==1.0.1
multidict==5.1.0
multipledispatch==0.6.0
nbconvert==5.6.1
nbformat==5.0.6
notebook @ file:///home/conda/feedstock_root/build_artifacts/notebook_1588887220875/work
numba==0.52.0
numpy==1.19.4
oauthlib==3.0.1
olefile==0.46
packaging==20.8
pamela==1.0.0
pandas==1.1.5
pandocfilters==1.4.2
panel==0.9.7
param==1.10.1
parso==0.7.1
partd==1.1.0
pexpect==4.8.0
pickleshare==0.7.5
Pillow==8.0.1
plotlydash-tornado-cmd==0.0.6
pluggy==0.13.1
prometheus-client @ file:///home/conda/feedstock_root/build_artifacts/prometheus_client_1590412252446/work
prompt-toolkit==3.0.8
psutil==5.7.3
ptyprocess==0.6.0
py4j==0.10.9
pyarrow==0.17.1
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycosat==0.6.3
pycparser @ file:///home/conda/feedstock_root/build_artifacts/pycparser_1593275161868/work
pyct==0.4.8
pycurl==7.43.0.5
Pygments==2.7.3
PyJWT==1.7.1
pyOpenSSL==19.1.0
pyparsing==2.4.7
pyrsistent==0.16.0
PySocks==1.7.1
pyspark==3.0.0
python-dateutil==2.8.1
python-editor==1.0.4
python-json-logger==0.1.11
python-snappy==0.5.4
pytz==2020.4
pyviz-comms==2.0.1
PyYAML==5.3.1
pyzmq==19.0.1
requests @ file:///home/conda/feedstock_root/build_artifacts/requests_1592425495151/work
requests-oauthlib==1.3.0
rsa==4.7
rshiny-server-cmd==0.0.2
ruamel-yaml==0.15.80
ruamel.yaml.clib==0.2.0
s3fs==0.5.1
scikit-learn==0.23.2
scipy==1.5.2
Send2Trash==1.5.0
simpervisor==0.4
six==1.15.0
sortedcontainers==2.3.0
SQLAlchemy @ file:///home/conda/feedstock_root/build_artifacts/sqlalchemy_1593116999372/work
tblib==1.7.0
terminado==0.8.3
testpath==0.4.4
threadpoolctl==2.1.0
thrift==0.13.0
toolz==0.11.1
tornado==6.1
tqdm @ file:///home/conda/feedstock_root/build_artifacts/tqdm_1593390011681/work
traitlets==5.0.5
typing-extensions==3.7.4.3
urllib3==1.25.11
voila-materialstream==0.2.6
wcwidth==0.2.5
webencodings==0.5.1
websocket-client==0.57.0
widgetsnbextension==3.5.1
wrapt==1.12.1
yarl==1.6.3
zict==2.0.0
zipp==3.1.0
Configuration
versions:
Jupyterhub Helm chart 0.9.0
CDS 0.4.3
Z2Hjupyterhub on Kubernetes, Google Cloud Engine
Using ReadMany filesystem
jupyterhub conf:
# serviceAccountName: default
defaultUrl: "/lab"
image:
name: eu.gcr.io/calm-nation-277816/dask-notebook
tag: latest
pullPolicy: Always
storage:
type: dynamic
capacity: 10Gi
dynamic:
pvcNameTemplate: claim-{username}
volumeNameTemplate: volume-{username}
storageAccessModes: [ ReadWriteMany ]
cloudMetadata:
enabled: true
hub:
image:
name: ideonate/cdsdashboards-jupyter-k8s-hub
tag: 0.9.0-0.4.3
extraConfig:
cds-handlers: |
from cdsdashboards.hubextension import cds_extra_handlers
c.JupyterHub.extra_handlers = cds_extra_handlers
cds-templates: |
from cdsdashboards.app import CDS_TEMPLATE_PATHS
c.JupyterHub.template_paths = CDS_TEMPLATE_PATHS
c.JupyterHub.allow_named_servers = True
cds-kube: |
c.JupyterHub.spawner_class = 'cdsdashboards.hubextension.spawners.variablekube.VariableKubeSpawner'
c.CDSDashboardsConfig.builder_class = 'cdsdashboards.builder.kubebuilder.KubeBuilder'
resources:
requests:
cpu: 1000m
memory: 2048Mi
Any directions are appreciated!
Describe the bug
A clear and concise description of what the bug is.
Best is to understand the problem is to look at the gif below.
To Reproduce
Clear steps to reproduce the behavior, including any of your own ipynb or py files if they led to the error.
Screenshots
Please add screenshots to help explain your problem.
The failure raises from https://github.com/jupyterhub/jupyterhub/blob/e5a6119505f89a293447ce4e727c4bd15e86b145/jupyterhub/apihandlers/auth.py#L277-282
But from the web dev tools, the Referer
header matches the page URL.
Configuration
Include as much jupyterhub_config information as you can - at least enough to understand which Spawner type you are using, and how your JupyterHub is deployed (e.g. The Littlest JupyterHub, or Zero to JupyterHub).
cdsdashboards 0.3.0
ldapauthenticator 1.3.0
jupyterhub 1.1.0
jupyterhub-base 1.1.0
spawner_class = (
"cdsdashboards.hubextension.spawners.VariableLocalProcessSpawner"
)
Hi Dan,
In my setup, the user of the jupyter is set with her/his username coupled to the permissions from an active directory.
With each user has its own homedir, such as '/home/ril' instead of home/jovyan
Unfortunately while trying to execute voila, I encountered the following error, which I think might be caused by trying to set username to jovyan.
I was wondering if you have encountered this issue before and I'll be very grateful if you could help, please.
Thank you in advance for your time and consideration.
The log with its error messages
Set username to: jovyan
usermod: no changes
Executing the command: python3 -m jhsingle_native_proxy.main --destport=0 python3 {-}m voila {presentation_path} {--}port={port} {--}no-browser {--}Voila.base_url={base_url}/ {--}Voila.server_url=/ --presentation-path=./gaussian_process_regression.ipynb --ip=0.0.0.0 --port=8888 --request-timeout=600 --last-activity-interval=600 --allow-root
sudo: setrlimit(RLIMIT_CORE): Operation not permitted
Hi all,
can we add to the section:
https://cdsdashboards.readthedocs.io/en/stable/chapters/customization/finetune.html
A section on how to define different user groups and how to redirect them as you are likely to have technical and non-technical users and you want to differentiate their user experience.
Many thanks
Tom
Does cdsdashboards support RShiny or did someone integrate them already? example?
Describe the bug
During starting up a dashboard, it yields 404
To Reproduce
Local jupyterhub setup with minikube: https://github.com/ricky-lim/jupyterhub-minikube/tree/fastapi
main.py
: https://github.com/ricky-lim/jupyterhub-minikube/blob/fastapi/examples/main.py
Screenshots
https://drive.google.com/file/d/13u8ZBx7nADi-nJI3Z9k6dIF8vaWaQLFh/view?usp=sharing
Any tweaks could you advise to avoid 404 during the start-up ?
Thanks in advance. I look forward to hearing from you
Is your feature request related to a problem? Please describe.
We have a jupyterhub deployment using batchspawner. We'd like to be able to configure the size of the instance (memory, cpus, etc.) on which the dashboard is deployed. Currently, when launching a new user session, JupyterHub displays a Server Options page at /hub/spawn/<user>
and displays the form which we've configure via c.Spawner.options_form
. It would be nice if 1) the same form specified by c.Spawner.options_form
was displayed on the edit-dashboard.html page or 2) have a {% block spawner_options %}{% endblock %}
added near the bottom of editdashboard.html file so that we could extend editdashboard directly without needing to redefine the entire file.
In our case, the options from the form need to end up in the new_server_options
variable of the processbuilder. In jupyerhub, this seems to occur by assigning a function which reutrns a dictionary to c.Spawner.options_from_form
in the config. It would be nice if setting c.Spawner.options_from_form
also added the options to new_server_options
of Process Builder, though there might be use cases where this isn't desirable.
Describe alternatives you've considered
Currently, I got around this by overwriting editdashboard.html, subclassing BasicDashboardEditHandler, and overwriting the post method in order to add the additional form options to dashboard.options. I then subclassed ProcessBuilder and overwrote prespawn_server_options to get the form values into new_server_options
defined in the ProcessBuilder.run method. Here is a link to the PR.
Hi
I am a user of Panel and a future user of JupyterHub. Please add support for Panel to your solution.
Panel is one of the four main python frameworks for building analytics apps and dashboards. Much more downloaded than for example Voila.
If you want to see what kind of things can be developed in Panel checkout the video below or the site I develop awesome-panel.org.
Hi, any ideas how to troubleshoot this error?
[E 2020-06-22 15:25:25.045 JupyterHub web:1792] Uncaught exception GET /hub/dashboards-api/rshiny-test/progress (::ffff:10.2.44.0)
HTTPServerRequest(protocol='http', host='staging-*****', method='GET', uri='/hub/dashboards-api/rshiny-test/progress', version='HTTP/1.1', remote_ip='::ffff:10.2.44.0')
Traceback (most recent call last):
File "/opt/conda/lib/python3.7/site-packages/tornado/web.py", line 1703, in _execute
result = await result
File "/opt/conda/lib/python3.7/site-packages/cdsdashboards/hubextension/events.py", line 82, in get
async for event in events:
File "/opt/conda/lib/python3.7/site-packages/async_generator/_impl.py", line 366, in step
return await ANextIter(self._it, start_fn, *args)
File "/opt/conda/lib/python3.7/site-packages/async_generator/_impl.py", line 197, in __next__
return self._invoke(first_fn, *first_args)
File "/opt/conda/lib/python3.7/site-packages/async_generator/_impl.py", line 209, in _invoke
result = fn(*args)
File "/opt/conda/lib/python3.7/site-packages/jupyterhub/utils.py", line 551, in iterate_until
[item_future, deadline_future], return_when=asyncio.FIRST_COMPLETED
File "/opt/conda/lib/python3.7/asyncio/tasks.py", line 387, in wait
fs = {ensure_future(f, loop=loop) for f in set(fs)}
File "/opt/conda/lib/python3.7/asyncio/tasks.py", line 387, in <setcomp>
fs = {ensure_future(f, loop=loop) for f in set(fs)}
File "/opt/conda/lib/python3.7/asyncio/tasks.py", line 619, in ensure_future
raise TypeError('An asyncio.Future, a coroutine or an awaitable is '
TypeError: An asyncio.Future, a coroutine or an awaitable is required
I've looking into adding a method to bulk create/start dashboards in cdsdashboards.
I took a quick look around hubextension/api and it looks like there are api endpoints for listing and deleting dashboards but nothing pre-existing for creating or updating dashboards - that I could add my own endpoints for adding/starting/stopping dashboards there but that it was not currently implemented.
Is anyone doing anything similar? Are there others interested in this sort of thing? Known gotchas to beware of?
Is your feature request related to a problem? Please describe.
I did not find a easy way to obtain a shareable link for a dashboard (sorry if I miss a straightforward way).
Maybe a copy shareable link
feature on the dashboards page could be helpful.
Describe the solution you'd like
Describe alternatives you've considered
Background context
Configuration
Hi! Firstly, awesome project!
Currently the Voila template argument defaults to 'materialstream'.
Is there a way to overwrite the template argument set when launching Voila? Or do I have to make add a launcher to extra_presentation_launchers?
cdsdashboards/cdsdashboards/hubextension/spawners/variablemixin.py
Lines 87 to 92 in 8406977
Describe the bug
If the file used for the dashboard contains the character '
, it won't work. In the form to create the dashboard, an error will appear telling the user the path is not a valid URL. And if the path in URL encoded, voila is not able to pick the notebook as the path it receives is encoded. Therefore voila is not finding the file.
To Reproduce
Name a notebook: Bob's dashboard.ipynb
Create a dashboard from that notebook, using the exact name you will the form will complain that the path is not a valid URL.
If '
is replaced by %27
in the form to create the dashboard, the dashboard form is valid. But voila will not spin the server due to a FileNotFoundError
.
Screenshots
N/A
Configuration
LocalSpawner
voila
Thanks for this project! I saw:
Support for Kubernetes JupyterHub installations running KubeSpawner is in development
So maybe we can keep this issue to track the status of KubeSpawner support
Is your feature request related to a problem? Please describe.
To scale with large number of created dashboards
Describe the solution you'd like
A configurable pagination using config from jupyterhub 1.2.
Pagination(Configurable) options
--------------------------------
--Pagination.default_per_page=<Int>
Default number of entries per page for paginated results.
Default: 100
--Pagination.max_per_page=<Int>
Maximum number of entries per page for paginated results.
Default: 250
Describe alternatives you've considered
Pagination in the admin panel in jupyterhub: jupyterhub/jupyterhub#2929
Background context
I am a scientific programmer and currently testing the feasibility of this software to create a large number of dashboards.
Configuration
I am using Zero to JupyterHub
It would be great for the user to create their own HTML overview for the individual dashboards in a similar fashion as the voila-gallery. Is there a place where I could store this HTML. I would then point the default URL to this HTML and the HTML has links to the individual dashboards
Many thanks
Tom
Is your feature request related to a problem? Please describe.
As I have done quite some tests and demos, I generated plenty dashboards that eventually got broken simply because the underlying file has been moved or deleted.
I think removing them automatically is probably not a good idea. But maybe adding some visual hint (similar to a broken link) in the dashboards page may help spotting dashboards that need attention.
Describe the solution you'd like
Describe alternatives you've considered
Background context
Configuration
Is your feature request related to a problem? Please describe.
Serving a directory with the voila presentation type only serves .ipynb files, not any other static content. One use case I have is serving data as a file alongside the notebook, for both performance reasons and limiting user access.
Describe the solution you'd like
Allow specifying a file whitelist, either when creating a dashboard or in the configuration files. I'm open to better solutions though
Describe alternatives you've considered
There's a super hacky solution by subclassing VariableKubeSpawner, e.g.:
class ExtendedVariableKubeSpawner(VariableKubeSpawner):
def get_args(self):
args = super().get_args()
if self._get_presentation_type() == 'voila':
args.append("{--}VoilaConfiguration.file_whitelist=['.*']")
return args
Also, data files could just be stored elsewhere (e.g., S3) and downloaded on spawn. I haven't explored this option much and don't know if users have a temporary directory that is cleaned up when the server shuts down.
Background context
Most of the dashboards at my company are aimed at presenting large amounts of data in different slices or visualizations. One way we've done this is send aggregated or highlight reports. We'd want dashboards we make to support showing small, short-lived datasets even when generating that dataset is expensive.
Describe the bug
When launching a voila dashboard server, 2 jupyter kernel process are created. I don't know if this is related to this project or to voila itself.
fcollon+ 455 9.0 0.3 787552 113316 ? Sl 14:39 0:01 python3 -m voila /srv/userspace/fcollonval/Presentation.ipynb --port=42309 --no-browser --Voila.base_url=/user/fcollonval/dash-demo-2/ --Voila.server_url=/ --template=axt-material
fcollon+ 468 13.6 0.3 1174500 122664 ? Ssl 14:39 0:02 /opt/conda/bin/python -m ipykernel_launcher -f /tmp/voila_r5ji1acz/kernel-5a8782af-3327-4216-8441-55c0455686b2.json
fcollon+ 476 12.8 0.3 1174496 110708 ? Ssl 14:39 0:02 /opt/conda/bin/python -m ipykernel_launcher -f /tmp/voila_r5ji1acz/kernel-922ddf42-5c12-417f-a154-005dad8eb086.json
fcollon+ 484 1.8 0.1 797816 42744 ? Ssl 14:39 0:00 python3 -m jhsingle_native_proxy.main --destport=0 python3 {-}m voila {presentation_path} {--}port={port} {--}no-browser {--}Voila.base_url={base_url}/ {--}Voila.server_url=/ --presentation-path=/srv/userspace/fcollonval/Presentation.ipynb --port=57519 {--}template=default
fcollon+ 542 8.5 0.3 784940 116160 ? Sl 14:39 0:01 python3 -m voila /srv/userspace/fcollonval/Presentation.ipynb --port=39029 --no-browser --Voila.base_url=/user/fcollonval/dash-demo/ --Voila.server_url=/ --template=default
fcollon+ 566 15.1 0.3 1174500 110344 ? Ssl 14:39 0:02 /opt/conda/bin/python -m ipykernel_launcher -f /tmp/voila_xr_2blkv/kernel-59f73e48-ea8d-40a0-9451-a14e388bd4ba.json
fcollon+ 574 14.4 0.3 1174492 110912 ? Ssl 14:39 0:02 /opt/conda/bin/python -m ipykernel_launcher -f /tmp/voila_xr_2blkv/kernel-5f7a8664-d400-4149-be4e-c2c2b55f4de0.json
To Reproduce
ps auww
on the server machineScreenshots
N/A
Configuration
c.JupyterHub.spawner_class = 'cdsdashboards.hubextension.spawners.VariableLocalProcessSpawner'
Hi,
I'm experimenting with fastapi (https://fastapi.tiangolo.com/) as a dashboard served with a uvicorn, and it works as expected.
I'm curious if we could use cdsdashboards for authentication in this case.
I was wondering what would be the best approach with cdsdashboards, to obtain the user-info, once authenticated, programmatically ?
I was trying from /hub/dashboards-api/hub-info/user
, unfortunately it gives error during redirection on the browser. INFO:tornado.application:b'INFO: 10.42.0.166:0 - "GET /user/ril/dash-biotools-api/whoami HTTP/1.1" 307 Temporary Redirect\n'
Below is my current setup.
# uvicorn to serve the main.py
c.VariableMixin.extra_presentation_launchers = {
'uvicorn': {
'cmd': ['start.sh', 'python3', '-m', 'jhsingle_native_proxy.main', '--logs', '--debug'],
'args': ['--destport=0', 'python3',
'{presentation_path}',
'{--}root-path={base_url}',
'{--}port={port}',
],
},
}
# main.py
import typer
import uvicorn
from fastapi import FastAPI
from starlette.responses import RedirectResponse
api = FastAPI()
@api.get('/foo')
def foo():
return 'bar'
@api.get('/whoami')
async def get_user():
return RedirectResponse('/hub/dashboards-api/hub-info/user')
def main(root_path: str = typer.Option(...), port: int = typer.Option(...)):
uvicorn.run(api, host='0.0.0.0', root_path=root_path, port=port, access_log=True)
if __name__ == '__main__':
typer.run(main)
Describe the solution you'd like
A user json, such as {"kind": "user", "name": "ril"}
Describe alternatives you've considered
Configuration
Using Zero to jupyterhub
Thank you in advance and cheers
I managed to install cdsdashboards and to create new dashboards. Many thanks for creating this tool!
For some reason, some ipywidgets such as dropdown menus and radio buttons are not displayed. I also have a green bar at the top and the widgets that are displayed look a bit odd and not how voila displays them. I attached one screenshot of how the dashboard looks in voila and how it looks with cdsdashboards. Any idea why that is?
Many thanks
Tom
Describe the bug
When installing, pip download all dependencies even those not mandatory (i.e. depending on the spawner):
Installing collected packages: Click, bokeh-root-cmd, pluggy, multidict, yarl, async-timeout, aiohttp, simpervisor, jhsingle-native-proxy, rshiny-server-cmd, pygments, jupyterlab-pygments, pyzmq, jupyter-core, jupyter-client, Send2Trash, pyparsing, packaging, webencodings, bleach, testpath, defusedxml, pandocfilters, nbformat, mistune, nbconvert, wcwidth, prompt-toolkit, parso, jedi, pickleshare, backcall, ptyprocess, pexpect, ipython, ipykernel, terminado, jupyter-server, voila, voila-materialstream, plotlydash-tornado-cmd, websocket-client, docker, escapism, dockerspawner, cdsdashboards
It would be better to use the extras section in setup.
To Reproduce
pip install cdsdashboards
Configuration
N/A
Now that the base DockerSpawner code supports multiple images through DockerSpawner.allowed_images is cdsdashboards.hubextension.spawners.variabledocker.VariableDockerSpawner still needed?
If it is, is it compatible with DockerSpawner.allowed_images?
Looking at the code that I think implements that ... (I'm not good with Mixins) ... is it still needed to enable the override of the invocation command on the docker image?
I want to use relative path in my streamlit demo,
e.g
f = open('abc.log')
but it not works,
I must use it like this, the absolute path
f = open('/home/user/{username}/{streamlit}/abc.log')
so how should i do
Describe the bug
Adding the following to my cdsdashboards_config.py and reloading per the Hub Options: Named Server display documentation does not work.
c.JupyterHub.template_vars = {
'cds_hide_user_named_servers': True,
'cds_hide_user_dashboard_servers': True
}
To Reproduce
nano /opt/tljh/config/jupyterhub_config.d/cdsdashboards_config.py
add code above and save
sudo tljh-config reload
...and log back in as regular user. I expect the New Dashboard option to be hidden but instead I see the following:
Configuration
see above
p.s. Thank you so much for creating this addition to Jupyterhub, it's wonderful!
Multiple conda envs work correctly in Voila since the conda env name is embedded in the ipynb.
For the other frameworks, the user should be able to select from a list of available envs when they create the dashboard.
To try, on TLJH:
source /opt/tljh/user/bin/activate
conda create --name newenv python=3.7
conda activate newenv
conda install -c conda-forge ipykernel
python -m ipykernel install --name "newenv" --display-name "Python [newenv]"
conda install -c conda-forge dash # To show the env is different
If jhsingle-native-proxy adds something like this before the 'real' command:
conda init bash && conda activate newenv && ...
Then there is an error because the conda command 'eats' all of the command line and doesn't like e.g. '-m' of python -m.
simpervisor/process.py is usingasyncio.create_subprocess_exec instead of asyncio.create_subprocess_shell which could be the problem.
So this still needs some investigation.
Is your feature request related to a problem? Please describe.
The hub pages are kind of hidden (except for the login page) for the JupyterLab single user. The creation of a dashboard from a hub page is a bit cryptic for the final user (that have no clue about the software architecture).
Describe the solution you'd like
It would be nice to show the dashboard creation form from within JupyterLab - the must would be a publish button next to the voilà preview.
If you think it is not too hard, I gladly find time next month to create a PoC. Should it be part of this project (like for voila lab preview) or a separate one on my personal account?
Describe alternatives you've considered
N/A
Background context
It is essential to understand the wider context of what you are trying to achieve from ContainDS Dashboards. Please explain your job role, and how/why your team are using the software in the first place when you encounter this need.
Configuration
If specific to a configuration type, include as much jupyterhub_config information as necessary - at least enough to understand which Spawner type you are using, and how your JupyterHub is deployed (e.g. The Littlest JupyterHub, or Zero to JupyterHub).
Hi all,
I have been setting up the cdsdashboard. Everything works perfectly with Chrome.
Checking the dashboard with other browser didn't work
With safari I can only see the markdowns but no widgets and with IE I get a blank white page.
Has anyone else experienced this? Any suggestions on why that is happening?
Many thanks
Tom
Describe the bug
The user group created with a dashboard is not removed when the dashboard is deleted.
To Reproduce
Screenshots
Configuration
Jupyterhub 1.3
cdsdashboards 0.4.2
Where HTTPS termination happens on the load balancer under z2jh on EKS/AWS:
proxy:
https:
enabled: true
type: offload
authing into someone else's dashboard doesn't work - you get '403 auth form must be sent from auth page'.
Same as this issue:
#22
but needs a different solution under z2jh
It is a bug/feature in either JupyterHub's OAuth code or in the Configurable HTTP Proxy component, depending on how you look at it.
Unfortunately, the workaround suggested in the GitHub issue above won't work here because in Zero2JH there is no easy way to specify a bespoke ConfigurableHTTPProxy.command setting to the ['configurable-http-proxy', '--no-x-forward'] which we need, as noted recently here: jupyterhub/zero-to-jupyterhub-k8s#1302
Fixing the z2jh issue 1302 would be ideal in the short term. Using an alternative proxy (Traefik instead of the standard Configurable Http Proxy is supposed to be supported soon) might make this go away: jupyterhub/zero-to-jupyterhub-k8s#1162. Or trying a different https method could work - e.g. getting z2jh to do the https termination.
But all of these have too many moving parts - I will look at building a workaround into the jupyterhub code used by ContainDS Dashboards. I am away this week but should be able to take a look at the start of next. In the meantime, I'm hoping you can continue evaluation with https off or moved to z2jh (if that solves the problem).
This was reported here: https://gitter.im/ideonate/ContainDS?at=5f430cf0ec534f584fb8fd89
Sometimes if you have a more advanced use case you don't start your Panel app via panel serve
.
Instead you start it via python app.py
and the panel.serve
command inside the file. The name app.py
is just an example. It can be the name of any python file. You can find the serve
documentation here.
An example is the app.py
file for awesome-panel.org at https://github.com/MarcSkovMadsen/awesome-panel/blob/master/app.py.
Please support this use case as well. Thanks.
app.py
file.import panel as pn
def app1():
return pn.pane.Markdown("# Hello Panel World")
def app2():
return pn.pane.Markdown("# Hello ContainDS Dashboard World")
ROUTES = {
"panel-app": app1, "cdsdashboard-app": app2
}
pn.config.sizing_mode="stretch_width"
pn.serve(ROUTES, port=80, title="Example")
python app.py'
Is your feature request related to a problem? Please describe.
Once a dashboard starts, it is difficult to pull new updates from git into the dashboard even when restarting.
Loading files can be confusing as you'd expect the working directory to be the repo root.
Describe the solution you'd like
Git source should force re-pull when dashboard restarts.
Working directory should be repo root
Describe alternatives you've considered
One way or another, the behavior needs to be clear to the dashboard creator.
JupyterHubs using DockerSpawner are currently treated differently to any other spawner type (e.g. KubeSpawner or LocalProcessSpawner). This was to provide extra functionality available from Docker - it clones the entire environment from a 'source server' selected by the user. This means any recently installed packages make it into the dashboard server, and also that any files stored in the environment (instead of the user's home folder) are copied into the dashboard server.
While this approach could be useful, in practice it doesn't reflect how JupyterHubs are used. Script files and notebooks are usually stored in the user's home folder which is shared between Jupyter servers - so the dashboard server will normally just access a copy of the scripts on a volume shared between all of that user's servers. Similarly, most packages required by the scripts will be installed into the base Docker image rather than installed only in the container they are currently using.
Dashboard configuration (such as the starting script/notebook and presentation framework type) are baked into the committed Docker image. The dashboard server is then just configured to use this new image, but otherwise runs much like a regular Jupyter server.
The other spawners work differently, doing more work at spawn-time to adjust the commands being run to start the presentation server (e.g. running voila or streamlit instead of jupyter).
My plan is to bring DockerSpawner in line with the other spawners. This will mean a single (simpler) entrypoint will be required in the Docker image, and a new VariableDockerSpawner wrapper class will need to be specified in your JupyterHub instead of the regular DockerSpawner.
The upgrade path should not be difficult, but may introduce breaking changes if this change is introduced without a bit of care at upgrade time.
If anyone is using DockerSpawner I would really like to hear your thoughts - especially if the approach described above does or does not reflect how you tend to use your Jupyter servers and Docker images.
And ideally the UI would be reshaped so they don't see any of the developer-only stuff (e.g. My Server or New Dashboard)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.