|
1
|
|
|
""" |
|
2
|
|
|
SDoc |
|
3
|
|
|
|
|
4
|
|
|
Copyright 2016 Set Based IT Consultancy |
|
5
|
|
|
|
|
6
|
|
|
Licence MIT |
|
7
|
|
|
""" |
|
8
|
|
|
# ---------------------------------------------------------------------------------------------------------------------- |
|
9
|
|
|
import re |
|
10
|
|
|
import csv |
|
11
|
|
|
import io |
|
12
|
|
|
from sdoc.sdoc2 import node_store, in_scope, out_scope |
|
13
|
|
|
from sdoc.sdoc2.node.Node import Node |
|
14
|
|
|
|
|
15
|
|
|
|
|
16
|
|
|
class TableNode(Node): |
|
17
|
|
|
""" |
|
18
|
|
|
SDoc2 node for table. |
|
19
|
|
|
""" |
|
20
|
|
|
|
|
21
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
22
|
|
|
def __init__(self, options): |
|
23
|
|
|
""" |
|
24
|
|
|
Object constructor. |
|
25
|
|
|
|
|
26
|
|
|
:param dict[str,str] options: The options of this table. |
|
27
|
|
|
""" |
|
28
|
|
|
super().__init__('table', options) |
|
29
|
|
|
|
|
30
|
|
|
self.rows = [] |
|
31
|
|
|
""" |
|
32
|
|
|
The table rows. |
|
33
|
|
|
|
|
34
|
|
|
:type: list[list[str]] |
|
35
|
|
|
""" |
|
36
|
|
|
|
|
37
|
|
|
self.column_headers = [] |
|
38
|
|
|
""" |
|
39
|
|
|
The column headers of the table (if any). |
|
40
|
|
|
|
|
41
|
|
|
:type: list[str] |
|
42
|
|
|
""" |
|
43
|
|
|
|
|
44
|
|
|
self.alignments = [] |
|
45
|
|
|
""" |
|
46
|
|
|
The text alignments in the table columns. |
|
47
|
|
|
|
|
48
|
|
|
:type: list[str|None] |
|
49
|
|
|
""" |
|
50
|
|
|
|
|
51
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
52
|
|
|
def get_command(self): |
|
53
|
|
|
""" |
|
54
|
|
|
Returns the command of this node, i.e. table. |
|
55
|
|
|
|
|
56
|
|
|
:rtype: str |
|
57
|
|
|
""" |
|
58
|
|
|
return 'table' |
|
59
|
|
|
|
|
60
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
61
|
|
|
def is_block_command(self): |
|
62
|
|
|
""" |
|
63
|
|
|
Returns True. |
|
64
|
|
|
|
|
65
|
|
|
:rtype: bool |
|
66
|
|
|
""" |
|
67
|
|
|
return True |
|
68
|
|
|
|
|
69
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
70
|
|
|
def is_inline_command(self): |
|
71
|
|
|
""" |
|
72
|
|
|
Returns False. |
|
73
|
|
|
|
|
74
|
|
|
:rtype: bool |
|
75
|
|
|
""" |
|
76
|
|
|
return False |
|
77
|
|
|
|
|
78
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
79
|
|
|
def prepare_content_tree(self): |
|
80
|
|
|
""" |
|
81
|
|
|
Prepares this node for further processing. |
|
82
|
|
|
""" |
|
83
|
|
|
for node_id in self.child_nodes: |
|
84
|
|
|
node = in_scope(node_id) |
|
85
|
|
|
|
|
86
|
|
|
self.extract_table(node) |
|
87
|
|
|
|
|
88
|
|
|
node.prepare_content_tree() |
|
89
|
|
|
|
|
90
|
|
|
out_scope(node) |
|
91
|
|
|
|
|
92
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
93
|
|
|
def extract_table(self, node): |
|
94
|
|
|
""" |
|
95
|
|
|
Extract the table data from a TextNode. |
|
96
|
|
|
|
|
97
|
|
|
:param sdoc.sdoc2.node.Node.Node node: The node which may be interpreted as table. |
|
98
|
|
|
""" |
|
99
|
|
|
temp_table_items = node.argument.split('\n') |
|
100
|
|
|
|
|
101
|
|
|
# Remove empty rows. |
|
102
|
|
|
while '' in temp_table_items: |
|
103
|
|
|
temp_table_items.remove('') |
|
104
|
|
|
|
|
105
|
|
|
# Derive table data. |
|
106
|
|
|
rows = [] |
|
107
|
|
|
for item in temp_table_items: |
|
108
|
|
|
string = io.StringIO(item) |
|
109
|
|
|
reader = csv.reader(string, delimiter='|') |
|
110
|
|
|
for line in reader: |
|
111
|
|
|
row = line |
|
112
|
|
|
row = self.prune_whitespace(row) |
|
113
|
|
|
rows.append(row) |
|
114
|
|
|
|
|
115
|
|
|
if self.has_header(rows): |
|
116
|
|
|
self.column_headers = rows[0] |
|
117
|
|
|
self.rows = rows[2:] |
|
118
|
|
|
self.alignments = self.get_column_alignments(rows[1]) |
|
119
|
|
|
else: |
|
120
|
|
|
self.rows = rows |
|
121
|
|
|
|
|
122
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
123
|
|
|
@staticmethod |
|
124
|
|
|
def has_header(row): |
|
125
|
|
|
""" |
|
126
|
|
|
Returns True if the table has a table header. |
|
127
|
|
|
|
|
128
|
|
|
:param list[str] row: The second row of the table. |
|
129
|
|
|
|
|
130
|
|
|
:rtype: bool |
|
131
|
|
|
""" |
|
132
|
|
|
is_header = True |
|
133
|
|
|
for align in row[1]: |
|
134
|
|
|
header_part = re.findall(':?---+-*:?', align) |
|
135
|
|
|
if not header_part: |
|
136
|
|
|
is_header = False |
|
137
|
|
|
break |
|
138
|
|
|
|
|
139
|
|
|
return is_header |
|
140
|
|
|
|
|
141
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
142
|
|
|
@staticmethod |
|
143
|
|
|
def get_column_alignments(row): |
|
144
|
|
|
""" |
|
145
|
|
|
Sets alignments on table columns. |
|
146
|
|
|
|
|
147
|
|
|
:param list[str] row: The row with hyphens for creating column headers. |
|
148
|
|
|
|
|
149
|
|
|
:rtype: list[str] |
|
150
|
|
|
""" |
|
151
|
|
|
alignments = [] |
|
152
|
|
|
for hyphens in row: |
|
153
|
|
|
hyphens = hyphens.strip() |
|
154
|
|
|
if hyphens.startswith(':') and hyphens.endswith(':'): |
|
155
|
|
|
alignments.append('center') |
|
156
|
|
|
elif hyphens.startswith(':'): |
|
157
|
|
|
alignments.append('left') |
|
158
|
|
|
elif hyphens.endswith(':'): |
|
159
|
|
|
alignments.append('right') |
|
160
|
|
|
else: |
|
161
|
|
|
alignments.append('') |
|
162
|
|
|
|
|
163
|
|
|
return alignments |
|
164
|
|
|
|
|
165
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
|
166
|
|
|
@staticmethod |
|
167
|
|
|
def prune_whitespace(row): |
|
168
|
|
|
""" |
|
169
|
|
|
Strips whitespaces from the text of an each cell. |
|
170
|
|
|
|
|
171
|
|
|
:param list[str] row: The row with text of an each cell. |
|
172
|
|
|
:rtype: list[str] |
|
173
|
|
|
""" |
|
174
|
|
|
clear_row = [] |
|
175
|
|
|
for item in row: |
|
176
|
|
|
clear_text = item.strip() |
|
177
|
|
|
clear_text = re.sub('\s+', ' ', clear_text) |
|
|
|
|
|
|
178
|
|
|
clear_row.append(clear_text) |
|
179
|
|
|
|
|
180
|
|
|
return clear_row |
|
181
|
|
|
|
|
182
|
|
|
# ---------------------------------------------------------------------------------------------------------------------- |
|
183
|
|
|
node_store.register_block_command('table', TableNode) |
|
184
|
|
|
|
Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with
rorRare they interpreted as regular expressions.The escape sequence that was used indicates that you might have intended to write a regular expression.
Learn more about the available escape sequences. in the Python documentation.