总览
善科 (“Good fox” 在 日本)是一个报告系统(在这里查看Github上的代码) 一世’是在过去两周内在Mozilla创建的。基本上,我的非技术同事对Tableau感到非常沮丧(“INNER JOIN和OUTER JOIN有什么区别?”),我决定为他们创建一个简单的仪表板界面。
它是一个简单的引导程序前端,它包含数据库的活动统计信息 赞助瓷砖. You can drill down to 每 tile or 客户/partner and pivot 通过 things like locale, 国家 and 日期.
善科’s堆栈(从高到低)
- 高图
- 引导程序
- 烧瓶 在本地运行(由于安全限制)
- 使用Python脚本 psycopg2
- Postgres / OLAP Col Store正在运行 亚马逊Redshift
一项新功能
加载分析页面之一时,将显示一个表格。我的同事希望能够将数据下载到Excel。我想出了4种可能的方法来实现此目的:
- 只需重新运行查询,在后端将结果格式设置为csv,然后将其保存并在window.open()文件位置即可。
- 自动保存每个分析请求服务器请求中的数据,并定期清除旧文件。
- 使用像 ExcelBuilder
- Send the 数据 back to the server, 形成at it, and then back to the 客户 via an iframe
哪个是最佳解决方案?
- 这是有问题的,因为我们的重点是查询速度。 redshift数据库是一个 OLAP面向列的数据库和仅追加。这意味着添加数据的速度非常快,但是查询速度却很慢(通常超过6秒)。是的,它正在处理数十亿行这样令人难解的行,但是就等待这么长时间的用户体验而言并不是那么好。’如果他们已经有数据,则要再等待6秒钟重新运行分析。
- 听起来这可能最终会在客户端上存储大量数据,但是效果很好。在安全性方面,我’我不确定数据应该留在用户身上’不需要PC。
- 这没有’t work out so well –在Firefox中,文件命名错误。将来,我’d根据分析参数命名文件,例如<client>-<date>-<country>.xls
- 这是最怪异的解决方案,但是它有效! 烧瓶在本地运行,因此运行速度非常快。具有文件许可权的JQuery / 的JavaScript并不会带来很多麻烦,而且您可以在服务器上轻松地操纵数据这一事实也很不错。
解决方案4
该过程如下“Download 对于 Excel” button is clicked:
- 使用JavaScript引用HTML表并将其转换为数组数组
- 将iframe附加到DOM
- 将带有POST操作和隐藏字段的表单附加到iframe
- 将表内容插入隐藏字段’s value
- 提交表格
- 让Flask接收POST请求并将信息格式化为CSV
- 返回带有包含CSV的文件附件的HTTP响应
让’s implement it
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
功能 convert_table_to_array() { //将当前表转换为列表列表 可耐 = 文件.getElementById(“ impressions_table”) //该表在zenko中将始终称为this //将表格转换为列表列表(即数组数组) 变种 数据 = []; //元数据 col_count = 可耐.孩子们[0].孩子们[0].孩子们.长度 //列数 行_count = 可耐.孩子们[1].孩子们.长度 //行数 //获取标题(即包含列标题的第一行) 标头_cells = 可耐.孩子们[0].孩子们[0].孩子们 标头 = [] 对于 (i=0;i<标头_cells.长度;i++) { 标头.推(标头_cells[i].textContent) } 数据.推(标头) //遍历每一行 行_cells = 可耐.孩子们[1].孩子们 对于 (i=0;i<行_cells.长度;i++) { 行 = 可耐.孩子们[1].孩子们[i].孩子们 //获取行中的每个单元格 行_content = [] 对于 (j=0;j<行.长度;j++) { cell_content = 行[j].textContent.更换(“,”, ”) //一些文字输入已经包含一个逗号,使事情变得混乱 行_content.推(cell_content) } 数据.推(行_content) } 返回 数据 } |
在JQuery中,有多种方法可以使用 可重复的.每() 但我遇到了麻烦,仅使用.children引用细胞就容易得多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
功能 download_xls() { //将当前表下载为excel文件 //1. Create an iframe iframe = 文件.createElement(“ iframe”) iframe.setAttribute(“宽度”, 1) iframe.setAttribute(“高度”, 1) iframe.setAttribute(“框架边框”, 0) iframe.setAttribute(“ src”, “关于:空白”) //2. Create a 形成 that can send 数据 to 烧瓶 形成 = 文件.createElement(“形成”) 形成.setAttribute(“方法”, “ POST”) 形成.setAttribute(“行动”, “ / download_excel”) //3. Put the table 数据 在 to a hidden field 在 that 形成 数据 = 文件.createElement(“输入”) 数据.setAttribute(“类型”, “隐”) 数据.setAttribute(“值”, convert_table_to_array()) 数据.setAttribute(“名称”, “数据”) //4. Append these new elements to the DOM 形成.应用程式endChild(数据) iframe.应用程式endChild(形成) 文件.身体.应用程式endChild(iframe) //5. Send off the 数据 形成.提交() } |
然后,(本地运行中的)Flask将在以下位置接收POST请求: /download_excel . 让’s set up the 路线:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#接受POST数据(大于GET数据) @应用程式.路线('/ download_excel', 方法=['得到', 'POST']) 定义 download_excel(): “”“根据收到的帖子信息创建文件下载”“” #POST数据可通过request.form上的字典进行访问 数据 = 请求.形成[“数据”] 数据 = 数据.分裂(“,”) #以逗号分隔 #数据是列表的列表,例如[[1,2],[3,4]],但不幸的是发送了 #如 [1,2,3,4]。但是,我们知道有6列,因此我们可以将其拆分 #以列表理解进入六十年代 数据 = [数据[x:x+6] 对于 x 在 Xrange(0, 伦(数据), 6)] #N现在每个人都用逗号重新加入,所以它很csv-ish 数据 = “ \ n”.加入([','.加入(x) 对于 x 在 数据]) 响应 = make_response(数据) #返回带有附件的HTTP响应 响应.标头[“内容处置”] = “附件;文件名= 数据.csv” 返回 响应 |
现在,当用户单击按钮时:
他们立即得到:
对不起,我可以’无法显示Excel中的数据,因为数据不是’目前还没有公开。所有代码均可用 在github上!
然而,一件奇怪的事情是表格没有’t出现在检查器中(在Chrome或Firefox中):
不过,您可以使用一些相当长的吸气剂来访问它:
未来功能
- 这些文件的名称可能比data.csv更直观–可能是网址中出现的各种情况的组合’s query string
- 可容纳超过6行的表格。使用不同的定界符(例如,“###”.
- 如果有好处,请创建一个.xls文件而不是CSV文件