Added View for Surveys.
Patch by: James Levy, Daniel Diniz
Reviewed by: to-be-reviewed
#!/usr/bin/python
#
# Copyright (C) 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for the gviz_api module."""
__author__ = "Amit Weinstein"
from datetime import date
from datetime import datetime
from datetime import time
import re
import unittest
from gviz_api import DataTable
from gviz_api import DataTableException
class DataTableTest(unittest.TestCase):
def testSingleValueToJS(self):
# We first check that given an unknown type it raises exception
self.assertRaises(DataTableException,
DataTable.SingleValueToJS, 1, "no_such_type")
# If we give a type which does not match the value, we expect it to fail
self.assertRaises(DataTableException,
DataTable.SingleValueToJS, "a", "number")
self.assertRaises(DataTableException,
DataTable.SingleValueToJS, "b", "timeofday")
self.assertRaises(DataTableException,
DataTable.SingleValueToJS, 10, "date")
# A tuple for value and formatted value should be of length 2
self.assertRaises(DataTableException,
DataTable.SingleValueToJS, (5, "5$", "6$"), "string")
# Some good examples from all the different types
self.assertEqual("true", DataTable.SingleValueToJS(True, "boolean"))
self.assertEqual("false", DataTable.SingleValueToJS(False, "boolean"))
self.assertEqual("true", DataTable.SingleValueToJS(1, "boolean"))
self.assertEqual("null", DataTable.SingleValueToJS(None, "boolean"))
self.assertEqual(("false", "'a'"),
DataTable.SingleValueToJS((False, "a"), "boolean"))
self.assertEqual("1", DataTable.SingleValueToJS(1, "number"))
self.assertEqual("1.0", DataTable.SingleValueToJS(1., "number"))
self.assertEqual("-5", DataTable.SingleValueToJS(-5, "number"))
self.assertEqual("null", DataTable.SingleValueToJS(None, "number"))
self.assertEqual(("5", "'5$'"),
DataTable.SingleValueToJS((5, "5$"), "number"))
self.assertEqual("'-5'", DataTable.SingleValueToJS(-5, "string"))
self.assertEqual("'abc'", DataTable.SingleValueToJS("abc", "string"))
self.assertEqual("null", DataTable.SingleValueToJS(None, "string"))
self.assertEqual("new Date(2010,0,2)",
DataTable.SingleValueToJS(date(2010, 1, 2), "date"))
self.assertEqual("new Date(2001,1,3)",
DataTable.SingleValueToJS(datetime(2001, 2, 3, 4, 5, 6),
"date"))
self.assertEqual("null", DataTable.SingleValueToJS(None, "date"))
self.assertEqual("[10,11,12]",
DataTable.SingleValueToJS(time(10, 11, 12), "timeofday"))
self.assertEqual("[3,4,5]",
DataTable.SingleValueToJS(datetime(2010, 1, 2, 3, 4, 5),
"timeofday"))
self.assertEqual("null", DataTable.SingleValueToJS(None, "timeofday"))
self.assertEqual("new Date(2001,1,3,4,5,6)",
DataTable.SingleValueToJS(datetime(2001, 2, 3, 4, 5, 6),
"datetime"))
self.assertEqual("null", DataTable.SingleValueToJS(None, "datetime"))
self.assertEqual(("null", "'none'"),
DataTable.SingleValueToJS((None, "none"), "string"))
def testDifferentStrings(self):
# Checking escaping of strings
problematic_strings = ["control", "new\nline", "",
"single'quote", 'double"quote',
r"one\slash", r"two\\slash", u"unicode eng",
u"unicode \u05e2\u05d1\u05e8\u05d9\u05ea"]
for s in problematic_strings:
js_value = DataTable.SingleValueToJS(s, "string")
if isinstance(js_value, unicode):
js_value = "u%s" % js_value
self.assertEquals(s, eval(js_value))
js_value = DataTable.SingleValueToJS(("str", s), "string")[1]
if isinstance(js_value, unicode):
js_value = "u%s" % js_value
self.assertEquals(s, eval(js_value))
def testDifferentCustomProperties(self):
# Checking escaping of custom properties
problematic_cps = [{"control": "test"}, {u"unicode": "value"},
{"key": u"unicode"}, {u"unicode": u"unicode"},
{"regular": "single'quote"}, {"unicode": u"s'quote"}]
for cp in problematic_cps:
js_value = DataTable._EscapeCustomProperties(cp)
self.assertEquals(cp, eval(js_value))
def testColumnTypeParser(self):
# Checking several wrong formats
self.assertRaises(DataTableException,
DataTable.ColumnTypeParser, 5)
self.assertRaises(DataTableException,
DataTable.ColumnTypeParser, ("a", "b", "c", "d"))
self.assertRaises(DataTableException,
DataTable.ColumnTypeParser, ("a", 5, "c"))
# Checking several legal formats
self.assertEqual({"id": "abc", "label": "abc", "type": "string",
"custom_properties": {}},
DataTable.ColumnTypeParser("abc"))
self.assertEqual({"id": "abc", "label": "abc", "type": "string",
"custom_properties": {}},
DataTable.ColumnTypeParser(("abc",)))
self.assertEqual({"id": "abc", "label": "bcd", "type": "string",
"custom_properties": {}},
DataTable.ColumnTypeParser(("abc", "string", "bcd")))
self.assertEqual({"id": "a", "label": "b", "type": "number",
"custom_properties": {}},
DataTable.ColumnTypeParser(("a", "number", "b")))
self.assertEqual({"id": "a", "label": "a", "type": "number",
"custom_properties": {}},
DataTable.ColumnTypeParser(("a", "number")))
self.assertEqual({"id": "i", "label": "l", "type": "string",
"custom_properties": {"key": "value"}},
DataTable.ColumnTypeParser(("i", "string", "l",
{"key": "value"})))
def testTableDescriptionParser(self):
# We expect it to fail with empty lists or dictionaries
self.assertRaises(DataTableException,
DataTable.TableDescriptionParser, {})
self.assertRaises(DataTableException,
DataTable.TableDescriptionParser, [])
self.assertRaises(DataTableException,
DataTable.TableDescriptionParser, {"a": []})
self.assertRaises(DataTableException,
DataTable.TableDescriptionParser, {"a": {"b": {}}})
# We expect it to fail if we give a non-string at the lowest level
self.assertRaises(DataTableException,
DataTable.TableDescriptionParser, {"a": 5})
self.assertRaises(DataTableException,
DataTable.TableDescriptionParser, [("a", "number"), 6])
# Some valid examples which mixes both dictionaries and lists
self.assertEqual(
[{"id": "a", "label": "a", "type": "date",
"depth": 0, "container": "iter", "custom_properties": {}},
{"id": "b", "label": "b", "type": "timeofday",
"depth": 0, "container": "iter", "custom_properties": {}}],
DataTable.TableDescriptionParser([("a", "date"), ("b", "timeofday")]))
self.assertEqual(
[{"id": "a", "label": "a", "type": "string",
"depth": 0, "container": "dict", "custom_properties": {}},
{"id": "b", "label": "b", "type": "number",
"depth": 1, "container": "iter", "custom_properties": {}},
{"id": "c", "label": "column c", "type": "string",
"depth": 1, "container": "iter", "custom_properties": {}}],
DataTable.TableDescriptionParser({"a": [("b", "number"),
("c", "string", "column c")]}))
self.assertEqual(
[{"id": "a", "label": "column a", "type": "number",
"depth": 0, "container": "dict", "custom_properties": {}},
{"id": "b", "label": "b", "type": "number",
"depth": 1, "container": "dict", "custom_properties": {}},
{"id": "c", "label": "c", "type": "string",
"depth": 1, "container": "dict", "custom_properties": {}}],
DataTable.TableDescriptionParser({("a", "number", "column a"):
{"b": "number", "c": "string"}}))
self.assertEqual(
[{"id": "a", "label": "column a", "type": "number",
"depth": 0, "container": "dict", "custom_properties": {}},
{"id": "b", "label": "column b", "type": "string",
"depth": 1, "container": "scalar", "custom_properties": {}}],
DataTable.TableDescriptionParser({("a", "number", "column a"):
("b", "string", "column b")}))
def testAppendData(self):
# We check a few examples where the format of the data does not match the
# description and hen a few valid examples. The test for the content itself
# is done inside the ToJSCode and ToJSon functions.
table = DataTable([("a", "number"), ("b", "string")])
self.assertEqual(0, table.NumberOfRows())
self.assertRaises(DataTableException,
table.AppendData, [[1, "a", True]])
self.assertRaises(DataTableException,
table.AppendData, {1: ["a"], 2: ["b"]})
self.assertEquals(None, table.AppendData([[1, "a"], [2, "b"]]))
self.assertEqual(2, table.NumberOfRows())
self.assertEquals(None, table.AppendData([[3, "c"], [4]]))
self.assertEqual(4, table.NumberOfRows())
table = DataTable({"a": "number", "b": "string"})
self.assertEqual(0, table.NumberOfRows())
self.assertRaises(DataTableException,
table.AppendData, [[1, "a"]])
self.assertRaises(DataTableException,
table.AppendData, {5: {"b": "z"}})
self.assertEquals(None, table.AppendData([{"a": 1, "b": "z"}]))
self.assertEqual(1, table.NumberOfRows())
table = DataTable({("a", "number"): [("b", "string")]})
self.assertEqual(0, table.NumberOfRows())
self.assertRaises(DataTableException,
table.AppendData, [[1, "a"]])
self.assertRaises(DataTableException,
table.AppendData, {5: {"b": "z"}})
self.assertEquals(None, table.AppendData({5: ["z"], 6: ["w"]}))
self.assertEqual(2, table.NumberOfRows())
table = DataTable({("a", "number"): {"b": "string", "c": "number"}})
self.assertEqual(0, table.NumberOfRows())
self.assertRaises(DataTableException,
table.AppendData, [[1, "a"]])
self.assertRaises(DataTableException,
table.AppendData, {1: ["a", 2]})
self.assertEquals(None, table.AppendData({5: {"b": "z", "c": 6},
7: {"c": 8},
9: {}}))
self.assertEqual(3, table.NumberOfRows())
def testToJSCode(self):
table = DataTable([("a", "number", "A"), "b", ("c", "timeofday")],
[[1],
[None, "z", time(1, 2, 3)],
[(2, "2$"), "w", time(2, 3, 4)]])
self.assertEqual(3, table.NumberOfRows())
self.assertEqual(("var mytab = new google.visualization.DataTable();\n"
"mytab.addColumn('number', 'A', 'a');\n"
"mytab.addColumn('string', 'b', 'b');\n"
"mytab.addColumn('timeofday', 'c', 'c');\n"
"mytab.addRows(3);\n"
"mytab.setCell(0, 0, 1);\n"
"mytab.setCell(1, 1, 'z');\n"
"mytab.setCell(1, 2, [1,2,3]);\n"
"mytab.setCell(2, 0, 2, '2$');\n"
"mytab.setCell(2, 1, 'w');\n"
"mytab.setCell(2, 2, [2,3,4]);\n"),
table.ToJSCode("mytab"))
table = DataTable({("a", "number"): {"b": "date", "c": "datetime"}},
{1: {},
2: {"b": date(1, 2, 3)},
3: {"c": datetime(1, 2, 3, 4, 5, 6)}})
self.assertEqual(3, table.NumberOfRows())
self.assertEqual(("var mytab2 = new google.visualization.DataTable();\n"
"mytab2.addColumn('datetime', 'c', 'c');\n"
"mytab2.addColumn('date', 'b', 'b');\n"
"mytab2.addColumn('number', 'a', 'a');\n"
"mytab2.addRows(3);\n"
"mytab2.setCell(0, 2, 1);\n"
"mytab2.setCell(1, 1, new Date(1,1,3));\n"
"mytab2.setCell(1, 2, 2);\n"
"mytab2.setCell(2, 0, new Date(1,1,3,4,5,6));\n"
"mytab2.setCell(2, 2, 3);\n"),
table.ToJSCode("mytab2", columns_order=["c", "b", "a"]))
def testToJSon(self):
# The json of the initial data we load to the table.
init_data_json = ("{cols:"
"[{id:'a',label:'A',type:'number'},"
"{id:'b',label:'b',type:'string'},"
"{id:'c',label:'c',type:'boolean'}],"
"rows:["
"{c:[{v:1},,{v:null}]},"
"{c:[,{v:'z'},{v:true}]}"
"]}")
table = DataTable([("a", "number", "A"), "b", ("c", "boolean")],
[[1],
[None, "z", True]])
self.assertEqual(2, table.NumberOfRows())
self.assertEqual(init_data_json,
table.ToJSon())
table.AppendData([[-1, "w", False]])
self.assertEqual(3, table.NumberOfRows())
self.assertEqual(init_data_json[:-2] + ",{c:[{v:-1},{v:'w'},{v:false}]}]}",
table.ToJSon())
cols_json = ("{cols:"
"[{id:'t',label:'T',type:'timeofday'},"
"{id:'d',label:'d',type:'date'},"
"{id:'dt',label:'dt',type:'datetime'}],")
table = DataTable({("d", "date"): [("t", "timeofday", "T"),
("dt", "datetime")]})
table.LoadData({date(1, 2, 3): [time(1, 2, 3)]})
self.assertEqual(1, table.NumberOfRows())
self.assertEqual(cols_json +
"rows:[{c:[{v:[1,2,3]},{v:new Date(1,1,3)},{v:null}]}]}",
table.ToJSon(columns_order=["t", "d", "dt"]))
table.LoadData({date(2, 3, 4): [(time(2, 3, 4), "time 2 3 4"),
datetime(1, 2, 3, 4, 5, 6)],
date(3, 4, 5): []})
self.assertEqual(2, table.NumberOfRows())
self.assertEqual((cols_json + "rows:["
"{c:[{v:[2,3,4],f:'time 2 3 4'},{v:new Date(2,2,4)},"
"{v:new Date(1,1,3,4,5,6)}]},"
"{c:[,{v:new Date(3,3,5)},{v:null}]}]}"),
table.ToJSon(columns_order=["t", "d", "dt"]))
json = ("{cols:[{id:'a',label:'a',type:'string'},"
"{id:'b',label:'b',type:'number'}],"
"rows:[{c:[{v:'a1'},{v:1}]},{c:[{v:'a2'},{v:2}]},"
"{c:[{v:'a3'},{v:3}]}]}")
table = DataTable({"a": ("b", "number")},
{"a1": 1, "a2": 2, "a3": 3})
self.assertEqual(3, table.NumberOfRows())
self.assertEqual(json,
table.ToJSon())
def testCustomProperties(self):
# The json of the initial data we load to the table.
json = ("{cols:"
"[{id:'a',label:'A',type:'number',p:{'col_cp':'col_v'}},"
"{id:'b',label:'b',type:'string'},"
"{id:'c',label:'c',type:'boolean'}],"
"rows:["
"{c:[{v:1},,{v:null,p:{'null_cp':'null_v'}}],p:{'row_cp':'row_v'}},"
"{c:[,{v:'z',p:{'cell_cp':'cell_v'}},{v:true}]},"
"{c:[{v:3},,{v:null}],p:{'row_cp2':'row_v2'}}],"
"p:{'global_cp':'global_v'}"
"}")
jscode = ("var mytab = new google.visualization.DataTable();\n"
"mytab.setTableProperties({'global_cp':'global_v'});\n"
"mytab.addColumn('number', 'A', 'a');\n"
"mytab.setColumnProperties(0, {'col_cp':'col_v'});\n"
"mytab.addColumn('string', 'b', 'b');\n"
"mytab.addColumn('boolean', 'c', 'c');\n"
"mytab.addRows(3);\n"
"mytab.setCell(0, 0, 1);\n"
"mytab.setCell(0, 2, null, null, {'null_cp':'null_v'});\n"
"mytab.setRowProperties(0, {'row_cp':'row_v'});\n"
"mytab.setCell(1, 1, 'z', null, {'cell_cp':'cell_v'});\n"
"mytab.setCell(1, 2, true);\n"
"mytab.setCell(2, 0, 3);\n"
"mytab.setRowProperties(2, {'row_cp2':'row_v2'});\n")
table = DataTable([("a", "number", "A", {"col_cp": "col_v"}), "b",
("c", "boolean")],
custom_properties={"global_cp": "global_v"})
table.AppendData([[1, None, (None, None, {"null_cp": "null_v"})]],
custom_properties={"row_cp": "row_v"})
table.AppendData([[None, ("z", None, {"cell_cp": "cell_v"}), True], [3]])
table.SetRowsCustomProperties(2, {"row_cp2": "row_v2"})
self.assertEqual(json, table.ToJSon())
self.assertEqual(jscode, table.ToJSCode("mytab"))
def testToCsv(self):
init_data_csv = "\n".join(['"A", "b", "c"',
'1, "", ""',
'"", "zz\'top", true'])
table = DataTable([("a", "number", "A"), "b", ("c", "boolean")],
[[(1, "$1")], [None, "zz'top", True]])
self.assertEqual(init_data_csv, table.ToCsv())
table.AppendData([[-1, "w", False]])
init_data_csv = "%s\n%s" % (init_data_csv, '-1, "w", false')
self.assertEquals(init_data_csv, table.ToCsv())
init_data_csv = "\n".join([
'"T", "d", "dt"',
'"[1,2,3]", "new Date(1,1,3)", ""',
'"time ""2 3 4""", "new Date(2,2,4)", "new Date(1,1,3,4,5,6)"',
'"", "new Date(3,3,5)", ""'])
table = DataTable({("d", "date"): [("t", "timeofday", "T"),
("dt", "datetime")]})
table.LoadData({date(1, 2, 3): [time(1, 2, 3)],
date(2, 3, 4): [(time(2, 3, 4), 'time "2 3 4"'),
datetime(1, 2, 3, 4, 5, 6)],
date(3, 4, 5): []})
self.assertEqual(init_data_csv, table.ToCsv(columns_order=["t", "d", "dt"]))
def testToTsvExcel(self):
table = DataTable({("d", "date"): [("t", "timeofday", "T"),
("dt", "datetime")]})
table.LoadData({date(1, 2, 3): [time(1, 2, 3)],
date(2, 3, 4): [(time(2, 3, 4), 'time "2 3 4"'),
datetime(1, 2, 3, 4, 5, 6)],
date(3, 4, 5): []})
self.assertEqual(table.ToCsv().replace(", ", "\t").encode("UTF-16LE"),
table.ToTsvExcel())
def testToHtml(self):
html_table_header = "<html><body><table border='1'>"
html_table_footer = "</table></body></html>"
init_data_html = html_table_header + (
"<thead><tr>"
"<th>A</th><th>b</th><th>c</th>"
"</tr></thead>"
"<tbody>"
"<tr><td>'$1'</td><td></td><td></td></tr>"
"<tr><td></td><td>'<z>'</td><td>true</td></tr>"
"</tbody>") + html_table_footer
table = DataTable([("a", "number", "A"), "b", ("c", "boolean")],
[[(1, "$1")], [None, "<z>", True]])
self.assertEqual(init_data_html.replace("\n", ""), table.ToHtml())
init_data_html = html_table_header + (
"<thead><tr>"
"<th>T</th><th>d</th><th>dt</th>"
"</tr></thead>"
"<tbody>"
"<tr><td>[1,2,3]</td><td>new Date(1,1,3)</td><td></td></tr>"
"<tr><td>'time 2 3 4'</td><td>new Date(2,2,4)</td>"
"<td>new Date(1,1,3,4,5,6)</td></tr>"
"<tr><td></td><td>new Date(3,3,5)</td><td></td></tr>"
"</tbody>") + html_table_footer
table = DataTable({("d", "date"): [("t", "timeofday", "T"),
("dt", "datetime")]})
table.LoadData({date(1, 2, 3): [time(1, 2, 3)],
date(2, 3, 4): [(time(2, 3, 4), "time 2 3 4"),
datetime(1, 2, 3, 4, 5, 6)],
date(3, 4, 5): []})
self.assertEqual(init_data_html.replace("\n", ""),
table.ToHtml(columns_order=["t", "d", "dt"]))
def testOrderBy(self):
data = [("b", 3), ("a", 3), ("a", 2), ("b", 1)]
description = ["col1", ("col2", "number", "Second Column")]
table = DataTable(description, data)
table_num_sorted = DataTable(description,
sorted(data, key=lambda x: (x[1], x[0])))
table_str_sorted = DataTable(description,
sorted(data, key=lambda x: x[0]))
table_diff_sorted = DataTable(description,
sorted(sorted(data, key=lambda x: x[1]),
key=lambda x: x[0], reverse=True))
self.assertEqual(table_num_sorted.ToJSon(),
table.ToJSon(order_by=("col2", "col1")))
self.assertEqual(table_num_sorted.ToJSCode("mytab"),
table.ToJSCode("mytab", order_by=("col2", "col1")))
self.assertEqual(table_str_sorted.ToJSon(), table.ToJSon(order_by="col1"))
self.assertEqual(table_str_sorted.ToJSCode("mytab"),
table.ToJSCode("mytab", order_by="col1"))
self.assertEqual(table_diff_sorted.ToJSon(),
table.ToJSon(order_by=[("col1", "desc"), "col2"]))
self.assertEqual(table_diff_sorted.ToJSCode("mytab"),
table.ToJSCode("mytab",
order_by=[("col1", "desc"), "col2"]))
def testToJSonResponse(self):
description = ["col1", "col2", "col3"]
data = [("1", "2", "3"), ("a", "b", "c"), ("One", "Two", "Three")]
req_id = 4
table = DataTable(description, data)
start_str_default = r"google.visualization.Query.setResponse"
start_str_handler = r"MyHandlerFunction"
default_params = (r"\s*'version'\s*:\s*'0.6'\s*,\s*'reqId'\s*:\s*'%s'\s*,"
r"\s*'status'\s*:\s*'OK'\s*" % req_id)
regex1 = re.compile("%s\(\s*\{%s,\s*'table'\s*:\s*{(.*)}\s*\}\s*\);" %
(start_str_default, default_params))
regex2 = re.compile("%s\(\s*\{%s,\s*'table'\s*:\s*{(.*)}\s*\}\s*\);" %
(start_str_handler, default_params))
json_str = table.ToJSon().strip()
json_response = table.ToJSonResponse(req_id=req_id)
match = regex1.findall(json_response)
self.assertEquals(len(match), 1)
# We want to match against the json_str without the curly brackets.
self.assertEquals(match[0], json_str[1:-1])
json_response = table.ToJSonResponse(req_id=req_id,
response_handler=start_str_handler)
match = regex2.findall(json_response)
self.assertEquals(len(match), 1)
# We want to match against the json_str without the curly brackets.
self.assertEquals(match[0], json_str[1:-1])
def testToResponse(self):
description = ["col1", "col2", "col3"]
data = [("1", "2", "3"), ("a", "b", "c"), ("One", "Two", "Three")]
table = DataTable(description, data)
self.assertEquals(table.ToResponse(), table.ToJSonResponse())
self.assertEquals(table.ToResponse(tqx="out:csv"), table.ToCsv())
self.assertEquals(table.ToResponse(tqx="out:html"), table.ToHtml())
self.assertRaises(DataTableException, table.ToResponse, tqx="version:0.1")
self.assertEquals(table.ToResponse(tqx="reqId:4;responseHandler:handle"),
table.ToJSonResponse(req_id=4, response_handler="handle"))
self.assertEquals(table.ToResponse(tqx="out:csv;reqId:4"), table.ToCsv())
self.assertEquals(table.ToResponse(order_by="col2"),
table.ToJSonResponse(order_by="col2"))
self.assertEquals(table.ToResponse(tqx="out:html",
columns_order=("col3", "col2", "col1")),
table.ToHtml(columns_order=("col3", "col2", "col1")))
self.assertRaises(ValueError, table.ToResponse, tqx="SomeWrongTqxFormat")
self.assertRaises(DataTableException, table.ToResponse, tqx="out:bad")
if __name__ == "__main__":
unittest.main()