11from typing import List
2+ from math import floor , ceil
3+
4+ # constants
5+ ALIGN_LEFT = 0
6+ ALIGN_CENTER = 1
7+ ALIGN_RIGHT = 2
28
39
410class TableToAscii :
5- def __init__ (
6- self ,
7- header_row : List [str ],
8- body : List [List [str ]],
9- footer_row : List [str ],
10- ):
11- self .header_row = header_row
12- self .body = body
13- self .footer_row = footer_row
14- self .cell_width = 5
11+ """Class used to convert a 2D Python table to ASCII text"""
12+
13+ def __init__ (self , header_row : List , body : List [List ], footer_row : List ):
14+ self .__header_row = header_row
15+ self .__body = body
16+ self .__footer_row = footer_row
17+ self .__cell_width = 5 # ! TODO: Remove
18+ self .__cell_widths = [5 , 5 , 5 , 5 , 5 ] # TODO: make this automatic
1519 """
1620 ╔═════╦═══════════════════════╗
1721 ║ # ║ G H R S ║
@@ -32,78 +36,103 @@ def __init__(
3236 i11111j11111k11111k11111k11111l
3337 """
3438 self .parts = {
35- "top_left_corner" : "╔" , # 0
36- "top_bottom_edge " : "═" , # 1
37- "first_col_top_tee" : "╦" , # 2
38- "top_tee" : "═" , # 3
39- "top_right_corner" : "╗" , # 4
40- "left_right_edge " : "║" , # 5
41- "first_col_sep" : "║" , # 6
42- "middle_edge" : " " , # 7
43- "header_left_tee" : "╟" , # 8
44- "header_row_sep" : "─" , # 9
45- "first_col_header_cross" : "╫" , # a
46- "header_row_cross" : "─" , # b
47- "right_tee" : "╢" , # c
48- "footer_left_tee" : "╟" , # d
49- "footer_row_sep" : "─" , # e
50- "first_col_footer_cross" : "╫" , # f
51- "footer_row_cross" : "─" , # g
52- "bottom_left_corner" : "╚" , # i
53- "first_col_bottom_tee" : "╩" , # j
54- "bottom_tee" : "═" , # k
55- "bottom_right_corner" : "╝" , # l
39+ "top_left_corner" : "╔" , # 0
40+ "top_and_bottom_edge " : "═" , # 1
41+ "first_col_top_tee" : "╦" , # 2
42+ "top_tee" : "═" , # 3
43+ "top_right_corner" : "╗" , # 4
44+ "left_and_right_edge " : "║" , # 5
45+ "first_col_sep" : "║" , # 6
46+ "middle_edge" : " " , # 7
47+ "header_left_tee" : "╟" , # 8
48+ "header_row_sep" : "─" , # 9
49+ "first_col_header_cross" : "╫" , # a
50+ "header_row_cross" : "─" , # b
51+ "right_tee" : "╢" , # c
52+ "footer_left_tee" : "╟" , # d
53+ "footer_row_sep" : "─" , # e
54+ "first_col_footer_cross" : "╫" , # f
55+ "footer_row_cross" : "─" , # g
56+ "bottom_left_corner" : "╚" , # i
57+ "first_col_bottom_tee" : "╩" , # j
58+ "bottom_tee" : "═" , # k
59+ "bottom_right_corner" : "╝" , # l
5660 }
5761
62+ def __pad (self , text : str , width : int , alignment : int = ALIGN_CENTER ):
63+ if alignment == ALIGN_LEFT :
64+ return f" { text } " + (" " * (width - len (text ) - 2 ))
65+ if alignment == ALIGN_CENTER :
66+ before = " " * floor ((width - len (text ) - 2 ) / 2 )
67+ after = " " * ceil ((width - len (text ) - 2 ) / 2 )
68+ return before + f" { text } " + after
69+ if alignment == ALIGN_RIGHT :
70+ return (" " * (width - len (text ) - 2 )) + f" { text } "
71+ raise ValueError (f"The value '{ alignment } ' is not valid for alignment." )
72+
73+ def __top_edge_to_ascii (self ) -> str :
74+ """Assembles the top edge of the ascii table"""
75+ # top-left corner of the table
76+ output : str = self .parts ["top_left_corner" ]
77+ # top edge above the first column
78+ output += self .parts ["top_and_bottom_edge" ] * self .__cell_widths [0 ]
79+ # tee separating first column from the rest of the table
80+ output += self .parts ["first_col_top_tee" ]
81+ # add remaining columns
82+ for i in range (len (self .__header_row ) - 1 ):
83+ output += self .parts ["top_and_bottom_edge" ] * self .__cell_widths [i ]
84+ output += self .parts ["top_tee" ]
85+ # replace last top tee with top-right corner
86+ output = output [0 :- 1 ] + self .parts ["top_right_corner" ]
87+ return output
88+
89+ def __header_row_to_ascii (self ) -> str :
90+ """Assembles the header row line of the ascii table"""
91+ # add left edge of table
92+ output : str = self .parts ["left_and_right_edge" ]
93+ # add first column contents and padding
94+ output += self .__pad (str (self .__header_row [0 ]), self .__cell_widths [0 ])
95+ # separate first column from the rest of the table
96+ output += self .parts ["first_col_sep" ]
97+ # add the rest of the headers
98+ output += self .parts ["middle_edge" ].join (
99+ self .__pad (str (val ), self .__cell_widths [i + 1 ])
100+ for i , val in enumerate (self .__header_row [1 :])
101+ )
102+ # add right edge of the table
103+ output += self .parts ["left_and_right_edge" ]
104+ return output
105+
106+ def __header_sep_to_ascii (self ) -> str :
107+ """Assembles the seperator below the header of the ascii table"""
108+ # add left edge tee
109+ output : str = self .parts ["header_left_tee" ]
110+ # top edge above the first column
111+ output += self .parts ["header_row_sep" ] * self .__cell_widths [0 ]
112+ # tee separating first column from the rest of the table
113+ output += self .parts ["first_col_header_cross" ]
114+ # add remaining columns
115+ for i in range (len (self .__header_row ) - 1 ):
116+ output += self .parts ["header_row_sep" ] * self .__cell_widths [i ]
117+ output += self .parts ["header_row_cross" ]
118+ # replace last top tee with top-right corner
119+ output = output [0 :- 1 ] + self .parts ["right_tee" ]
120+ return output
121+
58122 def to_ascii (self ) -> str :
59- cols = len (self .header_row )
123+ cols = len (self .__header_row )
60124 # create table header
61125 table = [
62- # ╔
63- self .parts ["top_left_corner" ]
64- # ═════╦
65- + self .parts ["top_bottom_edge" ] * self .cell_width + self .parts ["first_col_top_tee" ]
66- #
67- + (
68- (self .parts ["top_bottom_edge" ] * self .cell_width + self .parts ["top_tee" ])
69- * (cols - 1 )
70- )[0 :- 1 ]
71- # ╗
72- + self .parts ["top_right_corner" ],
73- # ║
74- self .parts ["left_right_edge" ]
75- # # ║
76- + f" { self .header_row [0 ]} " + self .parts ["first_col_sep" ]
77- # G H R S
78- + self .parts ["middle_edge" ].join (
79- " " + val + " " for val in self .header_row [1 :]
80- )
81- # ║
82- + self .parts ["left_right_edge" ],
83- # ╟
84- self .parts ["header_left_tee" ]
85- # ─────╫
86- + (
87- self .parts ["header_row_sep" ] * self .cell_width
88- + self .parts ["first_col_header_cross" ]
89- )
90- # ───────────────────────
91- + (
92- (
93- self .parts ["header_row_sep" ] * self .cell_width
94- + self .parts ["header_row_cross" ]
95- )
96- * (cols - 1 )
97- )[0 :- 1 ]
98- # ╢
99- + self .parts ["right_tee" ],
126+ self .__top_edge_to_ascii (),
127+ self .__header_row_to_ascii (),
128+ self .__header_sep_to_ascii (),
100129 ]
101130 # add table body
102- for p in self .body :
131+ for p in self .__body :
103132 # add table row
104133 table += [
105134 # ║
106- self .parts ["left_right_edge " ]
135+ self .parts ["left_and_right_edge " ]
107136 +
108137 # 1 ║
109138 f" { p [0 ]} "
@@ -113,45 +142,48 @@ def to_ascii(self) -> str:
113142 f"{ p [i ].rjust (4 )} " for i in range (1 , cols )
114143 )
115144 # ║
116- + self .parts ["left_right_edge " ]
145+ + self .parts ["left_and_right_edge " ]
117146 ]
118147 # footer row
119148 table += [
120149 # ╟
121150 self .parts ["footer_left_tee" ]
122151 # ─────╫
123152 + (
124- self .parts ["footer_row_sep" ] * self .cell_width
153+ self .parts ["footer_row_sep" ] * self .__cell_width
125154 + self .parts ["first_col_footer_cross" ]
126155 )
127156 # ───────────────────────
128157 + (
129158 (
130- self .parts ["footer_row_sep" ] * self .cell_width
159+ self .parts ["footer_row_sep" ] * self .__cell_width
131160 + self .parts ["footer_row_cross" ]
132161 )
133162 * (cols - 1 )
134163 )[0 :- 1 ]
135164 # ╢
136165 + self .parts ["right_tee" ],
137166 # ║
138- self .parts ["left_right_edge " ]
167+ self .parts ["left_and_right_edge " ]
139168 # SUM ║
140- + f"{ self .footer_row [0 ].rjust (4 )} " + self .parts ["first_col_sep" ]
169+ + f"{ self .__footer_row [0 ].rjust (4 )} " + self .parts ["first_col_sep" ]
141170 # 120 ║ 120 ║ 120 ║ 120 ║
142171 + self .parts ["middle_edge" ].join (
143- f"{ self .footer_row [i ].rjust (4 )} " for i in range (1 , cols )
172+ f"{ self .__footer_row [i ].rjust (4 )} " for i in range (1 , cols )
144173 )
145174 # ║
146- + self .parts ["left_right_edge " ],
175+ + self .parts ["left_and_right_edge " ],
147176 # ╚
148177 self .parts ["bottom_left_corner" ]
149178 # ═════╩
150- + self .parts ["top_bottom_edge " ] * self .cell_width
179+ + self .parts ["top_and_bottom_edge " ] * self .__cell_width
151180 + self .parts ["first_col_bottom_tee" ]
152181 # ════════════════════════
153182 + (
154- (self .parts ["top_bottom_edge" ] * self .cell_width + self .parts ["bottom_tee" ])
183+ (
184+ self .parts ["top_and_bottom_edge" ] * self .__cell_width
185+ + self .parts ["bottom_tee" ]
186+ )
155187 * (cols - 1 )
156188 )[0 :- 1 ]
157189 # ╗
@@ -160,9 +192,7 @@ def to_ascii(self) -> str:
160192 return "\n " .join (table )
161193
162194
163- def table2ascii (
164- header_row : List [str ], body : List [List [str ]], footer_row : List [str ]
165- ) -> str :
195+ def table2ascii (header_row : List , body : List [List ], footer_row : List ) -> str :
166196 """Convert a 2D Python table to ASCII text
167197 #TODO: add param documentation
168198 """
0 commit comments