Composing the write_html() function

If the user specifies an HTML report, the write_html() function is called to read data from the database, generate the HTML tags for our data, and, using Bootstrap styling, create a table with our file metadata. Because this is HTML, we can customize it to create a professional-looking report that can be converted into a PDF or viewed by anyone with a web browser. If additional HTML elements prove to be useful in your version of the report, they can easily be added to the following strings and customized with logos, highlighting by extension, responsive tables, graphs, and much more, which is possible if you use various web styles and scripts.

Since this book is focused on the design of Python scripts, we won't be diving into detail about HTML, CSS, or other web design languages. Where we use these features, we will describe the basics of why they are used and how to implement them, though we recommend using related resources (such as http://www.w3schools.com) to learn more about those topics if they are of interest to you.

This function begins similarly to write_csv(): we select the files that belong to the custodian in a SQL statement on line 287. Once executed, we again gather our cols using list comprehension on line 291. With our column names, we define the table_header HTML string using the join() function on our list and separating each value with <th></th> tags on line 292. For all except the first and last element, this will enclose each element in a <th>{{ element }}</th> tag. Now, we need to close the first and last element tags to ensure that they form the proper table header. For the beginning of the string, we append the <tr><th> tags to define the table row <tr> for the entire row, and the table header <th> for the first entry. Likewise, we close the table header and table row tags at the end of the string on line 293, as follows:

277 def write_html(conn, target, custodian_id, custodian_name):
278 """
279 The write_html function generates an HTML report from the
280 Files table
281 :param conn: The sqlite3 database connection object
282 :param target: The output filepath
283 :param custodian_id: The custodian ID
284 :return: None
285 """
286 cur = conn.cursor()
287 sql = "SELECT * FROM Files where custodian = {}".format(
288 custodian_id)
289 cur.execute(sql)
290
291 cols = [description[0] for description in cur.description]
292 table_header = '</th><th>'.join(cols)
293 table_header = '<tr><th>' + table_header + '</th></tr>'
294
295 logger.info('Writing HTML report')

On line 297, we open our HTML file in w mode as the html_file variable. With the file open, we begin to build our HTML code, starting with the <html><body> tags that are used to initialize HTML documents on line 298. Next, we connect to the custom style sheet that's hosted online to provide the Bootstrap styles for our table. We do this by using the <link> tag, with the type and the source of the style sheet, which is located at https://www.bootstrapcdn.com/

Now, let's define the header of our HTML report so that we can ensure it contains the custodian ID and name. We will do this by using the <h1></h1> or heading 1 tags. For our table, we use the table tags on line 302 and the Bootstrap styles (table, table-hover, and table-striped) we would like to implement.

For additional information on Bootstrap, visit http://getbootstrap.com. While this script uses Bootstrap CSS version 3.3.5, explore the more recent updates to Bootstrap and see if you can implement the newer features in your code.

With this header information in the HTML string, we can write it to the file, first writing the HTML header and style sheet information on line 304, followed by the column names for our table on line 305, as follows:

297     with open(target, 'w') as html_file:
298 html_string = """<html><body>\n
299 <link rel="stylesheet"
300 href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
301 <h1>File Listing for Custodian ID: {}, {}</h1>\n
302 <table class='table table-hover table-striped'>\n
303 """.format(custodian_id, custodian_name)
304 html_file.write(html_string)
305 html_file.write(table_header)

Now, let's iterate over the records in the database and write them to the table as individual rows. We begin by joining each element in the table data tags (<td></td>) that specify the table cell content. We use list comprehension before joining the data on line 308 and converting it to the string value that the join() method requires:

307         for entry in cur:
308 row_data = "</td><td>".join(
309 [str(x) for x in entry])

On line 310, we add a new line character (\n) followed by a <tr> table row tag and the initial <td> tag to open the table data for the first element. The newline character reduces the loading time in some HTML viewers, as it breaks the data into multiple lines. We also have to close the last table data tag and the entire table row, as seen at the end of line 310. The row data is written to the file on line 311. Finally, within the loop for the table rows, we .flush() the content to the file. With the table data built, we can close the table, body, and the HTML tags on line 313. Once outside of the for loop, we log the report's status and location on line 315:

310             html_string = "\n<tr><td>" + row_data + "</td></tr>"
311 html_file.write(html_string)
312 html_file.flush()
313 html_string = "\n</table>\n</body></html>"
314 html_file.write(html_string)
315 logger.info('HTML Report completed: ' + target)