aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZachary Sloan2012-10-02 17:24:09 -0500
committerZachary Sloan2012-10-02 17:24:09 -0500
commitfe9082941a9c3a7815fb986a4717144f3ae7c021 (patch)
treece0196b4567d5a5c7ef639f5360870884ba962af
parente5ba48c06a76ac1d4d4725cf633de1cf3abfde2d (diff)
downloadgenenetwork2-fe9082941a9c3a7815fb986a4717144f3ae7c021.tar.gz
Finished getting the dropdowns for the black samples by attribute value function working
-rw-r--r--wqflask/wqflask/show_trait/SampleList.py4
-rwxr-xr-xwqflask/wqflask/show_trait/show_trait.py103
-rw-r--r--wqflask/wqflask/static/new/javascript/show_trait.coffee28
-rw-r--r--wqflask/wqflask/static/new/javascript/show_trait.js236
-rw-r--r--wqflask/wqflask/templates/show_trait.html16
-rw-r--r--wqflask/wqflask/views.py43
6 files changed, 361 insertions, 69 deletions
diff --git a/wqflask/wqflask/show_trait/SampleList.py b/wqflask/wqflask/show_trait/SampleList.py
index 62bc63c3..d39559d3 100644
--- a/wqflask/wqflask/show_trait/SampleList.py
+++ b/wqflask/wqflask/show_trait/SampleList.py
@@ -24,7 +24,7 @@ class SampleList(object):
self.sample_list = [] # The actual list
- self.calc_attributes()
+ self.get_attributes()
print("camera: attributes are:", pf(self.attributes))
@@ -85,7 +85,7 @@ class SampleList(object):
sample.outlier = False
- def calc_attributes(self):
+ def get_attributes(self):
"""Finds which extra attributes apply to this dataset"""
#ZS: Id and name values for this trait's extra attributes
diff --git a/wqflask/wqflask/show_trait/show_trait.py b/wqflask/wqflask/show_trait/show_trait.py
index 743e4ad6..9bad6154 100755
--- a/wqflask/wqflask/show_trait/show_trait.py
+++ b/wqflask/wqflask/show_trait/show_trait.py
@@ -161,7 +161,10 @@ class ShowTrait(templatePage):
self.sample_group_types['primary_only'] = fd.RISet + " Only"
self.sample_group_types['other_only'] = "Non-" + fd.RISet
self.sample_group_types['all_cases'] = "All Cases"
- self.js_data = dict(sample_groups = self.sample_group_types)
+ js_data = dict(sample_groups = self.sample_group_types,
+ attribute_names = self.sample_groups[0].attributes)
+ print("js_data:", pf(js_data))
+ self.js_data = js_data
def get_this_trait(self):
@@ -989,55 +992,55 @@ class ShowTrait(templatePage):
except:
plotTitle = str(this_trait.name)
- #normalplot_img = BasicStatisticsFunctions.plotNormalProbability(vals=vals, RISet=fd.RISet, title=plotTitle, specialStrains=specialStrains)
- #normalplot.append(HT.TR(HT.TD(normalplot_img)))
- #normalplot.append(HT.TR(HT.TD(HT.BR(),HT.BR(),"This plot evaluates whether data are \
- #normally distributed. Different symbols represent different groups.",HT.BR(),HT.BR(),
- #"More about ", HT.Href(url="http://en.wikipedia.org/wiki/Normal_probability_plot",
- # target="_blank", text="Normal Probability Plots"), " and more about interpreting these plots from the ", HT.Href(url="/glossary.html#normal_probability", target="_blank", text="glossary"))))
- #normalplot_container.append(normalplot)
- #normalplot_div.append(normalplot_container)
- #stats_container.append(normalplot_div)
-
- #boxplot_div = HT.Div(id="statstabs-2")
- #boxplot_container = HT.Paragraph()
- #boxplot = HT.TableLite(cellspacing=0, cellpadding=0, width="100%")
- #boxplot_img, boxplot_link = BasicStatisticsFunctions.plotBoxPlot(vals)
- #boxplot.append(HT.TR(HT.TD(boxplot_img, HT.P(), boxplot_link, align="left")))
- #boxplot_container.append(boxplot)
- #boxplot_div.append(boxplot_container)
- #stats_container.append(boxplot_div)
-
-
- #barName_div = HT.Div(id="statstabs-3")
- #barName_container = HT.Paragraph()
- #barName = HT.TableLite(cellspacing=0, cellpadding=0, width="100%")
- #barName_img = BasicStatisticsFunctions.plotBarGraph(identification=fd.identification, RISet=fd.RISet, vals=vals, type="name")
- #barName.append(HT.TR(HT.TD(barName_img)))
- #barName_container.append(barName)
- #barName_div.append(barName_container)
- #stats_container.append(barName_div)
- #
- #barRank_div = HT.Div(id="statstabs-4")
- #barRank_container = HT.Paragraph()
- #barRank = HT.TableLite(cellspacing=0, cellpadding=0, width="100%")
- #barRank_img = BasicStatisticsFunctions.plotBarGraph(identification=fd.identification, RISet=fd.RISet, vals=vals, type="rank")
- #barRank.append(HT.TR(HT.TD(barRank_img)))
- #barRank_container.append(barRank)
- #barRank_div.append(barRank_container)
- #stats_container.append(barRank_div)
-
- # stats_cell.append(stats_container)
- #
- #stats_script.append(stats_script_text)
- #
- #submitTable = HT.TableLite(cellspacing=0, cellpadding=0, width="100%", Class="target2")
- #stats_row.append(stats_cell)
-
- #submitTable.append(stats_row)
- #submitTable.append(stats_script)
-
- #title2Body.append(submitTable)
+ #normalplot_img = BasicStatisticsFunctions.plotNormalProbability(vals=vals, RISet=fd.RISet, title=plotTitle, specialStrains=specialStrains)
+ #normalplot.append(HT.TR(HT.TD(normalplot_img)))
+ #normalplot.append(HT.TR(HT.TD(HT.BR(),HT.BR(),"This plot evaluates whether data are \
+ #normally distributed. Different symbols represent different groups.",HT.BR(),HT.BR(),
+ #"More about ", HT.Href(url="http://en.wikipedia.org/wiki/Normal_probability_plot",
+ # target="_blank", text="Normal Probability Plots"), " and more about interpreting these plots from the ", HT.Href(url="/glossary.html#normal_probability", target="_blank", text="glossary"))))
+ #normalplot_container.append(normalplot)
+ #normalplot_div.append(normalplot_container)
+ #stats_container.append(normalplot_div)
+
+ #boxplot_div = HT.Div(id="statstabs-2")
+ #boxplot_container = HT.Paragraph()
+ #boxplot = HT.TableLite(cellspacing=0, cellpadding=0, width="100%")
+ #boxplot_img, boxplot_link = BasicStatisticsFunctions.plotBoxPlot(vals)
+ #boxplot.append(HT.TR(HT.TD(boxplot_img, HT.P(), boxplot_link, align="left")))
+ #boxplot_container.append(boxplot)
+ #boxplot_div.append(boxplot_container)
+ #stats_container.append(boxplot_div)
+
+
+ #barName_div = HT.Div(id="statstabs-3")
+ #barName_container = HT.Paragraph()
+ #barName = HT.TableLite(cellspacing=0, cellpadding=0, width="100%")
+ #barName_img = BasicStatisticsFunctions.plotBarGraph(identification=fd.identification, RISet=fd.RISet, vals=vals, type="name")
+ #barName.append(HT.TR(HT.TD(barName_img)))
+ #barName_container.append(barName)
+ #barName_div.append(barName_container)
+ #stats_container.append(barName_div)
+ #
+ #barRank_div = HT.Div(id="statstabs-4")
+ #barRank_container = HT.Paragraph()
+ #barRank = HT.TableLite(cellspacing=0, cellpadding=0, width="100%")
+ #barRank_img = BasicStatisticsFunctions.plotBarGraph(identification=fd.identification, RISet=fd.RISet, vals=vals, type="rank")
+ #barRank.append(HT.TR(HT.TD(barRank_img)))
+ #barRank_container.append(barRank)
+ #barRank_div.append(barRank_container)
+ #stats_container.append(barRank_div)
+
+ # stats_cell.append(stats_container)
+ #
+ #stats_script.append(stats_script_text)
+ #
+ #submitTable = HT.TableLite(cellspacing=0, cellpadding=0, width="100%", Class="target2")
+ #stats_row.append(stats_cell)
+
+ #submitTable.append(stats_row)
+ #submitTable.append(stats_script)
+
+ #title2Body.append(submitTable)
def build_correlation_tools(self, fd, this_trait):
diff --git a/wqflask/wqflask/static/new/javascript/show_trait.coffee b/wqflask/wqflask/static/new/javascript/show_trait.coffee
index 803045d5..a91e9681 100644
--- a/wqflask/wqflask/static/new/javascript/show_trait.coffee
+++ b/wqflask/wqflask/static/new/javascript/show_trait.coffee
@@ -69,7 +69,7 @@ $ ->
make_table = ->
header = "<thead><tr><th>&nbsp;</th>"
console.log("js_data.sample_groups:", js_data.sample_groups)
- for key, value of js_data.sample_groups
+ for own key, value of js_data.sample_groups
console.log("aa key:", key)
console.log("aa value:", value)
the_id = process_id("column", key)
@@ -108,7 +108,7 @@ $ ->
row_line = """<tr>"""
row_line += """<td id="#{ row.vn }">#{ row.pretty }</td>"""
console.log("box - js_data.sample_groups:", js_data.sample_groups)
- for key, value of js_data.sample_groups
+ for own key, value of js_data.sample_groups
console.log("apple key:", key)
the_id = process_id(key, row.vn)
console.log("the_id:", the_id)
@@ -166,6 +166,30 @@ $ ->
#End Calculate Correlations Code
+ #Populate Samples Attribute Values Code
+
+ create_value_dropdown = (value) ->
+ return """<option val=#{value}>#{value}</option>"""
+
+ populate_sample_attributes_values_dropdown = ->
+ console.log("in beginning of psavd")
+ $('#attribute_values').empty()
+ sample_attributes = {}
+ for own key, attribute_info of js_data.attribute_names
+ sample_attributes[attribute_info.name] = attribute_info.distinct_values
+ console.log("[visa] attributes is:", sample_attributes)
+ selected_attribute = $('#exclude_menu').val()
+ for value in sample_attributes[selected_attribute]
+ $(create_value_dropdown(value))
+ .appendTo($('#attribute_values'))
+
+ # Must run once at beginning
+ populate_sample_attributes_values_dropdown()
+ $('#exclude_menu').change(populate_sample_attributes_values_dropdown)
+
+
+
+ #End Populate Samples Attribute Values Code
console.log("before registering show_hide_outliers")
$('#show_hide_outliers').click(show_hide_outliers)
diff --git a/wqflask/wqflask/static/new/javascript/show_trait.js b/wqflask/wqflask/static/new/javascript/show_trait.js
new file mode 100644
index 00000000..039a5e04
--- /dev/null
+++ b/wqflask/wqflask/static/new/javascript/show_trait.js
@@ -0,0 +1,236 @@
+// Generated by CoffeeScript 1.3.3
+(function() {
+ var is_number,
+ __hasProp = {}.hasOwnProperty,
+ __slice = [].slice;
+
+ console.log("start_b");
+
+ is_number = function(o) {
+ return !isNaN((o - 0) && o !== null);
+ };
+
+ $(function() {
+ var change_stats_value, create_value_dropdown, edit_data_change, hide_tabs, make_table, on_corr_method_change, populate_sample_attributes_values_dropdown, process_id, show_hide_outliers, stats_mdp_change, update_stat_values;
+ hide_tabs = function(start) {
+ var x, _i, _results;
+ _results = [];
+ for (x = _i = start; start <= 10 ? _i <= 10 : _i >= 10; x = start <= 10 ? ++_i : --_i) {
+ _results.push($("#stats_tabs" + x).hide());
+ }
+ return _results;
+ };
+ hide_tabs(1);
+ stats_mdp_change = function() {
+ var selected;
+ selected = $(this).val();
+ hide_tabs(0);
+ return $("#stats_tabs" + selected).show();
+ };
+ $(".stats_mdp").change(stats_mdp_change);
+ change_stats_value = function(sample_sets, category, value_type, decimal_places) {
+ var current_value, id, in_box, the_value;
+ id = "#" + process_id(category, value_type);
+ console.log("the_id:", id);
+ in_box = $(id).html;
+ current_value = parseFloat($(in_box)).toFixed(decimal_places);
+ the_value = sample_sets[category][value_type]();
+ if (decimal_places > 0) {
+ the_value = the_value.toFixed(decimal_places);
+ }
+ if (the_value !== current_value) {
+ return $(id).html(the_value).effect("highlight");
+ }
+ };
+ update_stat_values = function(sample_sets) {
+ var category, stat, _i, _len, _ref, _results;
+ _ref = ['primary_only', 'other_only', 'all_cases'];
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ category = _ref[_i];
+ change_stats_value(sample_sets, category, "n_of_samples", 0);
+ _results.push((function() {
+ var _j, _len1, _ref1, _results1;
+ _ref1 = ["mean", "median", "std_dev", "std_error"];
+ _results1 = [];
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+ stat = _ref1[_j];
+ _results1.push(change_stats_value(sample_sets, category, stat, 2));
+ }
+ return _results1;
+ })());
+ }
+ return _results;
+ };
+ edit_data_change = function() {
+ var category, checkbox, checked, real_value, row, sample_sets, value, values, _i, _len;
+ sample_sets = {
+ primary_only: new Stats([]),
+ other_only: new Stats([]),
+ all_cases: new Stats([])
+ };
+ console.log("at beginning:", sample_sets);
+ values = $('#value_table').find(".edit_sample_value");
+ for (_i = 0, _len = values.length; _i < _len; _i++) {
+ value = values[_i];
+ real_value = $(value).val();
+ row = $(value).closest("tr");
+ category = row[0].id;
+ checkbox = $(row).find(".edit_sample_checkbox");
+ checked = $(checkbox).attr('checked');
+ if (checked && is_number(real_value) && real_value !== "") {
+ real_value = parseFloat(real_value);
+ if (_(category).startsWith("Primary")) {
+ sample_sets.primary_only.add_value(real_value);
+ } else if (_(category).startsWith("Other")) {
+ sample_sets.other_only.add_value(real_value);
+ }
+ sample_sets.all_cases.add_value(real_value);
+ }
+ }
+ console.log("towards end:", sample_sets);
+ return update_stat_values(sample_sets);
+ };
+ make_table = function() {
+ var header, key, row, row_line, rows, table, the_id, the_rows, value, _i, _len, _ref, _ref1;
+ header = "<thead><tr><th>&nbsp;</th>";
+ console.log("js_data.sample_groups:", js_data.sample_groups);
+ _ref = js_data.sample_groups;
+ for (key in _ref) {
+ if (!__hasProp.call(_ref, key)) continue;
+ value = _ref[key];
+ console.log("aa key:", key);
+ console.log("aa value:", value);
+ the_id = process_id("column", key);
+ header += "<th id=\"" + the_id + "\">" + value + "</th>";
+ }
+ header += "</thead>";
+ console.log("windex header is:", header);
+ rows = [
+ {
+ vn: "n_of_samples",
+ pretty: "N of Samples"
+ }, {
+ vn: "mean",
+ pretty: "Mean"
+ }, {
+ vn: "median",
+ pretty: "Median"
+ }, {
+ vn: "std_error",
+ pretty: "Standard Error (SE)"
+ }, {
+ vn: "std_dev",
+ pretty: "Standard Deviation (SD)"
+ }
+ ];
+ console.log("rows are:", rows);
+ the_rows = "<tbody>";
+ console.log("length of rows:", rows.length);
+ for (_i = 0, _len = rows.length; _i < _len; _i++) {
+ row = rows[_i];
+ console.log("rowing");
+ row_line = "<tr>";
+ row_line += "<td id=\"" + row.vn + "\">" + row.pretty + "</td>";
+ console.log("box - js_data.sample_groups:", js_data.sample_groups);
+ _ref1 = js_data.sample_groups;
+ for (key in _ref1) {
+ if (!__hasProp.call(_ref1, key)) continue;
+ value = _ref1[key];
+ console.log("apple key:", key);
+ the_id = process_id(key, row.vn);
+ console.log("the_id:", the_id);
+ row_line += "<td id=\"" + the_id + "\">foo</td>";
+ }
+ row_line += "</tr>";
+ console.log("row line:", row_line);
+ the_rows += row_line;
+ }
+ the_rows += "</tbody>";
+ table = header + the_rows;
+ console.log("table is:", table);
+ return $("#stats_table").append(table);
+ };
+ process_id = function() {
+ var processed, value, values, _i, _len;
+ values = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ /* Make an id or a class valid javascript by, for example, eliminating spaces
+ */
+
+ processed = "";
+ for (_i = 0, _len = values.length; _i < _len; _i++) {
+ value = values[_i];
+ console.log("value:", value);
+ value = value.replace(" ", "_");
+ if (processed.length) {
+ processed += "-";
+ }
+ processed += value;
+ }
+ return processed;
+ };
+ show_hide_outliers = function() {
+ var label;
+ console.log("FOOBAR in beginning of show_hide_outliers");
+ label = $('#show_hide_outliers').val();
+ console.log("lable is:", label);
+ if (label === "Hide Outliers") {
+ return $('#show_hide_outliers').val("Show Outliers");
+ } else if (label === "Show Outliers") {
+ console.log("Found Show Outliers");
+ $('#show_hide_outliers').val("Hide Outliers");
+ return console.log("Should be now Hide Outliers");
+ }
+ };
+ on_corr_method_change = function() {
+ var corr_method;
+ console.log("in beginning of on_corr_method_change");
+ corr_method = $('select[name=corr_method]').val();
+ console.log("corr_method is:", corr_method);
+ $('.correlation_desc').hide();
+ $('#' + corr_method + "_r_desc").show().effect("highlight");
+ if (corr_method === "lit") {
+ return $("#corr_sample_method_options").hide();
+ } else {
+ return $("#corr_sample_method_options").show();
+ }
+ };
+ $('select[name=corr_method]').change(on_corr_method_change);
+ create_value_dropdown = function(value) {
+ return "<option val=" + value + ">" + value + "</option>";
+ };
+ populate_sample_attributes_values_dropdown = function() {
+ var attribute_info, key, sample_attributes, selected_attribute, value, _i, _len, _ref, _ref1, _results;
+ console.log("in beginning of psavd");
+ $('#attribute_values').empty();
+ sample_attributes = {};
+ _ref = js_data.attribute_names;
+ for (key in _ref) {
+ if (!__hasProp.call(_ref, key)) continue;
+ attribute_info = _ref[key];
+ sample_attributes[attribute_info.name] = attribute_info.distinct_values;
+ }
+ console.log("[visa] attributes is:", sample_attributes);
+ selected_attribute = $('#exclude_menu').val();
+ _ref1 = sample_attributes[selected_attribute];
+ _results = [];
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+ value = _ref1[_i];
+ _results.push($(create_value_dropdown(value)).appendTo($('#attribute_values')));
+ }
+ return _results;
+ };
+ populate_sample_attributes_values_dropdown();
+ $('#exclude_menu').change(populate_sample_attributes_values_dropdown);
+ console.log("before registering show_hide_outliers");
+ $('#show_hide_outliers').click(show_hide_outliers);
+ console.log("after registering show_hide_outliers");
+ _.mixin(_.str.exports());
+ $('#value_table').change(edit_data_change);
+ console.log("loaded");
+ make_table();
+ edit_data_change();
+ return console.log("end");
+ });
+
+}).call(this);
diff --git a/wqflask/wqflask/templates/show_trait.html b/wqflask/wqflask/templates/show_trait.html
index 7d0e671f..3d24e738 100644
--- a/wqflask/wqflask/templates/show_trait.html
+++ b/wqflask/wqflask/templates/show_trait.html
@@ -1227,6 +1227,20 @@
</select></span> &nbsp;&nbsp;&nbsp;
<input type="button" name="blockSamples" class="button" value=" Block ">
<br>
+
+ {% if sample_groups[0].attributes %}
+ <strong>&nbsp;&nbsp;Block samples by index:&nbsp;&nbsp;&nbsp;&nbsp;</strong>
+ <select id="exclude_menu" size=1>
+ {% for attribute in sample_groups[0].attributes %}
+ <option value="{{ sample_groups[0].attributes[attribute].name }}">{{ sample_groups[0].attributes[attribute].name }}</option>
+ {% endfor %}
+ </select>
+ <select id="attribute_values" size=1>
+ </select>
+ {% endif %}
+ <input type="button" name="exclude_group" class="button" value=" Block ">
+ <br>
+
<strong>&nbsp;&nbsp;Options:</strong>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<input type="button" id="show_hide_no_value" class="button" value="Hide No Value">
@@ -1335,7 +1349,7 @@
<script type="text/javascript" src="/static/new/js_external/underscore.string.min.js"></script>
<script type="text/javascript" src="/static/new/javascript/stats.js"></script>
- <script type="text/javascript" src="/static/new/javascript/trait_data_and_analysis.js"></script>
+ <script type="text/javascript" src="/static/new/javascript/show_trait.js"></script>
<!-- End of body -->
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 4da1082f..c38ecb47 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -1,6 +1,6 @@
from __future__ import absolute_import, division, print_function
-import json
+import simplejson as json
import yaml
import flask
@@ -10,7 +10,7 @@ from wqflask import app
from flask import render_template, request
from wqflask import search_results
-from wqflask.show_trait import DataEditingPage
+from wqflask.show_trait import show_trait
from wqflask.correlation import CorrelationPage
from wqflask.dataSharing import SharingInfo, SharingInfoPage
@@ -19,8 +19,6 @@ from base import webqtlFormData
from pprint import pformat as pf
-print("latest blue")
-
@app.route("/")
def index_page():
@@ -29,7 +27,7 @@ def index_page():
@app.route("/data_sharing")
-def data_sharing():
+def data_sharing_page():
print("In data_sharing")
fd = webqtlFormData.webqtlFormData(request.args)
print("1Have fd")
@@ -45,7 +43,7 @@ def data_sharing():
@app.route("/search")
-def search():
+def search_page():
if 'info_database' in request.args:
print("Going to sharing_info_page")
template_vars = sharing_info_page()
@@ -60,7 +58,7 @@ def search():
@app.route("/whats_new")
-def whats_new():
+def whats_new_page():
#variables = whats_new.whats_new()
with open("/home/sam/gene/wqflask/wqflask/yaml_data/whats_new.yaml") as fh:
contents = fh.read()
@@ -72,19 +70,20 @@ def whats_new():
@app.route("/show_trait")
-def show_trait():
+def show_trait_page():
# Here it's currently too complicated not to use an fd that is a webqtlFormData
fd = webqtlFormData.webqtlFormData(request.args)
- #template_vars = show_trait_page.ShowTraitPage(fd)
- template_vars = show_trait.show_trait(fd)
- template_vars.js_data = json.dumps(template_vars.js_data)
-
+ template_vars = show_trait.ShowTrait(fd)
+ template_vars.js_data = json.dumps(template_vars.js_data,
+ default=json_default_handler,
+ indent=" ",
+ sort_keys=True)
print("show_trait template_vars:", pf(template_vars.__dict__))
- return render_template("trait_data_and_analysis.html", **template_vars.__dict__)
+ return render_template("show_trait.html", **template_vars.__dict__)
@app.route("/corr_compute", methods=('POST',))
-def corr_compute():
+def corr_compute_page():
#print("In corr_compute, request.args is:", pf(request.form))
fd = webqtlFormData.webqtlFormData(request.form)
print("Have fd")
@@ -101,3 +100,19 @@ def sharing_info_page():
template_vars = SharingInfoPage.SharingInfoPage(fd)
print("2 Made it to rendering")
return template_vars
+
+
+def json_default_handler(obj):
+ '''Based on http://stackoverflow.com/a/2680060/1175849'''
+ # Handle datestamps
+ if hasattr(obj, 'isoformat'):
+ return obj.isoformat()
+ # Handle integer keys for dictionaries
+ elif isinstance(obj, int):
+ return str(int)
+ # Handle custom objects
+ if hasattr(obj, '__dict__'):
+ return obj.__dict__
+ else:
+ raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (
+ type(obj), repr(obj))