Skip to content

Commit

Permalink
add table
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinzbw committed Jan 27, 2018
1 parent ee10c26 commit a9bfd21
Show file tree
Hide file tree
Showing 9 changed files with 1,014 additions and 17 deletions.
86 changes: 69 additions & 17 deletions psense.py
Expand Up @@ -2,6 +2,9 @@
import subprocess as sp
import re
import argparse
from terminaltables import SingleTable
from textwrap import wrap
import shutil

def parse_psi_output(output):
result = ""
Expand Down Expand Up @@ -248,8 +251,65 @@ def rename_psi_out(exp, extend_name):
parts = exp.split(":=")
newname = parts[0].replace("[", extend_name + "[")
return newname + ":=" + parts[-1]

def run_file(file, output_file, explict_eps, noise_percentage, metrics, custom_metric_file, psi_timeout, math_timeout, verbose):

def print_plain_results(message, output_file):
if output_file:
with open(output_file, "a") as f:
f.write(message + "\n")
else:
print(message)

def print_table_results(title, data, output_file):
table = SingleTable(data, title)
max_width = int(shutil.get_terminal_size()[0] / len(data[0]))
for j in range(1, len(data[0])):
table.table_data[1][j] = '\n'.join(wrap(data[1][j], max_width))
for j in range(1, len(data[0])):
table.table_data[2][j] = '\n'.join(data[2][j].split(", "))
print_plain_results(table.table, output_file)


def parse_math_out(math_out):
table_dict = {}
func_type = "Unknown"
prompt_func_type = "Function Type:"
prompt_metrics = ["Expectation Distance", "KS Distance", "TVD", "KL Divergence"]
prompt_info = ["Expression", "Maximum", "Linear"]
lines = math_out.split("\n")
lines = [line for line in lines if line]
i = 0
while i < len(lines):
if lines[i] == prompt_func_type:
func_type = lines[i+1]
i += 2
elif lines[i] in prompt_metrics:
table_dict[lines[i]] = [lines[i+1], lines[i+3].replace("{", "").replace("}", ""), lines[i+5]]
i += 6
else:
i += 1
metrics = list(table_dict.keys())
table_data = [[""] + metrics]
for i in range(len(prompt_info)):
row = [prompt_info[i]]
for key in metrics:
row.append(table_dict[key][i])
table_data.append(row)
return table_data, func_type

def print_results(index, math_file, math_out, output_file, plain):
if not math_out:
failure_message = "Mathematica fails or runs out of time: " + math_file
print_plain_results(failure_message, output_file)
elif plain:
success_message = "Analyzed parameter " + str(index+1) + ":\n" + math_out
print_plain_results(success_message, output_file)
else:
table_math_out, func_type = parse_math_out(math_out)
message = "Function Type:\n" + func_type
print_plain_results(message, output_file)
print_table_results("Analyzed parameter " + str(index+1), table_math_out, output_file)

def run_file(file, output_file, explict_eps, noise_percentage, metrics, custom_metric_file, psi_timeout, math_timeout, verbose, plain):
psi_file = file
psi_file_name = os.path.basename(psi_file)
psi_file_dir = os.path.dirname(psi_file)
Expand Down Expand Up @@ -329,17 +389,7 @@ def run_file(file, output_file, explict_eps, noise_percentage, metrics, custom_m
math_run = generate_math_exp(math_output_files[i], psi_func_name, psi_pdf_func_name, psi_eps_func_name, None, None, psi_func_num_param, code_eps_params[i], explict_eps, noise_percentage, metrics, custom_metric_name)
f.write(math_run + "\n")
math_out = run_math(math_files[i], math_timeout)
if output_file:
with open(output_file, "a") as f:
if math_out:
f.write("Changed parameter" + str(i+1) + ":\n" + math_out + "\n")
else:
f.write("Mathematica runs out of time: " + math_files[i] + "\n")
else:
if math_out:
print("Changed parameter", i+1, ":\n", math_out)
else:
print("Mathematica runs out of time: " + math_files[i])
print_results(i, math_files[i], math_out, output_file, plain)

def exit_message(message):
print(message)
Expand All @@ -359,12 +409,13 @@ def main():
parser.add_argument("-metric", nargs="?", help=help_metric)
parser.add_argument("-tp", nargs="?", help="Optional PSI timeout (second)")
parser.add_argument("-tm", nargs="?", help="Optional Mathematica timeout (second)")
parser.add_argument("-plain", action="store_true", help="Print raw outputs")
parser.add_argument("-verbose", action="store_true", help="Print all outputs of PSI")
argv = parser.parse_args(sys.argv[1:])
if argv.f and not os.path.isfile(argv.f):
exit_message("PSI file doesn\"t exist")
exit_message("PSI file doesn\'t exist")
if argv.r and not os.path.isdir(argv.r):
exit_message("Directory doesn\"t exist")
exit_message("Directory doesn\'t exist")
if argv.tm and not argv.tm.isdigit():
exit_message("Timeout parameter is invalid")
if argv.tp and not argv.tp.isdigit():
Expand Down Expand Up @@ -408,13 +459,14 @@ def main():
math_timeout = argv.tm
psi_timeout = argv.tp
verbose = argv.verbose
plain = argv.plain
if output_file:
if os.path.exists(output_file):
os.remove(output_file)
else:
create_dirs_from_path(os.path.dirname(output_file))
if argv.f:
run_file(argv.f, output_file, explict_eps, noise_percentage, metrics, custom_metric_file, psi_timeout, math_timeout, verbose)
run_file(argv.f, output_file, explict_eps, noise_percentage, metrics, custom_metric_file, psi_timeout, math_timeout, verbose, plain)
else:
for filename in os.listdir(argv.r):
if filename.endswith(".psi"):
Expand All @@ -425,7 +477,7 @@ def main():
f.write("\n" + file + ":\n")
else:
print("\n" + file + ":")
run_file(file, output_file, explict_eps, noise_percentage, metrics, custom_metric_file, psi_timeout, math_timeout, verbose)
run_file(file, output_file, explict_eps, noise_percentage, metrics, custom_metric_file, psi_timeout, math_timeout, verbose, plain)

if __name__ == "__main__":
main()
17 changes: 17 additions & 0 deletions terminaltables/__init__.py
@@ -0,0 +1,17 @@
"""Generate simple tables in terminals from a nested list of strings.
Use SingleTable or DoubleTable instead of AsciiTable for box-drawing characters.
https://github.com/Robpol86/terminaltables
https://pypi.python.org/pypi/terminaltables
"""

from terminaltables.ascii_table import AsciiTable # noqa
from terminaltables.github_table import GithubFlavoredMarkdownTable # noqa
from terminaltables.other_tables import DoubleTable # noqa
from terminaltables.other_tables import SingleTable # noqa
from terminaltables.other_tables import PorcelainTable # noqa

__author__ = '@Robpol86'
__license__ = 'MIT'
__version__ = '3.1.0'
55 changes: 55 additions & 0 deletions terminaltables/ascii_table.py
@@ -0,0 +1,55 @@
"""AsciiTable is the main table class. To be inherited by other tables. Define convenience methods here."""

from terminaltables.base_table import BaseTable
from terminaltables.terminal_io import terminal_size
from terminaltables.width_and_alignment import column_max_width, max_dimensions, table_width


class AsciiTable(BaseTable):
"""Draw a table using regular ASCII characters, such as ``+``, ``|``, and ``-``.
:ivar iter table_data: List (empty or list of lists of strings) representing the table.
:ivar str title: Optional title to show within the top border of the table.
:ivar bool inner_column_border: Separates columns.
:ivar bool inner_footing_row_border: Show a border before the last row.
:ivar bool inner_heading_row_border: Show a border after the first row.
:ivar bool inner_row_border: Show a border in between every row.
:ivar bool outer_border: Show the top, left, right, and bottom border.
:ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center.
:ivar int padding_left: Number of spaces to pad on the left side of every cell.
:ivar int padding_right: Number of spaces to pad on the right side of every cell.
"""

def column_max_width(self, column_number):
"""Return the maximum width of a column based on the current terminal width.
:param int column_number: The column number to query.
:return: The max width of the column.
:rtype: int
"""
inner_widths = max_dimensions(self.table_data)[0]
outer_border = 2 if self.outer_border else 0
inner_border = 1 if self.inner_column_border else 0
padding = self.padding_left + self.padding_right
return column_max_width(inner_widths, column_number, outer_border, inner_border, padding)

@property
def column_widths(self):
"""Return a list of integers representing the widths of each table column without padding."""
if not self.table_data:
return list()
return max_dimensions(self.table_data)[0]

@property
def ok(self): # Too late to change API. # pylint: disable=invalid-name
"""Return True if the table fits within the terminal width, False if the table breaks."""
return self.table_width <= terminal_size()[0]

@property
def table_width(self):
"""Return the width of the table including padding and borders."""
outer_widths = max_dimensions(self.table_data, self.padding_left, self.padding_right)[2]
outer_border = 2 if self.outer_border else 0
inner_border = 1 if self.inner_column_border else 0
return table_width(outer_widths, outer_border, inner_border)

0 comments on commit a9bfd21

Please sign in to comment.