Matthew Ruttley: Converting an HTML table to an Excel download HTTP Response: A hack for slow OLAP DB connections |
Zenko (“Good fox” in Japanese) is a reporting system (see code on Github here) I’ve created over the last couple of weeks at Mozilla. Basically my non-technical coworkers were getting so frustrated by Tableau (“what the heck is the difference between INNER JOIN and OUTER JOIN?”) that I decided to create a simple dashboard interface for them.
Its a simple bootstrap front-end to a database containing campaign stats for sponsored tiles. You can drill down to each tile or client/partner and pivot by things like locale, country and date.
Zenko’s stack (high to low)
A new feature
When loading one of the analyses pages, a table will be shown. My coworker wanted to be able to download the data to Excel. I came up with 4 possible ways to implement this:
Which is the best solution?
Solution 4
The process is as follows when the “Download for Excel” button is clicked:
Let’s implement it
function convert_table_to_array() {
//convert the current table to a list of lists
itable = document.getElementById("impressions_table") //the table will always be called this in zenko
//convert the table to a list of lists (i.e. array of arrays)
var data = [];
//meta data
col_count = itable.children[0].children[0].children.length //number of cols
row_count = itable.children[1].children.length //number of rows
//grab the header (i.e. first row containing column titles)
header_cells = itable.children[0].children[0].children
header = []
for (i=0;i/iterate through each row
row_cells = itable.children[1].children
for (i=0;i/get each cell in the row
row_content = []
for (j=0;j/some textual entries already contained a comma which messed with things
row_content.push(cell_content)
}
data.push(row_content)
}
return data
}There were various ways to do this in JQuery with
iterable.each()but I ran into complications and simply referencing cells using .children was much easier.
function download_xls() {
//Downloads the current table as an excel file
//1. Create an iframe
iframe = document.createElement("iframe")
iframe.setAttribute("width", 1)
iframe.setAttribute("height", 1)
iframe.setAttribute("frameborder", 0)
iframe.setAttribute("src", "about:blank")
//2. Create a form that can send data to Flask
form = document.createElement("form")
form.setAttribute("method", "POST")
form.setAttribute("action", "/download_excel")
//3. Put the table data into a hidden field in that form
data = document.createElement("input")
data.setAttribute("type", "hidden")
data.setAttribute("value", convert_table_to_array())
data.setAttribute("name", "data")
//4. Append these new elements to the DOM
form.appendChild(data)
iframe.appendChild(form)
document.body.appendChild(iframe)
//5. Send off the data
form.submit()
}The (locally running) Flask will then recieve a POST request at
/download_excel. Let’s set up the route:
#accepts POST data (larger than GET data)
@app.route('/download_excel', methods=['GET', 'POST'])
def download_excel():
"""Creates a file download from received post information"""
#POST data is accessed via a dictionary at request.form
data = request.form["data"]
data = data.split(",") #Split it up by comma
#The data is a list of lists like [[1,2], [3,4]] but is unfortunately sent
#as [1,2,3,4]. However, we know that there are 6 columns, so we can split it
#up into sixes with a list comprehension
data = [data[x:x+6] for x in xrange(0, len(data), 6)]
#Now re-join each one with commas, so it is nicely csv-ish
data = "\n".join([','.join(x) for x in data])
response = make_response(data)
#Return an HTTP Response with an attachment
response.headers["Content-Disposition"] = "attachment; filename=data.csv"
return responseNow, when the user clicks the button:
They instantly get:
Sorry, I can’t show what it looks like in Excel because the data isn’t public at the moment. All code is however available here on github!
One bizarre thing, however, is that the form doesn’t appear in the inspector (in either Chrome or Firefox):
Though, you can access it with some fairly lengthy getters:
Future features
| Комментировать | « Пред. запись — К дневнику — След. запись » | Страницы: [1] [Новые] |