I am attempting to learn about implementing bokeh custom extensions to create some widgets that I would like for my data project. I was attempting to follow along with this example here but I am having trouble running the example extensions that bokeh has provided. I am currently getting back an error of
ValueError: expected a subclass of HasProps, got class 'bokeh.models.sources.ColumnDataSource'
which is being caused when the DrawTool class calls source = Instance(ColumnDataSource) on line 80. I am not sure exactly what I am doing wrong right now but my first thought is it has something to do with the IDE I'm using? I'm currently using spyder(python 3.6) and have the most recent bokeh update 2.1.0.
This is my first experience with bokeh extensions so I am fairly clueless and I've been scouring the web for help but can't really find much. My current code is exactly what they have on the examples site but I will post it here for convenience,
from bokeh.core.properties import Instance
from bokeh.io import output_file, show
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure
from bokeh.util.compiler import TypeScript
output_file('tool.html')
TS_CODE = """
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
import {ColumnDataSource} from "models/sources/column_data_source"
import {PanEvent} from "core/ui_events"
import * as p from "core/properties"
export class DrawToolView extends GestureToolView {
model: DrawTool
//this is executed when the pan/drag event starts
_pan_start(_ev: PanEvent): void {
this.model.source.data = {x: [], y: []}
}
//this is executed on subsequent mouse/touch moves
_pan(ev: PanEvent): void {
const {frame} = this.plot_view
const {sx, sy} = ev
if (!frame.bbox.contains(sx, sy))
return
const x = frame.xscales.default.invert(sx)
const y = frame.yscales.default.invert(sy)
const {source} = this.model
source.get_array("x").push(x)
source.get_array("y").push(y)
source.change.emit()
}
// this is executed then the pan/drag ends
_pan_end(_ev: PanEvent): void {}
}
export namespace DrawTool {
export type Attrs = p.AttrsOf<Props>
export type Props = GestureTool.Props & {
source: p.Property<ColumnDataSource>
}
}
export interface DrawTool extends DrawTool.Attrs {}
export class DrawTool extends GestureTool {
properties: DrawTool.Props
__view_type__: DrawToolView
constructor(attrs?: Partial<DrawTool.Attrs>) {
super(attrs)
}
tool_name = "Drag Span"
icon = "bk-tool-icon-lasso-select"
event_type = "pan" as "pan"
default_order = 12
static init_DrawTool(): void {
this.prototype.default_view = DrawToolView
this.define<DrawTool.Props>({
source: [ p.Instance ],
})
}
}
"""
class DrawTool(Tool):
__implementation__ = TypeScript(TS_CODE)
source = Instance(ColumnDataSource)
source = ColumnDataSource(data=dict(x=[], y=[]))
plot = figure(x_range=(0, 10), y_range=(0, 10), tools=[DrawTool(source=source)])
plot.title.text = "Drag to draw on the plot"
plot.line('x', 'y', source=source)
show(plot)
Your code works just fine for me with Bokeh 2.1.0. And is should not have anything to do with the IDE because it should not affect the runtime properties of your code. To be sure, you can run the script with Python manually.
If that still gives you the error, then maybe your Bokeh installation is corrupted. After all bokeh.models.sources.ColumnDataSource is a subclass of HasProps. Try creating the virtual environment from scratch.
Here's what I see when I run your code and interact with the plot a bit:
Just in case anyone runs into this issue in the future I thought I would post what I did here for anyone who is new to creating custom extensions and is having trouble finding a solution.
It turned out that my bokeh installation had corrupted as pointed out by Eugene. I went ahead and did a full rebuild of bokeh following Bokehs documents at Getting Started. After forking the latest bokeh repository I then ran into an issue with the anaconda environment trying to run this step where I found some Openssl corrupt files that were preventing me from creating an environment.
I found this fix on GitHub which worked for me with a thread of other possible solutions. After following the rest of the steps I successfully got the custom extensions running locally.
TLDR: The issue was a corrupt bokeh installation. If you are new to this and plan on doing custom extensions or using bokeh models, fork and clone a local build of bokeh rather than relying on the quick installation method.
Related
I'm stuck with following problem while creating my Flask based blog.
Firstly, I used CKeditor 4 but than upgraded to 5 version.
I can't understand how to handle image upload on server side now, with adapters etc. As for 4 version, I used flask-ckeditor extension and Flask documentation to handle image uploading.
I didn't find any examples for this combination. I understand that I lack knowledge and I'm looking for advice in which direction may I advance and which concepts should I know to approach such subject.
Thanks in advance.
My takes on this so far:
According to https://ckeditor.com/docs/ckeditor5/latest/features/image-upload/simple-upload-adapter.html
(official guide on simplest adapters).
config.simpleUpload.uploadUrl should be like /upload route that was used in cke4. Object with URL property needed by cke5 is cke4's upload_successful which was returned by /upload route.
So I figured it out.
As for cke 4:
/upload route handled uploading process by returning upload_successful() from flask-ckeditor extension.
upload_successful() itself is a jsonify-ing function, which in turn modify arguments to fit json format.
As for cke 5:
There were some things aside upload handling, which caused problems.
Plugin used: "Simple upload adapter"
I integrated cke5 by downloading from Online-builder and then reinstalling and rebuilding it by myself. (for this on Ubuntu 20.04 I installed nodejs and npm by sudo apt install.) Plugin is installed by executing from /static/ckeditor folder:
npm install
npm install --save #ckeditor/ckeditor5-upload
npm run build (need to wait here for a little)
Different adapters may conflict and not allow Editor to load, so I removed CKFinder adapter from src/ckeditor.js in import and .builtinPlugins sections, replacing them by import SimpleUploadAdapter from '#ckeditor/ckeditor5-upload/src/adapters/simpleuploadadapter.js'; and SimpleUploadAdapter correspondingly.
.html, where CKEditor instance is created. body here is name of flask_wtf text-field:
<script>
ClassicEditor
.create( document.querySelector( '#body' ), {
extraPlugins: ['SimpleUploadAdapter'],
simpleUpload: {
uploadUrl: '/upload',
},
mediaEmbed: {previewsInData: true}
} )
.catch( error => {
console.error( error.stack );
} );
</script>
Things to notice:
In official guide plugins are recommended to enable as following:
.create( document.querySelector( '#editor' ), {
plugins: [ Essentials, Paragraph, Bold, Italic, Alignment ],
For me it is not working: Editor would not load with such syntaxis. What worked is this (from docs):
.create( document.querySelector( '#body' ), {
extraPlugins: ['SimpleUploadAdapter'],
So, plugins -> extraPlugins and PluginName -> 'PluginName'.
/upload route itself:
#main.route('/files/<path:filename>')
def uploaded_files(filename):
app = current_app._get_current_object()
path = app.config['UPLOADED_PATH']
return send_from_directory(path, filename)
#main.route('/upload', methods=['POST'])
def upload():
app = current_app._get_current_object()
f = request.files.get('upload')
# Add more validations here
extension = f.filename.split('.')[-1].lower()
if extension not in ['jpg', 'gif', 'png', 'jpeg']:
return upload_fail(message='Image only!')
f.save(os.path.join(app.config['UPLOADED_PATH'], f.filename))
url = url_for('main.uploaded_files', filename=f.filename)
return jsonify(url=url)
I will edit this answer as I advance in this subject.
I'm looking to deploy a small data collection app that allows users to record themselves saying some phrases.
Building this with streamlit.
I can get it to work locally, but can't seem to find a solution that works in the Streamlit-Shearing service.
Any ideas?
I do not fully understand your question I think this might help.
import streamlit as st
from bokeh.models.widgets import Button
from bokeh.models import CustomJS
from streamlit_bokeh_events import streamlit_bokeh_events
stt_button = Button(label="Speak", width=100)
stt_button.js_on_event("button_click", CustomJS(code="""
var recognition = new webkitSpeechRecognition();
recognition.continuous = true;
recognition.interimResults = true;
recognition.onresult = function (e) {
var value = "";
for (var i = e.resultIndex; i < e.results.length; ++i) {
if (e.results[i].isFinal) {
value += e.results[i][0].transcript;
}
}
if ( value != "") {
document.dispatchEvent(new CustomEvent("GET_TEXT", {detail: value}));
}
}
recognition.start();
"""))
result = streamlit_bokeh_events(
stt_button,
events="GET_TEXT",
key="listen",
refresh_on_update=False,
override_height=75,
debounce_time=0)
if result:
if "GET_TEXT" in result:
st.write(result.get("GET_TEXT"))`
I have been working on implementing a custom component that enables recording audio from the client’s microphone in apps that are deployed on the web (Streamlit-Audio-Recorder). This version of the component supports downloading the recorded audio but not directly returning audio data to streamlit/Python.
I am trying to write an Ember CLI application that talks to a REST api developed using Django-Rest-Framework.
I tried to ember-django-adapter as my data adpater for the ember application, however I cannot find a sample code on how to configure and write a model to use this data adapter. Can someone please help.
This is the EDA code https://github.com/dustinfarris/ember-django-adapter.
Also all I did on the ember app side is to create new app, and change the config as recommended here http://dustinfarris.com/ember-django-adapter/configuring/:
if (environment === 'development') {
ENV.APP.API_HOST = 'http://localhost:8000';
}
if (environment === 'production') {
ENV.APP.API_HOST = 'https://api.myproject.com';
ENV.APP.API_NAMESPACE = 'v2';
}
but this doc, doesn't say how to configure the data adapter for ember! Please let me know if there is a way to make ember js and django-rest-framework talk.
Thanks.
Before using Ember-data, I would suggest you to create a basic Ajax call using jQuery.
Step 1 (basic AJAX call with jQuery):
route.js:
model() {
return Ember.$.getJSON("/api/v1/foo");
}
Step 2 (Create the model foo with the correct adapter using ActiveModelAdapter):
models/foo.js:
import DS from 'ember-data';
var attr = DS.attr;
export default DS.Model.extend({
bar: attr('string'),
isTest: attr('boolean')
});
adapters/foo.js:
import DS from 'ember-data';
import config from 'shippo-frontend/config/environment';
import Ember from 'ember';
export default DS.ActiveModelAdapter.extend({
namespace: 'api/v1',
host: apiUrl
});
Step 3 (replace your jQuery call by the Ember-data call):
route.js:
model() {
return this.get('store').findAll('foo');
}
Notes:
active-model-adapter allows you to transform your snake_keys into camelCaseKeys. https://github.com/ember-data/active-model-adapter
If you need to do other modifications on the data coming from Django, create a serializers/foo.js and play around with the payload.
is it possible to show HTML code like tables with css style instead of json/csv/text/whatever?
I tried to send html as string but it just inserts html like raw text
Thanks in advance!
This is an old question, but I just went through this and wanted to share my solution in case anyone else is going through it as well.
What I ended up having to do is shim the rest_framework.compat.apply_markdown function to enable the tables extension. At the end of my settings.py, I added the following:
# Monkey patch rest_framework's markdown rendering function, to enable the
# tables extension.
import markdown
import rest_framework.compat
def apply_markdown(text):
"""
Simple wrapper around :func:`markdown.markdown` to set the base level
of '#' style headers to <h2>.
"""
extensions = ['markdown.extensions.toc', 'markdown.extensions.tables']
extension_configs = {
'markdown.extensions.toc': {
'baselevel': '2'
}
}
md = markdown.Markdown(
extensions=extensions, extension_configs=extension_configs
)
return md.convert(text)
rest_framework.compat.apply_markdown = apply_markdown
In this case I'm using DRF 3.6.4 and markdown 2.6.9. In the original rest_framework.compat.apply_markdown function there's some code that sets different options based on the version of markdown, but I omitted that in the shim.
Also note that the default tables extension may not give you tables styled the way you want. I ended up copying markdown/extensions/tables.py into a new module and adding class="table" to the table element. The source for that change is in a gist. For more information about the limited table syntax in markdown see this thread.
For djangorestframework>=3.7 update the default view description function.
https://www.django-rest-framework.org/api-guide/settings/#view_description_function
REST_FRAMEWORK = {
# Module path to a callable which should have a signature (self, html=False)
'VIEW_DESCRIPTION_FUNCTION': 'app_name.view_description.get_view_description',
}
view_description.py
import markdown
from django.utils.encoding import smart_text
from django.utils.html import escape
from django.utils.safestring import mark_safe
from rest_framework.compat import (
HEADERID_EXT_PATH, LEVEL_PARAM, md_filter_add_syntax_highlight
)
from rest_framework.utils import formatting
TABLE_EXTENSION_PATH = 'markdown.extensions.tables'
def _apply_markdown(text):
extensions = [HEADERID_EXT_PATH, TABLE_EXTENSION_PATH]
extension_configs = {
HEADERID_EXT_PATH: {
LEVEL_PARAM: '2'
}
}
md = markdown.Markdown(
extensions=extensions, extension_configs=extension_configs
)
md_filter_add_syntax_highlight(md)
return md.convert(text)
def get_view_description(view_cls, html=False):
description = view_cls.__doc__ or ''
description = formatting.dedent(smart_text(description))
if html:
return mark_safe(_apply_markdown(description))
return description
in your settings.py use renderer classes.
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.AdminRenderer',
],
}
Have you installed the markdown and django-filter packages? These were required to get our HTML browsing capability working on a recent project.
sudo pip install markdown
sudo pip install django-filter
I'm trying to use Bokeh's 'create_html_snippet' method to present pandas/seaborn plots in a webpage.
As a test I cloned #rpazyaquian's demo repo here - https://github.com/rpazyaquian/bokeh-flask-tutorial/wiki/Rendering-Bokeh-plots-in-Flask. Unfortunately, it doesn't work due to a change in bokeh since the repo was created.
The main HTML output on the page is exactly the same, except obviously the unique bokeh file name is different.
The only difference overall is in the bokeh-generated *.embed.js file. As shown in the excerpts below, the host / static path variables in that file don't seem to work properly. Tested with both the built-in Flask dev server and with gunicorn.
Here's the top part of the working version on #rpazyaquian's Heroku site:
var host = "";
var staticRootUrl = "http://localhost:5006/bokeh/static/";
if (host!=""){
staticRootUrl = host + "/static/";
var bokehJSUrl = window.location.protocol + "//" + staticRootUrl + "js/bokeh.js";
}
else {
bokehJSUrl = staticRootUrl +"js/bokeh.js";
}
Here's the not-working local version, using Heroku 0.4:
var host = "";
var staticRootUrl = "http://localhost:5006/bokeh/static/";
if (host!=""){
staticRootUrl = "//" + host + "/bokehjs/static/";
var bokehJSUrl = staticRootUrl + "js/bokeh.js";
}
else {
bokehJSUrl = staticRootUrl +"js/bokeh.js";
}
Obvious issues:
The host isn't http://localhost:5006, it's http://127.0.0.1:5000/ when using the dev server or whatever you choose if using gunicorn / nginx
I don't know where the 'bokehjs' folder is being set - but it doesn't seem relevant here.
Any ideas on what has changed in Bokeh that could fix this? Or alternatively anyone who's successfully serving Bokeh plots through Flask templates using an alternative method - let me know if there's a better route. I'm just trying to return the HTML snippet for a chart to output into a template.
UPDATE: this function has been deprecated. Please now use the much better bokeh.embed module:
https://github.com/ContinuumIO/bokeh/blob/master/bokeh/embed.py
You can see some examples of its use at:
https://github.com/ContinuumIO/bokeh/tree/master/examples/embed