-
Notifications
You must be signed in to change notification settings - Fork 116
/
table_creation.py
executable file
·281 lines (232 loc) · 10.8 KB
/
table_creation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#!/usr/bin/env python
# coding=utf-8
"""Examples of using the cmd2 table creation API"""
import functools
import sys
from typing import (
Any,
List,
)
from cmd2 import (
EightBitBg,
EightBitFg,
Fg,
ansi,
)
from cmd2.table_creator import (
AlternatingTable,
BorderedTable,
Column,
HorizontalAlignment,
SimpleTable,
)
# Text styles used in the tables
bold_yellow = functools.partial(ansi.style, fg=Fg.LIGHT_YELLOW, bold=True)
blue = functools.partial(ansi.style, fg=Fg.LIGHT_BLUE)
green = functools.partial(ansi.style, fg=Fg.GREEN)
class DollarFormatter:
"""Example class to show that any object type can be passed as data to TableCreator and converted to a string"""
def __init__(self, val: float) -> None:
self.val = val
def __str__(self) -> str:
"""Returns the value in dollar currency form (e.g. $100.22)"""
return "${:,.2f}".format(self.val)
class Relative:
"""Class used for example data"""
def __init__(self, name: str, relationship: str) -> None:
self.name = name
self.relationship = relationship
class Book:
"""Class used for example data"""
def __init__(self, title: str, year_published: str) -> None:
self.title = title
self.year_published = year_published
class Author:
"""Class used for example data"""
def __init__(self, name: str, birthday: str, place_of_birth: str) -> None:
self.name = name
self.birthday = birthday
self.place_of_birth = place_of_birth
self.books: List[Book] = []
self.relatives: List[Relative] = []
def ansi_print(text):
"""Wraps style_aware_write so style can be stripped if needed"""
ansi.style_aware_write(sys.stdout, text + '\n\n')
def basic_tables():
"""Demonstrates basic examples of the table classes"""
# Table data which demonstrates handling of wrapping and text styles
data_list: List[List[Any]] = list()
data_list.append(["Billy Smith", "123 Sesame St.\n" "Fake Town, USA 33445", DollarFormatter(100333.03)])
data_list.append(
[
"William Longfellow Marmaduke III",
"984 Really Long Street Name Which Will Wrap Nicely\n" "Apt 22G\n" "Pensacola, FL 32501",
DollarFormatter(55135.22),
]
)
data_list.append(
[
"James " + blue("Bluestone"),
bold_yellow("This address has line feeds,\n" "text styles, and wrapping. ")
+ blue("Style is preserved across lines."),
DollarFormatter(300876.10),
]
)
data_list.append(["John Jones", "9235 Highway 32\n" + green("Greenville") + ", SC 29604", DollarFormatter(82987.71)])
# Table Columns (width does not account for any borders or padding which may be added)
columns: List[Column] = list()
columns.append(Column("Name", width=20))
columns.append(Column("Address", width=38))
columns.append(
Column("Income", width=14, header_horiz_align=HorizontalAlignment.RIGHT, data_horiz_align=HorizontalAlignment.RIGHT)
)
st = SimpleTable(columns)
table = st.generate_table(data_list)
ansi_print(table)
bt = BorderedTable(columns)
table = bt.generate_table(data_list)
ansi_print(table)
at = AlternatingTable(columns)
table = at.generate_table(data_list)
ansi_print(table)
def nested_tables():
"""
Demonstrates how to nest tables with styles which conflict with the parent table by setting style_data_text to False.
It also demonstrates coloring various aspects of tables.
"""
# Create data for this example
author_data: List[Author] = []
author_1 = Author("Frank Herbert", "10/08/1920", "Tacoma, Washington")
author_1.books.append(Book("Dune", "1965"))
author_1.books.append(Book("Dune Messiah", "1969"))
author_1.books.append(Book("Children of Dune", "1976"))
author_1.books.append(Book("God Emperor of Dune", "1981"))
author_1.books.append(Book("Heretics of Dune", "1984"))
author_1.books.append(Book("Chapterhouse: Dune", "1985"))
author_1.relatives.append(Relative("Flora Lillian Parkinson", "First Wife"))
author_1.relatives.append(Relative("Beverly Ann Stuart", "Second Wife"))
author_1.relatives.append(Relative("Theresa Diane Shackelford", "Third Wife"))
author_1.relatives.append(Relative("Penelope Herbert", "Daughter"))
author_1.relatives.append(Relative("Brian Patrick Herbert", "Son"))
author_1.relatives.append(Relative("Bruce Calvin Herbert", "Son"))
author_2 = Author("Jane Austen", "12/16/1775", "Steventon, Hampshire, England")
author_2.books.append(Book("Sense and Sensibility", "1811"))
author_2.books.append(Book("Pride and Prejudice", "1813"))
author_2.books.append(Book("Mansfield Park ", "1814"))
author_2.books.append(Book("Emma", "1815"))
author_2.books.append(Book("Northanger Abbey", "1818"))
author_2.books.append(Book("Persuasion", "1818"))
author_2.books.append(Book("Lady Susan", "1871"))
author_2.relatives.append(Relative("James Austen", "Brother"))
author_2.relatives.append(Relative("George Austen", "Brother"))
author_2.relatives.append(Relative("Edward Austen", "Brother"))
author_2.relatives.append(Relative("Henry Thomas Austen", "Brother"))
author_2.relatives.append(Relative("Cassandra Elizabeth Austen", "Sister"))
author_2.relatives.append(Relative("Francis William Austen", "Brother"))
author_2.relatives.append(Relative("Charles John Austen", "Brother"))
author_data.append(author_1)
author_data.append(author_2)
# Define table which presents Author data fields vertically with no header.
# This will be nested in the parent table's first column.
author_columns: List[Column] = list()
author_columns.append(Column("", width=14))
author_columns.append(Column("", width=20))
# The text labels in this table will be bold text. They will also be aligned by the table code.
# When styled text is aligned, a TextStyle.RESET_ALL sequence is inserted between the aligned text
# and the fill characters. Therefore, the Author table will contain TextStyle.RESET_ALL sequences,
# which would interfere with the background color applied by the parent table. To account for this,
# we will manually color the Author tables to match the background colors of the parent AlternatingTable's
# rows and set style_data_text to False in the Author column.
odd_author_tbl = SimpleTable(author_columns, data_bg=EightBitBg.GRAY_0)
even_author_tbl = SimpleTable(author_columns, data_bg=EightBitBg.GRAY_15)
# Define AlternatingTable for books checked out by people in the first table.
# This will be nested in the parent table's second column.
books_columns: List[Column] = list()
books_columns.append(Column(ansi.style("Title", bold=True), width=25))
books_columns.append(
Column(
ansi.style("Published", bold=True),
width=9,
header_horiz_align=HorizontalAlignment.RIGHT,
data_horiz_align=HorizontalAlignment.RIGHT,
)
)
books_tbl = AlternatingTable(
books_columns,
column_borders=False,
border_fg=EightBitFg.GRAY_15,
header_bg=EightBitBg.GRAY_0,
odd_bg=EightBitBg.GRAY_0,
even_bg=EightBitBg.GRAY_15,
)
# Define BorderedTable for relatives of the author
# This will be nested in the parent table's third column.
relative_columns: List[Column] = list()
relative_columns.append(Column(ansi.style("Name", bold=True), width=25))
relative_columns.append(Column(ansi.style("Relationship", bold=True), width=12))
# Since the header labels are bold, we have the same issue as the Author table. Therefore, we will manually
# color Relatives tables to match the background colors of the parent AlternatingTable's rows and set style_data_text
# to False in the Relatives column.
odd_relatives_tbl = BorderedTable(
relative_columns,
border_fg=EightBitFg.GRAY_15,
border_bg=EightBitBg.GRAY_0,
header_bg=EightBitBg.GRAY_0,
data_bg=EightBitBg.GRAY_0,
)
even_relatives_tbl = BorderedTable(
relative_columns,
border_fg=EightBitFg.GRAY_0,
border_bg=EightBitBg.GRAY_15,
header_bg=EightBitBg.GRAY_15,
data_bg=EightBitBg.GRAY_15,
)
# Define parent AlternatingTable which contains Author and Book tables
parent_tbl_columns: List[Column] = list()
# All of the nested tables already have background colors. Set style_data_text
# to False so the parent AlternatingTable does not apply background color to them.
parent_tbl_columns.append(
Column(ansi.style("Author", bold=True), width=odd_author_tbl.total_width(), style_data_text=False)
)
parent_tbl_columns.append(Column(ansi.style("Books", bold=True), width=books_tbl.total_width(), style_data_text=False))
parent_tbl_columns.append(
Column(ansi.style("Relatives", bold=True), width=odd_relatives_tbl.total_width(), style_data_text=False)
)
parent_tbl = AlternatingTable(
parent_tbl_columns,
column_borders=False,
border_fg=EightBitFg.GRAY_93,
header_bg=EightBitBg.GRAY_0,
odd_bg=EightBitBg.GRAY_0,
even_bg=EightBitBg.GRAY_15,
)
# Construct the tables
parent_table_data: List[List[Any]] = []
for row, author in enumerate(author_data, start=1):
# First build the author table and color it based on row number
author_tbl = even_author_tbl if row % 2 == 0 else odd_author_tbl
# This table has three rows and two columns
table_data = [
[ansi.style("Name", bold=True), author.name],
[ansi.style("Birthday", bold=True), author.birthday],
[ansi.style("Place of Birth", bold=True), author.place_of_birth],
]
# Build the author table string
author_tbl_str = author_tbl.generate_table(table_data, include_header=False, row_spacing=0)
# Now build this author's book table
table_data = [[book.title, book.year_published] for book in author.books]
book_tbl_str = books_tbl.generate_table(table_data)
# Lastly build the relatives table and color it based on row number
relatives_tbl = even_relatives_tbl if row % 2 == 0 else odd_relatives_tbl
table_data = [[relative.name, relative.relationship] for relative in author.relatives]
relatives_tbl_str = relatives_tbl.generate_table(table_data)
# Add these tables to the parent table's data
parent_table_data.append(['\n' + author_tbl_str, '\n' + book_tbl_str + '\n\n', '\n' + relatives_tbl_str + '\n\n'])
# Build the parent table
top_table_str = parent_tbl.generate_table(parent_table_data)
ansi_print(top_table_str)
if __name__ == '__main__':
# Default to terminal mode so redirecting to a file won't include the ANSI style sequences
ansi.allow_style = ansi.AllowStyle.TERMINAL
basic_tables()
nested_tables()