# -*- coding: utf-8 -*-
# Copyright (c) 2009-2014, Luke Maurits <luke@maurits.id.au>
# All rights reserved.
# With contributions from:
# * Chris Clark
# * Klein Stephane
# * John Filleau
# PTable is forked from original Google Code page in April, 2015, and now
# maintained by Kane Blueriver <kxxoling@gmail.com>.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# Taken from :
# https://github.com/kxxoling/PTable
# https://pypi.python.org/pypi/PTable
"""
Pretty table factory
********************
"""
import csv
from ._compact import HTMLParser
from .prettytable import PrettyTable
[docs]def from_csv(filepath, field_names=None, **kwargs):
"""Loads table from CSV file
:param filepath: param field_names: (Default value = None)
:param field_names: (Default value = None)
"""
fmtparams = {}
for param in ["delimiter", "doublequote", "escapechar", "lineterminator",
"quotechar", "quoting", "skipinitialspace", "strict"]:
if param in kwargs:
fmtparams[param] = kwargs.pop(param)
if fmtparams:
reader = csv.reader(filepath, **fmtparams)
else:
dialect = csv.Sniffer().sniff(filepath.read(1024))
filepath.seek(0)
reader = csv.reader(filepath, dialect)
table = PrettyTable(**kwargs)
if field_names:
table.field_names = field_names
next(reader)
else:
table.field_names = [x.strip() for x in next(reader)]
for row in reader:
table.add_row([x.strip() for x in row if len(x.strip()) > 0])
return table
[docs]def from_db_cursor(cursor, **kwargs):
"""
:param cursor:
:param kwargs:
"""
if cursor.description:
table = PrettyTable(**kwargs)
table.field_names = [col[0] for col in cursor.description]
for row in cursor.fetchall():
table.add_row(row)
return table
return None
[docs]class TableHandler(HTMLParser):
"""TableHandler"""
def __init__(self, **kwargs):
HTMLParser.__init__(self)
self.kwargs = kwargs
self.tables = []
self.last_row = []
self.rows = []
self.max_row_width = 0
self.active = None
self.last_content = ""
self.is_last_row_header = False
self.colspan = 0
[docs] def handle_starttag(self, tag, attrs):
"""
:param tag: param attrs:
:param attrs:
"""
self.active = tag
if tag == "th":
self.is_last_row_header = True
for (key, value) in attrs:
if key == "colspan":
self.colspan = int(value)
[docs] def handle_endtag(self, tag):
"""
:param tag:
"""
if tag in ["th", "td"]:
stripped_content = self.last_content.strip()
self.last_row.append(stripped_content)
if self.colspan:
for _ in range(1, self.colspan):
self.last_row.append("")
self.colspan = 0
if tag == "tr":
self.rows.append(
(self.last_row, self.is_last_row_header))
self.max_row_width = max(self.max_row_width, len(self.last_row))
self.last_row = []
self.is_last_row_header = False
if tag == "table":
table = self.generate_table(self.rows)
self.tables.append(table)
self.rows = []
self.last_content = " "
self.active = None
[docs] def handle_data(self, data):
"""
:param data:
"""
self.last_content += data
[docs] def generate_table(self, rows):
"""Generates from a list of rows a PrettyTable object.
:param rows:
"""
table = PrettyTable(**self.kwargs)
for row in self.rows:
if len(row[0]) < self.max_row_width:
appends = self.max_row_width - len(row[0])
for _ in range(1, appends):
row[0].append("-")
if row[1] is True:
self.make_fields_unique(row[0])
table.field_names = row[0]
else:
table.add_row(row[0])
return table
[docs] @staticmethod
def make_fields_unique(fields):
"""iterates over the row and make each field unique
:param fields: fields
"""
for i in range(0, len(fields)):
for j in range(i + 1, len(fields)):
if fields[i] == fields[j]:
fields[j] += "'"
[docs]def from_html(html_code, **kwargs):
"""Generates a list of PrettyTables from a string of HTML code.
Each <table> in the HTML becomes one PrettyTable object.
:param html_code: HTML code
:type html_code: str
:param kwargs: additional arguments
"""
parser = TableHandler(**kwargs)
parser.feed(html_code)
return parser.tables
[docs]def from_html_one(html_code, **kwargs):
"""Generates a PrettyTables from a string of HTML code which contains only
a single <table>
:param html_code: HTML code
:type html_code: str
:param kwargs: additional arguments
"""
tables = from_html(html_code, **kwargs)
try:
assert len(tables) == 1
except AssertionError:
raise Exception("More than one <table> in provided HTML code!"
+ " Use from_html instead.")
return tables[0]
[docs]def from_md(markdown, **kwargs):
"""Generate PrettyTable from markdown string.
:param markdown: markdown type string.
:param kwargs: additional arguments
:returns: a PrettyTable object.
"""
rows = markdown.split('\n')
title_row = rows[0]
content_rows = rows[2:]
table = PrettyTable(**kwargs)
table.field_names = split_md_row(title_row)
list(map(table.add_row, list(map(split_md_row,
[x for x in content_rows if x]))))
return table
[docs]def strip_md_content(markdown):
"""Strip the blank space and `:` in markdown table content cell.
:param markdown: a row of markdown table
:returns: stripped content cell
"""
return markdown.strip().strip(':').strip()
[docs]def split_md_row(row):
"""Split markdown table.
:param row: a row of markdown table
:returns: Split content list
"""
return [strip_md_content(s) for s in row.strip('|').split('|')]