PageRenderTime 67ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 0ms

/bokeh/mpl.py

https://gitlab.com/intruxxer/bokeh
Python | 776 lines | 772 code | 4 blank | 0 comment | 3 complexity | 9926acf1b15a92164616d1b0a768b963 MD5 | raw file
  1. import numpy as np
  2. import logging
  3. import urlparse
  4. import requests
  5. import uuid
  6. import bbmodel
  7. import protocol
  8. import data
  9. import os
  10. import dump
  11. import json
  12. import pandas
  13. from exceptions import DataIntegrityException
  14. from bokeh import protocol
  15. from bokeh.utils import get_json
  16. log = logging.getLogger(__name__)
  17. colors = [
  18. "#1f77b4",
  19. "#ff7f0e", "#ffbb78",
  20. "#2ca02c", "#98df8a",
  21. "#d62728", "#ff9896",
  22. "#9467bd", "#c5b0d5",
  23. "#8c564b", "#c49c94",
  24. "#e377c2", "#f7b6d2",
  25. "#7f7f7f",
  26. "#bcbd22", "#dbdb8d",
  27. "#17becf", "#9edae5"
  28. ]
  29. def script_inject(plotclient, modelid, typename):
  30. pc = plotclient
  31. f_dict = dict(
  32. docid = pc.docid,
  33. ws_conn_string = pc.ws_conn_string,
  34. docapikey = pc.apikey,
  35. root_url = pc.root_url,
  36. modelid = modelid,
  37. modeltype = typename,
  38. script_url = pc.root_url + "/bokeh/embed.js")
  39. e_str = '''<script src="%(script_url)s" bokeh_plottype="serverconn"
  40. bokeh_docid="%(docid)s" bokeh_ws_conn_string="%(ws_conn_string)s"
  41. bokeh_docapikey="%(docapikey)s" bokeh_root_url="%(root_url)s"
  42. bokeh_modelid="%(modelid)s" bokeh_modeltype="%(modeltype)s" async="true"></script>
  43. '''
  44. return e_str % f_dict
  45. def script_inject_escaped(plotclient, modelid, typename):
  46. pc = plotclient
  47. f_dict = dict(
  48. docid = pc.docid,
  49. ws_conn_string = pc.ws_conn_string,
  50. docapikey = pc.apikey,
  51. root_url = pc.root_url,
  52. modelid = modelid,
  53. modeltype = typename,
  54. script_url = pc.root_url + "/bokeh/embed.js")
  55. e_str = '''&lt; script src="%(script_url)s" bokeh_plottype="serverconn"
  56. bokeh_docid="%(docid)s" bokeh_ws_conn_string="%(ws_conn_string)s"
  57. bokeh_docapikey="%(docapikey)s" bokeh_root_url="%(root_url)s"
  58. bokeh_modelid="%(modelid)s" bokeh_modeltype="%(modeltype)s" async="true"&gt; &lt;/script&gt;
  59. '''
  60. return e_str % f_dict
  61. class BokehMPLBase(object):
  62. def __init__(self, *args, **kwargs):
  63. if 'plotclient' in kwargs:
  64. self.plotclient = kwargs['plotclient']
  65. def update(self):
  66. if self.plotclient.bbclient:
  67. self.plotclient.bbclient.upsert_all(self.allmodels())
  68. @property
  69. def foo(self):
  70. return "bar"
  71. def _repr_html_(self):
  72. html = self.plotclient.make_html(
  73. self.allmodels(),
  74. model=getattr(self, self.topmodel),
  75. template="basediv.html",
  76. script_paths=[],
  77. css_paths=[]
  78. )
  79. html = html.encode('utf-8')
  80. return html
  81. def script_inject(self):
  82. return script_inject(
  83. self.plotclient, self.plotmodel.id, self.plotmodel.typename)
  84. def htmldump(self, path=None):
  85. """ If **path** is provided, then writes output to a file,
  86. else returns the output as a string.
  87. """
  88. html = self.plotclient.make_html(self.allmodels(),
  89. model=getattr(self, self.topmodel),
  90. template="bokeh.html"
  91. )
  92. if path:
  93. with open(path, "w+") as f:
  94. f.write(html.encode("utf-8"))
  95. else:
  96. return html.encode("utf-8")
  97. class PandasTable(BokehMPLBase):
  98. topmodel = 'pivotmodel'
  99. def __init__(self, pivotmodel, plotclient=None):
  100. super(PandasTable, self).__init__(pivotmodel, plotclient=plotclient)
  101. self.pivotmodel = pivotmodel
  102. if hasattr(self.pivotmodel, 'pandassource'):
  103. self.pandassource = self.pivotmodel.pandassource
  104. def groupby(self, columns):
  105. self.pivotmodel.set('groups', columns)
  106. self.plotclient.bbclient.update(self.pivotmodel)
  107. def agg(self, agg):
  108. self.pivotmodel.set('agg', agg)
  109. self.plotclient.bbclient.update(self.pivotmodel)
  110. def sort(self, sort=None, direction=None):
  111. if sort is None:
  112. sort = []
  113. elif isinstance(sort, basestring):
  114. if direction is None: direction = True
  115. sort = [{'column' : sort, 'direction' : direction}]
  116. else:
  117. if direction is None: direction = [True for x in sort]
  118. sort = [{'column' : s, 'direction' : d} for
  119. s, d in zip(sort, direction)]
  120. self.pivotmodel.set('sort', sort)
  121. self.plotclient.bbclient.update(self.pivotmodel)
  122. def paginate(self, offset, length):
  123. self.pivotmodel.set('offset', offset)
  124. self.pivotmodel.set('length', length)
  125. self.plotclient.bbclient.update(self.pivotmodel)
  126. def data(self):
  127. self.pivotmodel.pull()
  128. return self.pivotmodel.get_data()
  129. def allmodels(self):
  130. models = [self.pivotmodel, self.pivotmodel.pandassource]
  131. return models
  132. class GridPlot(BokehMPLBase):
  133. topmodel = 'gridmodel'
  134. def __init__(self, container, children, title, plotclient=None):
  135. self.gridmodel = container
  136. self.children = children
  137. self.title = title
  138. super(GridPlot, self).__init__(container, children, title,
  139. plotclient=plotclient)
  140. def allmodels(self):
  141. models = [self.gridmodel]
  142. for row in self.children:
  143. for plot in row:
  144. models.extend(plot.allmodels())
  145. return models
  146. class XYPlot(BokehMPLBase):
  147. topmodel = 'plotmodel'
  148. def __init__(self, plot, xdata_range, ydata_range,
  149. xaxis, yaxis, pantool, zoomtool, selectiontool, embedtool,
  150. selectionoverlay, parent, plotclient=None):
  151. super(XYPlot, self).__init__(
  152. plot, xdata_range, ydata_range, xaxis, yaxis,
  153. pantool, zoomtool, selectiontool, selectionoverlay, parent,
  154. plotclient=plotclient)
  155. self.plotmodel = plot
  156. self.xdata_range = xdata_range
  157. self.ydata_range = ydata_range
  158. self.pantool = pantool
  159. self.zoomtool = zoomtool
  160. self.selectiontool = selectiontool
  161. self.embedtool = embedtool
  162. self.selectionoverlay = selectionoverlay
  163. self.xaxis = xaxis
  164. self.yaxis = yaxis
  165. self.parent = parent
  166. self.last_source = None
  167. self.color_index = 0
  168. self.renderers = []
  169. self.data_sources = []
  170. self.update()
  171. def allmodels(self):
  172. models = [self.plotmodel,
  173. self.xdata_range,
  174. self.ydata_range,
  175. self.pantool,
  176. self.zoomtool,
  177. self.selectiontool,
  178. self.embedtool,
  179. self.selectionoverlay,
  180. self.xaxis,
  181. self.yaxis]
  182. models += self.renderers
  183. models += self.data_sources
  184. for source in self.data_sources:
  185. if hasattr(source, 'typename') and \
  186. source.typename == 'PandasPlotSource' and \
  187. hasattr(self, 'pandassource'):
  188. models.append(source.pandassource)
  189. return models
  190. def scatter(self, *args, **kwargs):
  191. kwargs['scatter'] = True
  192. return self.plot(*args, **kwargs)
  193. def plot(self, x, y=None, color=None, data_source=None,
  194. scatter=False):
  195. if data_source and (data_source.typename == 'PandasDataSource'):
  196. if self.plotclient.plot_sources.get(data_source.id):
  197. data_source = self.plotclient.plot_sources.get(data_source.id)
  198. else:
  199. plotsource = self.plotclient.model(
  200. 'PandasPlotSource', pandassourceobj=data_source)
  201. self.plotclient.plot_sources[data_source.id] = plotsource
  202. plotsource.update()
  203. data_source = plotsource
  204. def source_from_array(x, y):
  205. if y.ndim == 1:
  206. source = self.plotclient.make_source(x=x, y=y)
  207. xfield = 'x'
  208. yfields = ['y']
  209. elif y.ndim == 2:
  210. kwargs = {}
  211. kwargs['x'] = x
  212. colnames = []
  213. for colnum in range(y.shape[1]):
  214. colname = 'y' + str(colnum)
  215. kwargs[colname] = y[:,colnum]
  216. colnames.append(colname)
  217. source = self.plotclient.make_source(**kwargs)
  218. xfield = 'x'
  219. yfields = colnames
  220. else:
  221. raise Exception, "too many dims"
  222. return source, xfield, yfields
  223. if not isinstance(x, basestring):
  224. if y is None:
  225. y = x
  226. x = range(len(y))
  227. if isinstance(y, np.ndarray):
  228. source, xfield, yfields = source_from_array(x, y)
  229. else:
  230. source = self.plotclient.make_source(x=x, y=y)
  231. xfield, yfields = ('x', ['y'])
  232. else:
  233. if isinstance(y, np.ndarray):
  234. source, xfield, yfields = source_from_array(x, y)
  235. else:
  236. source = self.plotclient.make_source(x=x, y=y)
  237. xfield, yfields = ('x', ['y'])
  238. else:
  239. xfield = x
  240. if y is None:
  241. raise Exception, 'must specify X and Y when calling with strings'
  242. yfields = [y]
  243. if data_source:
  244. source = data_source
  245. else:
  246. source = self.last_source
  247. self.last_source = source
  248. for yfield in yfields:
  249. if color is None:
  250. use_color = colors[self.color_index % len(colors)]
  251. else:
  252. use_color = color
  253. self.color_index += 1
  254. self.scatter(xfield, yfield, source, use_color)
  255. if not scatter:
  256. self.line(xfield, yfield, source, use_color)
  257. def ensure_source_exists(self, sourcerefs, source, columns):
  258. sources = [x for x in sourcerefs if x['ref']['id'] == source.get('id')]
  259. existed = True
  260. if len(sources) == 0:
  261. sourcerefs.append({'ref' : source.ref(), 'columns' : columns})
  262. existed = False
  263. else:
  264. for col in columns:
  265. if col not in sources[0]['columns']:
  266. sources[0]['columns'].append(col)
  267. existed = False
  268. return existed
  269. def scatter(self, x, y, data_source, color):
  270. update = []
  271. existed = self.ensure_source_exists(
  272. self.xdata_range.get('sources'),
  273. data_source, [x])
  274. if not existed : update.append(self.xdata_range)
  275. existed = self.ensure_source_exists(
  276. self.ydata_range.get('sources'),
  277. data_source, [y])
  278. if not existed : update.append(self.ydata_range)
  279. scatterrenderer = self.plotclient.model(
  280. 'ScatterRenderer',
  281. foreground_color=color,
  282. data_source=data_source.ref(),
  283. xfield=x,
  284. yfield=y,
  285. xdata_range=self.xdata_range.ref(),
  286. ydata_range=self.ydata_range.ref(),
  287. parent=self.plotmodel.ref())
  288. self.renderers.append(scatterrenderer)
  289. if data_source not in self.data_sources:
  290. self.data_sources.append(data_source)
  291. self.plotmodel.get('renderers').append(scatterrenderer.ref())
  292. self.selectiontool.get('renderers').append(scatterrenderer.ref())
  293. update.append(scatterrenderer)
  294. update.append(self.plotmodel)
  295. update.append(self.selectiontool)
  296. update.append(self.embedtool)
  297. update.append(self.selectionoverlay)
  298. if self.plotclient.bbclient:
  299. self.plotclient.bbclient.upsert_all(update)
  300. self.plotclient.show(self.plotmodel)
  301. def line(self, x, y, data_source, color):
  302. update = []
  303. existed = self.ensure_source_exists(
  304. self.xdata_range.get('sources'),
  305. data_source, [x])
  306. if not existed : update.append(self.xdata_range)
  307. existed = self.ensure_source_exists(
  308. self.ydata_range.get('sources'),
  309. data_source, [y])
  310. if not existed : update.append(self.ydata_range)
  311. linerenderer = self.plotclient.model(
  312. 'LineRenderer',
  313. foreground_color=color,
  314. data_source=data_source.ref(),
  315. xfield=x,
  316. yfield=y,
  317. xdata_range=self.xdata_range.ref(),
  318. ydata_range=self.ydata_range.ref(),
  319. parent=self.plotmodel.ref())
  320. self.renderers.append(linerenderer)
  321. if data_source not in self.data_sources:
  322. self.data_sources.append(data_source)
  323. self.plotmodel.get('renderers').append(linerenderer.ref())
  324. update.append(linerenderer)
  325. update.append(self.plotmodel)
  326. update.append(self.selectiontool)
  327. update.append(self.embedtool)
  328. update.append(self.selectionoverlay)
  329. if self.plotclient.bbclient:
  330. self.plotclient.bbclient.upsert_all(update)
  331. self.plotclient.show(self.plotmodel)
  332. class PlotClient(object):
  333. def __init__(self, username=None,
  334. serverloc=None,
  335. userapikey="nokey"):
  336. #the root url should be just protocol://domain
  337. self.username = username
  338. self.root_url = serverloc
  339. self.session = requests.session()
  340. self.session.headers.update({'content-type':'application/json'})
  341. self.session.headers.update({'BOKEHUSER-API-KEY' : userapikey})
  342. self.session.headers.update({'BOKEHUSER' : username})
  343. if self.root_url:
  344. self.update_userinfo()
  345. else:
  346. print 'Not using a server, plots will only work in embedded mode'
  347. self.docid = None
  348. self.models = {}
  349. self.clf()
  350. self._hold = True
  351. self.bbclient = None
  352. self.ic = self.model('PlotContext', children=[])
  353. # Caching pandas plot source so we can be smart about reusing them
  354. self.plot_sources = {}
  355. @property
  356. def ws_conn_string(self):
  357. split = urlparse.urlsplit(self.root_url)
  358. #how to fix this in bokeh and wakari?
  359. if split.scheme == 'http':
  360. return "ws://%s/bokeh/sub" % split.netloc
  361. else:
  362. return "wss://%s/bokeh/sub" % split.netloc
  363. def update_userinfo(self):
  364. url = urlparse.urljoin(self.root_url, '/bokeh/userinfo/')
  365. self.userinfo = get_json(self.session.get(url, verify=False))
  366. def load_doc(self, docid):
  367. url = urlparse.urljoin(self.root_url,"/bokeh/getdocapikey/%s" % docid)
  368. resp = self.session.get(url, verify=False)
  369. if resp.status_code == 401:
  370. raise Exception, 'unauthorized'
  371. apikey = get_json(resp)
  372. if 'apikey' in apikey:
  373. self.docid = docid
  374. self.apikey = apikey['apikey']
  375. print 'got read write apikey'
  376. else:
  377. self.docid = docid
  378. self.apikey = apikey['readonlyapikey']
  379. print 'got read only apikey'
  380. self.models = {}
  381. url = urlparse.urljoin(self.root_url, "/bokeh/bb/")
  382. self.bbclient = bbmodel.ContinuumModelsClient(
  383. docid, url, self.apikey)
  384. interactive_contexts = self.bbclient.fetch(
  385. typename='PlotContext')
  386. if len(interactive_contexts) > 1:
  387. print 'warning, multiple plot contexts here...'
  388. self.ic = interactive_contexts[0]
  389. def make_doc(self, title):
  390. url = urlparse.urljoin(self.root_url,"/bokeh/doc/")
  391. data = protocol.serialize_web({'title' : title})
  392. response = self.session.post(url, data=data, verify=False)
  393. if response.status_code == 409:
  394. raise DataIntegrityException
  395. self.userinfo = get_json(response)
  396. def remove_doc(self, title):
  397. matching = [x for x in self.userinfo['docs'] \
  398. if x.get('title') == title]
  399. docid = matching[0]['docid']
  400. url = urlparse.urljoin(self.root_url,"/bokeh/doc/%s/" % docid)
  401. response = self.session.delete(url, verify=False)
  402. if response.status_code == 409:
  403. raise DataIntegrityException
  404. self.userinfo = get_json(response)
  405. def use_doc(self, name):
  406. self.docname = name
  407. docs = self.userinfo.get('docs')
  408. matching = [x for x in docs if x.get('title') == name]
  409. if len(matching) > 1:
  410. print 'warning, multiple documents with that title'
  411. if len(matching) == 0:
  412. print 'no documents found, creating new document'
  413. self.make_doc(name)
  414. return self.use_doc(name)
  415. docs = self.userinfo.get('docs')
  416. matching = [x for x in docs if x.get('title') == name]
  417. self.load_doc(matching[0]['docid'])
  418. def notebook_connect(self):
  419. import IPython.core.displaypub as displaypub
  420. js = get_template('connect.js').render(
  421. username=self.username,
  422. root_url = self.root_url,
  423. docid=self.docid,
  424. docapikey=self.apikey,
  425. ws_conn_string=self.ws_conn_string
  426. )
  427. msg = """ <p>Connection Information for this %s document, only share with people you trust </p> """ % self.docname
  428. html = self.html(
  429. script_paths=[],
  430. css_paths=[],
  431. js_snippets=[js],
  432. html_snippets=[msg],
  433. template="basediv.html"
  434. )
  435. displaypub.publish_display_data('bokeh', {'text/html': html})
  436. return None
  437. def notebooksources(self):
  438. import IPython.core.displaypub as displaypub
  439. html = self.html(template="basediv.html",
  440. script_paths = dump.notebookscript_paths,
  441. html_snippets=["<p>Bokeh Sources</p>"])
  442. displaypub.publish_display_data('bokeh', {'text/html': html})
  443. return None
  444. def model(self, typename, **kwargs):
  445. if 'client' not in kwargs and self.bbclient:
  446. kwargs['client'] = self.bbclient
  447. model = bbmodel.make_model(typename, **kwargs)
  448. model.set('doc', self.docid)
  449. if hasattr(self, "apikey"):
  450. model.set('script_inject_escaped', script_inject_escaped(
  451. self, model.id, typename))
  452. self.models[model.id] = model
  453. return model
  454. def hold(self, val):
  455. if val == 'on':
  456. self._hold = True
  457. elif val == 'off':
  458. self._hold = False
  459. else:
  460. self._hold = val
  461. def make_source(self, *args, **kwargs):
  462. """call this with either kwargs of vectors, or a pandas dataframe
  463. """
  464. if len(args) > 0:
  465. df = args[0]
  466. model = self.model('PandasDataSource', df=df)
  467. else:
  468. output = data.make_source(**kwargs)
  469. model = self.model(
  470. 'ObjectArrayDataSource',
  471. data=output
  472. )
  473. if self.bbclient:
  474. self.bbclient.create(model)
  475. return model
  476. def _newxyplot(self, title=None, width=300, height=300,
  477. is_x_date=False, is_y_date=False,
  478. container=None):
  479. """
  480. Parameters
  481. ----------
  482. x : string of fieldname in data_source, or 1d vector
  483. y : string of fieldname in data_source or 1d_vector
  484. data_source : optional if x,y are not strings,
  485. backbonemodel of a data source
  486. container : bbmodel of container viewmodel
  487. Returns
  488. ----------
  489. """
  490. plot = self.model('Plot', width=width, height=height)
  491. if self.bbclient:
  492. plot.set('docapikey', self.apikey)
  493. plot.set('baseurl', self.bbclient.baseurl)
  494. if container:
  495. parent = container
  496. plot.set('parent', container.ref())
  497. else:
  498. parent = self.ic
  499. plot.set('parent', self.ic.ref())
  500. if title is not None: plot.set('title', title)
  501. xdata_range = self.model(
  502. 'DataRange1d',
  503. sources=[]
  504. )
  505. ydata_range = self.model(
  506. 'DataRange1d',
  507. sources=[]
  508. )
  509. axisclass = 'LinearAxis'
  510. if is_x_date: axisclass = 'LinearDateAxis'
  511. xaxis = self.model(
  512. axisclass, orientation='bottom', ticks=3,
  513. data_range=xdata_range.ref(), parent=plot.ref())
  514. axisclass = 'LinearAxis'
  515. if is_y_date: axisclass = 'LinearDateAxis'
  516. yaxis = self.model(
  517. axisclass, orientation='left', ticks=3,
  518. data_range=ydata_range.ref(), parent=plot.ref())
  519. pantool = self.model(
  520. 'PanTool',
  521. dataranges=[xdata_range.ref(), ydata_range.ref()],
  522. dimensions=['width', 'height']
  523. )
  524. zoomtool = self.model(
  525. 'ZoomTool',
  526. dataranges=[xdata_range.ref(), ydata_range.ref()],
  527. dimensions=['width', 'height']
  528. )
  529. selecttool = self.model(
  530. 'BoxSelectTool',
  531. renderers=[])
  532. embedtool = self.model(
  533. 'EmbedTool')
  534. selectoverlay = self.model(
  535. 'BoxSelectionOverlay',
  536. tool=selecttool.ref())
  537. plot.set('renderers', [])
  538. plot.set('axes', [xaxis.ref(), yaxis.ref()])
  539. plot.set('tools', [pantool.ref(), zoomtool.ref(), selecttool.ref(), embedtool.ref()])
  540. plot.set('overlays', [selectoverlay.ref()])
  541. output = XYPlot(
  542. plot, xdata_range, ydata_range,
  543. xaxis, yaxis, pantool, zoomtool,
  544. selecttool, embedtool, selectoverlay, parent,
  545. plotclient=self)
  546. return output
  547. def clf(self):
  548. self._plot = None
  549. def clear(self):
  550. self._plot = None
  551. def figure(self):
  552. self._plot = None
  553. def plot_dates(self, *args, **kwargs):
  554. kwargs['is_x_date'] = True
  555. return self.plot(*args, **kwargs)
  556. def scatter(self, *args, **kwargs):
  557. kwargs['scatter'] = True
  558. return self.plot(*args, **kwargs)
  559. def plot(self, x, y=None, color=None, title=None, width=300, height=300,
  560. scatter=False, is_x_date=False, is_y_date=False,
  561. data_source=None, container=None):
  562. if not self._hold:
  563. self.figure()
  564. if not self._plot:
  565. self._plot =self._newxyplot(
  566. title=title,
  567. width=width, height=height,
  568. is_x_date=is_x_date, is_y_date=is_y_date,
  569. container=container
  570. )
  571. self._plot.plot(x, y=y, color=color,
  572. data_source=data_source,
  573. scatter=scatter
  574. )
  575. return self._plot
  576. def pandastable(self, source, sort=[], groups=[],
  577. agg='sum', width=600, offset=0, length=100,
  578. height=400, container=None):
  579. if container is None:
  580. parent = self.ic
  581. else:
  582. parent = container
  583. if isinstance(source, pandas.DataFrame):
  584. source = self.model('PandasDataSource', df=source)
  585. source.update()
  586. table = self.model('PandasPivot',
  587. pandassourceobj=source,
  588. sort=sort, groups=groups,agg=agg,
  589. offset=offset,length=length,
  590. width=width, height=height)
  591. if self.bbclient:
  592. self.bbclient.create(table)
  593. if container is None:
  594. self.show(table)
  595. return PandasTable(table, self)
  596. def table(self, data_source, columns, title=None,
  597. width=600, height=300, container=None):
  598. if container is None:
  599. parent = self.ic
  600. else:
  601. parent = container
  602. table = self.model(
  603. 'DataTable', data_source=data_source.ref(),
  604. columns=columns, parent=parent.ref(),
  605. width=width,
  606. height=height)
  607. if self.bbclient:
  608. self.bbclient.update(table)
  609. if container is None:
  610. self.show(table)
  611. def _add_source_to_range(self, data_source, columns, range):
  612. sources = range.get('sources')
  613. added = False
  614. for source in sources:
  615. if source['ref'] == data_source:
  616. newcolumns = np.unique1d(columns, source['columns']).tolist()
  617. source['columns'] = newcolumns
  618. added = True
  619. if not added:
  620. sources.append({'ref' : data_source.ref(), 'columns' : columns})
  621. def grid(self, plots, title=None):
  622. container = self.model(
  623. 'GridPlotContainer',
  624. parent=self.ic.ref())
  625. if title is not None:
  626. container.set('title', title)
  627. flatplots = []
  628. for row in plots:
  629. for plot in row:
  630. flatplots.append(plot.plotmodel)
  631. for plot in flatplots:
  632. plot.set('parent', container.ref())
  633. plotrefs = [[x.plotmodel.ref() for x in row] for row in plots]
  634. container.set('children', plotrefs)
  635. if self.bbclient:
  636. to_update = [self.ic, container]
  637. to_update.extend(flatplots)
  638. self.bbclient.upsert_all(to_update)
  639. self.show(container)
  640. return GridPlot(container, plots, title, self)
  641. def show(self, plot):
  642. if self.bbclient:
  643. self.ic.pull()
  644. children = self.ic.get('children')
  645. if children is None: children = []
  646. if plot.get('id') not in [x['id'] for x in children]:
  647. children.insert(0, plot.ref())
  648. self.ic.set('children', children)
  649. if self.bbclient:
  650. self.bbclient.update(self.ic)
  651. def clearic(self):
  652. self.ic.set('children', [])
  653. if self.bbclient:
  654. self.bbclient.update(self.ic)
  655. def html(self, script_paths=None,
  656. css_paths=None, js_snippets=[],
  657. html_snippets=[], template="base.html",
  658. ):
  659. import jinja2
  660. if script_paths is None:
  661. script_paths = dump.script_paths
  662. if css_paths is None:
  663. css_paths=dump.css_paths
  664. template = get_template(template)
  665. result = template.render(
  666. rawjs=dump.inline_scripts(script_paths).decode('utf8'),
  667. rawcss=dump.inline_css(css_paths).decode('utf8'),
  668. js_snippets=js_snippets,
  669. html_snippets=html_snippets
  670. )
  671. return result
  672. def make_html(self, all_models, model=None, template="base.html",
  673. script_paths=None, css_paths=None):
  674. if model is None:
  675. model = self.ic
  676. elementid = str(uuid.uuid4())
  677. plot_js = get_template('plots.js').render(
  678. elementid=elementid,
  679. modelid=model.id,
  680. all_models=protocol.serialize_json([x.to_broadcast_json()\
  681. for x in all_models]),
  682. modeltype=model.typename,
  683. )
  684. plot_div = get_template('plots.html').render(
  685. elementid=elementid
  686. )
  687. result = self.html(js_snippets=[plot_js],
  688. template=template,
  689. html_snippets=[plot_div],
  690. script_paths=script_paths,
  691. css_paths=css_paths
  692. )
  693. return result
  694. def htmldump(self, path=None):
  695. """if inline, path is a filepath, otherwise,
  696. path is a dir
  697. """
  698. html = self.make_html(self.models.values())
  699. if path:
  700. with open(path, "w+") as f:
  701. f.write(html.encode("utf-8"))
  702. else:
  703. return html.encode("utf-8")
  704. def get_template(filename):
  705. import jinja2
  706. template = os.path.join(os.path.dirname(__file__),
  707. 'templates',
  708. filename,
  709. )
  710. with open(template) as f:
  711. return jinja2.Template(f.read())
  712. bbmodel.load_special_types()