<jupyter_start><jupyter_text>Host Annotable web app as Bokeh serverMake a bokeh plot in a Jupyter notebook and serve it up to a remote browser.ToDo:----- Make a basic `bokeh` plot servable- Make a `holoviews` plot servable- Make a `hvplot` with gridded data and a pen servable- Test whether doodles are synched back into the Notebook  Setup The following requirements are needed for serving up Bokeh plots from JupyterHub`$ pip install nbserverproxy && jupyter serverextension enable --py nbserverproxy`  Basic `bokeh` plot served from Jupyter Lab  `holoviews` plot served from Jupyter Lab<jupyter_code>import hvplot as hplt
import xarray as xr
import hvplot.xarray
import holoviews as hv
import geoviews as gv
import cartopy.crs as ccrs
import cartopy.feature as cf
from holoviews.streams import FreehandDraw
air_temp = xr.tutorial.open_dataset('air_temperature').load().air.isel(time=0)
print("Loaded dataset")
coastlines = gv.feature.coastline

proj = ccrs.Orthographic(-90, 30)

air_plot = air_temp.hvplot.quadmesh(
   'lon', 'lat', projection=proj, project=True, global_extent=True,
    width=800, height=720, cmap='viridis', rasterize=True, dynamic=False)

print("Made plot elements")
warm_front = gv.Path([]).opts(color='red', line_width=9)
warm_front_pen = FreehandDraw(source=warm_front)

warning_orange = gv.Polygons([]).opts(line_color='orange', line_width=9, 
                                      fill_color='orange', fill_alpha=0.6)
warning_orange_tool = FreehandDraw(source=warning_orange)

print("Made pen elements")
plot = air_plot * coastlines * warm_front * warning_orange

print("Combined elements")<jupyter_output><empty_output><jupyter_text>Deploy Jupyter notebook as `bokeh.server`<jupyter_code>doc = hv.renderer('bokeh').server_doc(plot)
doc.title = 'Annotable Bokeh App - From Jupyter Notebook'

print("Serving document")<jupyter_output><empty_output><jupyter_text>To run this notebook as a Bokeh app, run the following in a terminal:`$ bokeh serve --show bokeh_server.ipynb`  Deploy plot as a `bokeh.app`<jupyter_code>renderer = hv.renderer('bokeh')
print(renderer)
app = renderer.app(air_plot * coastlines * warm_front)
print(app)
from bokeh.server.server import Server

# server = Server({'/app': app}, port=0)
server = Server({'/app': app}, port=7777, allow_websocket_origin=['pangeo.informaticslab.co.uk'])
server.start()
server.show('/app')
warm_front_pen.element.data
server.stop()
warm_front_pen.element.data
warm_front_pen.element<jupyter_output><empty_output><jupyter_text>Define annotable function<jupyter_code>def make_annotable(plot, port=0, websocket_origin='pangeo.informaticslab.co.uk', url_path='annotable'):
    import holoviews as hv
    from bokeh.server.server import Server
    import os
    from IPython.core.display import display, HTML
    import ipywidgets as widgets
    
    renderer = hv.renderer('bokeh')
    app = renderer.app(plot)
    server = Server({f'/{url_path}': app}, port=port, allow_websocket_origin=[websocket_origin])
    
    server.start()
    
    prefix = os.environ['JUPYTERHUB_SERVICE_PREFIX']
    url = f"https://{websocket_origin}{prefix}proxy/{server.port}/{url_path}"
    display(HTML(f'<a href={url}>{url}</a>'))
    
    stop_button = widgets.Button(description="Stop Annotable")
    stop_button.on_click(lambda b: server.stop())
    display(stop_button)
    
    return server
annotable = make_annotable(plot)
annotable.start()
warm_front_pen.element()
warning_orange_tool.element()
annotable.stop()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11396011396011396, 'fraction_numerical': 0.005982905982905983, 'mean_word_length': 5.513914656771799, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '490128', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1246, 'n_tokens_neox': 1195, 'n_words': 279, 'path': 'bokeh_server.ipynb', 'repo_name': 'informatics-lab/annotable-bokeh', 'size': '3510'}