I am trying to print in console.log() my series variable but no success so far.
def multiple_series(res):
matrix = dict(res)
all_cat_keys = set(key[0] for key in matrix)
categories = sorted(all_cat_keys)
all_series_keys = set(key[1] for key in matrix)
series = [
{
'name': series_key,
'data': [
[cat_key, matrix.get((cat_key, series_key), 0)]
for cat_key in categories
],
}
for series_key in all_series_keys
]
return series
I've tried using json.dumps() and the well known console.log({{ series }}) but I get nothing.
I'd like to print the content of name and data. Can anyone help me ? I don't have much experience using JS. Thanks
console.log({{ series }}) can not work because {{ series }} gets evaluated to text. Therefore JS thinks it's a JS object.
Add quotes around the object to treat it as text and it should work:
console.log("{{ series }}");
Iam not sure about your input data - but you are building the series list the wrong way. You should build you series list like that:
def multiple_series(res):
matrix = dict(res)
all_cat_keys = set(key[0] for key in matrix)
categories = sorted(all_cat_keys)
all_series_keys = set(key[1] for key in matrix)
series = []
for series_key in all_series_keys:
dict_to_add = {'name': series_key}
data_information = []
for cat_key in categories:
data_information.append((cat_key, matrix.get((cat_key, series_key), 0)))
dict_to_add['data'] = data_information
series.append(dict_to_add)
return series
Related
It is to draw a plot graph on a web page using Python and js code.
Here's my python code
from bottle import route, run, request, redirect, template
import pymysql as sql
#route('/print')
def show_print():
db = sql.connect(host='localhost', user='root', password='1234', db='testdb')
if db is None:
return template("index.tpl", text="Non", x_value=[], y_value=[])
else:
query = "select * from testdata;"
cur = db.cursor()
n = cur.execute(query)
tpl_no=[]
tpl_date=[]
tpl_hum=[]
tpl_tmp=[]
for i in range(n):
value = cur.fetchone()
tpl_no.append(value[0])
tpl_hum.append(value[2])
return template("index.tpl", x_value = tpl_no, y_value = tpl_hum)
db.close
And MySQL test data table content:
enter image description here
and plotly.js
<h1> {{x_value}} Hi There</h1>
<h1> {{y_value}} Hi There</h1>
<div id="myPlot" style="width:200;height:800px;"></div>
<script type="text/javascript">
var x_arry = new Array();
var y_arry = new Array();
for(var i = 0; i < {{x_value}}.length; i++){
x_arry.push({{x_value}}[i]);
}
for(var i = 0; i < {{y_value}}.length; i++){
y_arry.push({{y_value}}[i]);
}
var data = [{
x: x_arry,
y: y_arry,
mode: "lines"
}];
var layout = {
xaxis:{title:"X: Number"},
yaxis:{title:"Y: Hum"},
title:"This is my graph"
};
Plotly.newPlot("myPlot", data, layout);
I made it like this. But I can't see the plot graph on the web page
The x_arry value has been output normally in the js code.
However, the plot graph is not drawn on the web page because the y_arry value is not printed. I want to solve this.
I would recommend making two changes to your code.
Firstly, you'll need to convert the Decimal values you are getting from the database to Python floats, by replacing the line
tpl_hum.append(value[2])
with
tpl_hum.append(float(value[2]))
Secondly, the most reliable way to get data from your Python code to your JavaScript code is to get Python to serialize the data as a JSON string, and put this in your template. Try replacing the following line in your Python code
return template("index.tpl", x_value = tpl_no, y_value = tpl_hum)
with
json_data = json.dumps({"x_array": tpl_no, "y_array": tpl_hum})
return template("index.tpl", json_data=json_data)
You'll also need to add import json to your Python code.
The JavaScript within your template then becomes:
var jsonData = {{json_data}};
var data = [{
x: jsonData.x_array,
y: jsonData.y_array,
mode: "lines"
}];
var layout = {
xaxis:{title:"X: Number"},
yaxis:{title:"Y: Hum"},
title:"This is my graph"
};
Plotly.newPlot("myPlot", data, layout);
You don't need the loops to read the data into variables such as x_arry and y_arry. (In fact, it's arguable you wouldn't have needed them before.)
Disclaimer: I haven't tested this.
apologies in advance for unprecise/unappreciated wording as this is my first question here.
Feel free to point out how I can improve it in the future.
I have been reading through all of Bokeh's user guide and various forums but belief this question is still insufficiently covered as it appears over and over again without an answer that can be applied generically.
My task is to construct a scatterplot in Python's Bokeh that can interactively be filtered based on a categorical variable. My limited understanding of Javascript (and how the data is structured) prevents me from figuring this out by myself.
I found, that one solution is to append x&y values that fulfill the condition (f.e. Filtering Bokeh LabelSet with Javascript). However, I want to keep all the other variables as well, since I use them to define graphic parameters / hover information in the plot.
Therefore my question, how can I append whole rows to the new output data if one of the columns fulfills a certain condition in Javascript? I am also unsure if I call the callback correctly such that the plot would actually react to my selection. So please fell free to point out any mistakes here as well.
See some example code here:
#Packages
import pandas as pd
import numpy as np
from bokeh.plotting import figure, output_file, show
import bokeh.events as bev
import bokeh.models as bmo
import bokeh.layouts as bla
#Data
data = pd.DataFrame(data = np.array([[1,1,'a',0.5],
[2,2,'a',0.5],
[3,3,'a',0.75],
[4,4,'b',1],
[5,5,'b',2]]),
columns = ['x', 'y', 'category', 'other information'])
#Setup
output_file('dashboard.html')
source = bmo.ColumnDataSource(data)
#Define dropdown options
dropdown_options = [('All', 'item_1'), None] + [(cat, str('item_' + str(i))) for i, cat in enumerate(sorted(data['category'].unique()), 2)]
#Generate dropdown widget
dropdown = bmo.Dropdown(label = 'Category', button_type = 'default', menu = dropdown_options)
#Callback
callback = bmo.CustomJS(args = dict(source = source),
code = """
var data = source.data;
var cat = cb_obj.value;
if (cat = 'All'){
data = source.data
} else {
var new_data = [];
for (cat i = 0; i <= source.data['category'].length; i++){
if (source.data['category'][i] == cat) {
new_data.push(source.data[][i])
}
}
data = new_data.data
}
source.data = data
source.change.emit();
""")
#Link actions
dropdown.js_on_event(bev.MenuItemClick, callback)
#Plot
p = figure(plot_width = 800, plot_height = 530, title = None)
p.scatter(x = 'x', y = 'y', source = source)
show(bla.column(dropdown, p))
Unsurprisingly, the filter does not work. As said, any help highly appreciated since I do not know how to index whole rows in Javascript and whatever else I am doing wrong.
Best regards,
Oliver
I wrote a solution for your issue. I am no Bokeh expert so I might not know everything but hope that helps to understand what is going on. Some explanation:
You had some syntax errors to start with: at your for loop you used cat i, you probably meant var i
In your if you were assigning All to cat, you need to do the comparison: with either cat == 'All' or cat === 'All'
your cb_obj.value did not work for some reason and was returning undefined. You can check your variables with simple console.log(variableName) and open dev console in the browser to see callbacks in action. I changed your list comprehension to be tuple of the same values instead of (category_name, item_category_number). Now cb_obj.item returns category_name which you can do comparison with.
You should understand what format your data is in, you can do so with console.log(source.data) for example. source.data here is object of arrays (or dictionary of lists if you were to describe that in Python). Because of that you could not push the data the way you did in for loop and also you had a syntax error: source.data[][i] - you won't access what you want with empty bracket. I wrote two functions to handle this functionality. generateNewDataObject creates object of empty arrays that we can append with addRowToAccumulator
The last thing is that I needed were two data_sources. First that we will not do changes on and second that we will modify and use to display on the plot. If we were to modify the first one then after the first filter all other categories would be dropped and we could get them back only by refreshing the page. The 'immutable' data_source allows us to reference it and not lose filtered data in the process.
I hope that helps.
# Packages
import bokeh.events as bev
import bokeh.layouts as bla
import bokeh.models as bmo
import numpy as np
import pandas as pd
from bokeh.plotting import figure, output_file, show
# Data
data = pd.DataFrame(
data=np.array(
[
[1, 1, 'a', 0.5],
[2, 2, 'a', 0.5],
[3, 3, 'a', 0.75],
[4, 4, 'b', 1],
[5, 5, 'b', 2]
]
),
columns=['x', 'y', 'category', 'other information']
)
# Setup
output_file('dashboard.html')
source = bmo.ColumnDataSource(data)
# Define dropdown options
dropdown_options = [
('All', 'All'), None
] + [(cat, cat)
for i, cat in enumerate(sorted(data['category'].unique()), 2)
]
# Generate dropdown widget
dropdown = bmo.Dropdown(label='Category', button_type='default', menu=dropdown_options)
filtered_data = bmo.ColumnDataSource(data)
# Callback
callback = bmo.CustomJS(
args=dict(unfiltered_data=source, filtered_data=filtered_data),
code="""
var data = unfiltered_data.data;
var cat = cb_obj.item;
function generateNewDataObject(oldDataObject){
var newDataObject = {}
for (var key of Object.keys(oldDataObject)){
newDataObject[key] = [];
}
return newDataObject
}
function addRowToAccumulator(accumulator, dataObject, index) {
for (var key of Object.keys(dataObject)){
accumulator[key][index] = dataObject[key][index];
}
return accumulator;
}
if (cat === 'All'){
data = unfiltered_data.data;
} else {
var new_data = generateNewDataObject(data);
for (var i = 0; i <= unfiltered_data.data['category'].length; i++){
if (unfiltered_data.data['category'][i] == cat) {
new_data = addRowToAccumulator(new_data, unfiltered_data.data, i);
}
}
data = new_data;
}
filtered_data.data = data;
filtered_data.change.emit();
"""
)
# Link actions
dropdown.js_on_event(bev.MenuItemClick, callback)
# Plot
p1 = figure(plot_width=800, plot_height=530, title=None)
p1.scatter(x='x', y='y', source=filtered_data)
show(bla.column(dropdown, p1))
Is there any way to return the list of all the built-in filters within the library.
For example:
var caman_Default_List = [];
Caman('myCanvas', function(){
caman_Default_List= this.getAllFilters();
});
For now I'm using this and it works okay:
var filters =
[
"vintage", "lomo", "clarity", "sinCity", "sunrise",
"crossProcess", "orangePeel", "love", "grungy", "jarques", "pinhole",
"oldBoot", "glowingSun", "hazyDays", "herMajesty", "nostalgia",
"hemingway", "concentrate"
];
myList.push(filters[ some filters ]);
Caman("#myCanvas", function(){
this[myList[index]]().render();
});
But I was wondering if there is a way to get the values of the filters without delcaring them customly. (eg. list = [ "vintage", "lomo", ......... ])
I was looking through their docs, but could not find anything helpful for the data you are trying to get. I took a look at their code and came up with the below for you.
I am not sure I would 100% trust the code, because the order of properties might change, but at least it gives you what you wanted.
console.log(Object.keys(Caman.prototype).slice(75, 93))
<script src="https://cdnjs.cloudflare.com/ajax/libs/camanjs/4.1.2/caman.full.min.js"></script>
I used the code below to achieve what I wanted as #AndrewLohr stated:
//Declare lists to store filter names
var list4,list5,list6,list7 = [];
//Get Caman Filters (Samples : "vintage", "clarity", ... )
list4 = (Object.keys(Caman.prototype).slice(75, 93));
list5 = list4.toString().toUpperCase(); //To upper Case as string value (use as Label Button Name)
//Get Caman Filters (Customs : "brightness", "saturation")
list6 = Object.keys(Caman.prototype).slice(45, 55);
//Add some more elements in the list
list6.push("clip", "stuckBlur", "exposure", "noise", "sharpen");
list7 = list6.toString().toUpperCase(); //To upper Case as string value (use as Slider Name)
//Print lists
console.log(list4);console.log(list5);console.log(list6);console.log(list7);
<script src="https://cdnjs.cloudflare.com/ajax/libs/camanjs/4.1.2/caman.full.min.js"></script>
I already try with no success to sort by sequence a dict of records by jquery I don't know where sorted again by name.
I ask the community on git but nobody answer me, I'm trying to sort by odoo sequence. using modules web_widget_x2many_2d_matrix, and sale_order_variant_mgmt
I modify python code, and if I debug the list of records the sort is the intended, but when the javascript code is loaded, it sorted by name and cant debug where the problem is
#api.onchange('product_tmpl_id')
def _onchange_product_tmpl_id(self):
self.variant_line_ids = [(6, 0, [])]
template = self.product_tmpl_id
context = self.env.context
record = self.env[context['active_model']].browse(context['active_id'])
if context['active_model'] == 'sale.order.line' or context['active_model'] == 'sale.order.line_group': #TODO check this modify for lentex group_sale_lines module
sale_order = record.order_id
else:
sale_order = record
num_attrs = len(template.attribute_line_ids)
if not template or not num_attrs:
return
line_x = template.attribute_line_ids[0]
line_y = False if num_attrs == 1 else template.attribute_line_ids[1]
lines = []
for value_x in line_x.value_ids.sorted(key=lambda r: r.sequence):
for value_y in line_y and line_y.value_ids.sorted(key=lambda r: r.sequence) or [False]: #I modify this and in python the sort is the intended, but not in JS
# Filter the corresponding product for that values
values = value_x
if value_y:
values += value_y
product = template.product_variant_ids.filtered(lambda x: not(values - x.attribute_value_ids))[:1]
order_line = sale_order.order_line.filtered(lambda x: x.product_id == product)[:1]
lines.append((0, 0, {
'product_id': product,
'disabled': not bool(product),
'value_x': value_x,
'value_y': value_y,
'product_uom_qty': order_line.product_uom_qty,
}))
self.variant_line_ids = lines
I think the problem is here
// get x axis values in the correct order
get_x_axis_values: function()
{
return _.keys(this.by_x_axis); //I think here is where the order is defined
},
// get y axis values in the correct order
get_y_axis_values: function()
{
return _.keys(this.by_y_axis); //I think here is where the order is defined
},
It looks like your sorting the dictionary, but dictionaries don't have or maintain an order.
Create a temporary list to hold the key values in order based on the value then iterate through that list to handle the dictionary values in the desired order.
alternatively you could use an "OrderedDict" in python
from collections import OrderedDict
After reading and testing since few days (And already post here but with a wrong question) I really need you because I fail again and again...
My goal : having many series on same charts (and many charts in the future)
My data source : a mysql and a json output :
"[{"name":"Station 1","data":"1360191600,398.625"},{"name":"Station 1","data":"1360192500,398.625"},{"name":"Station 1","data":"1360193400,398.25"},{"name":"Station 1","data":"1360194300,397.375"},{"name":"Station 1","data":"1360195200,397.5"},{"name":"Station 1","data":"1360196100,397.5"},{"name":"Station 1","data":"1360199700,396.75"},{"name":"Station 1","data":"1360200600,397"}...
These data are an example because in normal time I have many station and some with only the timestamp data.
My big fail at this moment is to send these information to the series:[] option.
I have try some loops like this :
data=JSON.parse(data) ;
var series = [];
series.data = [];
$.each(data,function(i,ligne) {
var string = JSON.stringify(ligne);
var obj = $.parseJSON(string);
//var index_serie = obj.name.slice(0,1) ;
console.log(obj) ;
points=(obj.data) ;
series.name=obj.name ;
series.data.push(points) ;
}) ;
options.series.push(series) ;
console.log(options.series) ;
var chart = new Highcharts.Chart(options);
The options of the chart are defined before the ajax call.
I have try with a other format of json like ["name":"station1","data":"[1321654,10],[5465... but I have difficulties to add the [] in my sql quesry and the GROUP_CONCAT have some limitation (2014 character)
So help me to create a nice loop in order to render multiple series with their name etc
Thanks for your help...
Can't you change that "almost" JSON to "another" JSON? Maybe something like this:
[{
"name":'station 1',
"data": [ [360191600,398.625], [360191600,398.625], [360191600,398.625] ... [360191600,398.625] ]
}, {
"name":'station 2',
"data": [ [360191600,398.625], [360191600,398.625], [360191600,398.625] ... [360191600,398.625] ]
}]
If not, you have to add some parsing for your values, example:
data = JSON.parse(data);
var names = [];
$.each(data, function (i, ligne) {
var ind = names.indexOf(ligne.name),
splited = ligne.data.split(','),
x = parseFloat(splited[0]),
y = parseFloat(splited[1]);
if (ind == -1) {
/*series name spotted first time need to add new series */
ind = names.push(ligne.name) - 1;
options.series.push({
data: [],
name: ligne.name
});
}
if(!isNaN(x) && !isNaN(y)){
options.series[ind].data.push([x,y]);
}
});
And working jsfiddle: http://jsfiddle.net/3bQne/40/