wordhelpers 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wordhelpers/__init__.py
CHANGED
|
@@ -81,7 +81,7 @@ def replace_placeholder_with_table(
|
|
|
81
81
|
|
|
82
82
|
def inject_table(
|
|
83
83
|
doc_obj: _Document,
|
|
84
|
-
table: dict,
|
|
84
|
+
table: dict | WordTableModel,
|
|
85
85
|
placeholder: str,
|
|
86
86
|
remove_leading_para: bool = True,
|
|
87
87
|
remove_placeholder: bool = True,
|
|
@@ -93,21 +93,29 @@ def inject_table(
|
|
|
93
93
|
After moving the Word table after the placeholder paragraph, delete the
|
|
94
94
|
placeholder paragraph.
|
|
95
95
|
"""
|
|
96
|
+
|
|
96
97
|
# Locate the paragraph from the supplied placeholder text
|
|
97
98
|
paragraph: Paragraph = get_para_by_string(doc_obj, placeholder)
|
|
99
|
+
if not paragraph:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f'WARNING: Could not locate placeholder "{placeholder}"'
|
|
102
|
+
)
|
|
98
103
|
|
|
99
104
|
# Build the word table and add it to the end of the document
|
|
100
|
-
table
|
|
105
|
+
table = (
|
|
106
|
+
build_table(
|
|
107
|
+
doc_obj, table, remove_leading_para=remove_leading_para
|
|
108
|
+
)
|
|
109
|
+
if isinstance(table, dict)
|
|
110
|
+
else build_table(doc_obj, table.model_dump(), remove_leading_para=remove_leading_para)
|
|
111
|
+
)
|
|
101
112
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
else:
|
|
105
|
-
# Move the Word table to a new paragraph immediately after the placeholder paragraph
|
|
106
|
-
paragraph._p.addnext(table._tbl)
|
|
113
|
+
# Move the Word table to a new paragraph immediately after the placeholder paragraph
|
|
114
|
+
paragraph._p.addnext(table._tbl)
|
|
107
115
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
if remove_placeholder:
|
|
117
|
+
# Delete the placeholder paragraph
|
|
118
|
+
delete_paragraph(paragraph)
|
|
111
119
|
|
|
112
120
|
|
|
113
121
|
def build_table(
|
wordhelpers/pydantic_models.py
CHANGED
|
@@ -63,34 +63,38 @@ class WordTableModel(BaseModel):
|
|
|
63
63
|
style: str | None = None
|
|
64
64
|
rows: list[WordRowModel] = Field(default_factory=list)
|
|
65
65
|
|
|
66
|
+
def add_row(
|
|
67
|
+
self,
|
|
68
|
+
width: int,
|
|
69
|
+
text: list[str] = [],
|
|
70
|
+
merge_cols: list[int] = [],
|
|
71
|
+
background_color: str | None = None,
|
|
72
|
+
style: str | None = None,
|
|
73
|
+
alignment: AlignmentEnum | None = None,
|
|
74
|
+
) -> None:
|
|
66
75
|
|
|
67
|
-
def add_row(self,
|
|
68
|
-
width: int,
|
|
69
|
-
text: list[str] = [],
|
|
70
|
-
merge_cols: list[int] = [],
|
|
71
|
-
background_color: str | None = None,
|
|
72
|
-
style: str | None = None,
|
|
73
|
-
alignment: AlignmentEnum | None = None,
|
|
74
|
-
) -> None:
|
|
75
|
-
|
|
76
76
|
# Make sure width is same as existing rows if any
|
|
77
77
|
if self.rows:
|
|
78
78
|
existing_width = len(self.rows[0].cells)
|
|
79
79
|
if width != existing_width:
|
|
80
|
-
raise ValueError(
|
|
81
|
-
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"New row width {width} does not match existing row width {existing_width}"
|
|
82
|
+
)
|
|
83
|
+
|
|
82
84
|
# Make sure text length is less than the row width minus merged columns
|
|
83
85
|
num_merge_cols = len(merge_cols)
|
|
84
86
|
if len(text) > width - num_merge_cols:
|
|
85
|
-
raise ValueError(
|
|
86
|
-
|
|
87
|
+
raise ValueError(
|
|
88
|
+
f"Text length {len(text)} exceeded expected length {width - num_merge_cols} based on width and merge_cols"
|
|
89
|
+
)
|
|
90
|
+
|
|
87
91
|
# Make sure merge_cols are valid
|
|
88
92
|
for col in merge_cols:
|
|
89
93
|
if not isinstance(col, int):
|
|
90
94
|
raise ValueError(f"merge_cols must contain integers, got: {type(col)}")
|
|
91
95
|
if col < 0 or col >= width:
|
|
92
96
|
raise ValueError(f"merge_cols contains invalid column index: {col}")
|
|
93
|
-
|
|
97
|
+
|
|
94
98
|
# Build the cells
|
|
95
99
|
cells: list = []
|
|
96
100
|
for i in range(width):
|
|
@@ -101,37 +105,68 @@ class WordTableModel(BaseModel):
|
|
|
101
105
|
# Build a normal cell
|
|
102
106
|
paragraphs: list = []
|
|
103
107
|
if text:
|
|
104
|
-
paragraphs = [
|
|
105
|
-
|
|
108
|
+
paragraphs = [
|
|
109
|
+
WordParagraphModel(
|
|
110
|
+
style=style, alignment=alignment, text=[text.pop(0)]
|
|
111
|
+
)
|
|
112
|
+
]
|
|
113
|
+
cells.append(
|
|
114
|
+
WordCellModel(
|
|
115
|
+
background_color=background_color, paragraphs=paragraphs
|
|
116
|
+
)
|
|
117
|
+
)
|
|
106
118
|
# Re-build the rows
|
|
107
119
|
rows: list = self.rows.copy()
|
|
108
120
|
rows.append(WordRowModel(cells=cells))
|
|
109
121
|
self.rows = rows
|
|
110
122
|
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
def add_text_to_row(
|
|
124
|
+
self,
|
|
125
|
+
row_index: int,
|
|
126
|
+
text: list[str],
|
|
127
|
+
style: str | None = None,
|
|
128
|
+
alignment: AlignmentEnum | None = None,
|
|
129
|
+
) -> None:
|
|
113
130
|
# Validate row_index
|
|
114
131
|
if row_index < 0 or row_index >= len(self.rows):
|
|
115
132
|
raise IndexError(f"Row index {row_index} out of range")
|
|
116
|
-
|
|
133
|
+
|
|
117
134
|
# Make sure text length is less than the row width minus merged columns
|
|
118
|
-
num_merge_cols = len(
|
|
135
|
+
num_merge_cols = len(
|
|
136
|
+
[
|
|
137
|
+
cell
|
|
138
|
+
for cell in self.rows[row_index].cells
|
|
139
|
+
if isinstance(cell, str) and cell == "merge"
|
|
140
|
+
]
|
|
141
|
+
)
|
|
119
142
|
width = len(self.rows[row_index].cells)
|
|
120
143
|
if len(text) > width - num_merge_cols:
|
|
121
|
-
raise ValueError(
|
|
122
|
-
|
|
144
|
+
raise ValueError(
|
|
145
|
+
f"Text length {len(text)} exceeded expected length {width - num_merge_cols} based on width and merge_cols"
|
|
146
|
+
)
|
|
147
|
+
|
|
123
148
|
rows = self.rows.copy()
|
|
124
149
|
row = rows[row_index]
|
|
125
150
|
for cell in row.cells:
|
|
126
151
|
if isinstance(cell, str) and cell == "merge":
|
|
127
152
|
continue # Skip merge cells
|
|
128
153
|
if text:
|
|
129
|
-
cell.paragraphs.append(
|
|
154
|
+
cell.paragraphs.append(
|
|
155
|
+
WordParagraphModel(
|
|
156
|
+
style=style, alignment=alignment, text=[text.pop(0)]
|
|
157
|
+
)
|
|
158
|
+
)
|
|
130
159
|
|
|
131
160
|
self.rows = rows
|
|
132
161
|
|
|
133
|
-
|
|
134
|
-
|
|
162
|
+
def add_text_to_cell(
|
|
163
|
+
self,
|
|
164
|
+
row_index: int,
|
|
165
|
+
col_index: int,
|
|
166
|
+
text: str,
|
|
167
|
+
style: str | None = None,
|
|
168
|
+
alignment: AlignmentEnum | None = None,
|
|
169
|
+
) -> None:
|
|
135
170
|
# Validate row_index and col_index
|
|
136
171
|
if row_index < 0 or row_index >= len(self.rows):
|
|
137
172
|
raise IndexError(f"Row index {row_index} out of range")
|
|
@@ -139,21 +174,22 @@ class WordTableModel(BaseModel):
|
|
|
139
174
|
row = rows[row_index]
|
|
140
175
|
if col_index < 0 or col_index >= len(row.cells):
|
|
141
176
|
raise IndexError(f"Column index {col_index} out of range")
|
|
142
|
-
|
|
177
|
+
|
|
143
178
|
cell = row.cells[col_index]
|
|
144
179
|
if isinstance(cell, str) and cell == "merge":
|
|
145
180
|
raise ValueError("Cannot add text to a merged cell")
|
|
146
|
-
|
|
147
|
-
cell.paragraphs.append(WordParagraphModel(style=style, alignment=alignment, text=[text]))
|
|
148
181
|
|
|
149
|
-
|
|
182
|
+
cell.paragraphs.append(
|
|
183
|
+
WordParagraphModel(style=style, alignment=alignment, text=[text])
|
|
184
|
+
)
|
|
150
185
|
|
|
186
|
+
self.rows = rows
|
|
151
187
|
|
|
152
188
|
def style_row(self, row_index: int, text_style: str) -> None:
|
|
153
189
|
# Validate row_index
|
|
154
190
|
if row_index < 0 or row_index >= len(self.rows):
|
|
155
191
|
raise IndexError(f"Row index {row_index} out of range")
|
|
156
|
-
|
|
192
|
+
|
|
157
193
|
rows = self.rows.copy()
|
|
158
194
|
row = rows[row_index]
|
|
159
195
|
|
|
@@ -165,7 +201,6 @@ class WordTableModel(BaseModel):
|
|
|
165
201
|
|
|
166
202
|
self.rows = rows
|
|
167
203
|
|
|
168
|
-
|
|
169
204
|
def style_cell(self, row_index: int, col_index: int, text_style: str) -> None:
|
|
170
205
|
# Validate row_index and col_index
|
|
171
206
|
if row_index < 0 or row_index >= len(self.rows):
|
|
@@ -174,22 +209,21 @@ class WordTableModel(BaseModel):
|
|
|
174
209
|
row = rows[row_index]
|
|
175
210
|
if col_index < 0 or col_index >= len(row.cells):
|
|
176
211
|
raise IndexError(f"Column index {col_index} out of range")
|
|
177
|
-
|
|
212
|
+
|
|
178
213
|
cell = row.cells[col_index]
|
|
179
214
|
if isinstance(cell, str) and cell == "merge":
|
|
180
215
|
raise ValueError("Cannot style a merged cell")
|
|
181
|
-
|
|
216
|
+
|
|
182
217
|
for paragraph in cell.paragraphs:
|
|
183
218
|
paragraph.style = text_style
|
|
184
219
|
|
|
185
220
|
self.rows = rows
|
|
186
221
|
|
|
187
|
-
|
|
188
222
|
def color_row(self, row_index: int, background_color: str) -> None:
|
|
189
223
|
# Validate row_index
|
|
190
224
|
if row_index < 0 or row_index >= len(self.rows):
|
|
191
225
|
raise IndexError(f"Row index {row_index} out of range")
|
|
192
|
-
|
|
226
|
+
|
|
193
227
|
rows = self.rows.copy()
|
|
194
228
|
row = rows[row_index]
|
|
195
229
|
|
|
@@ -200,7 +234,6 @@ class WordTableModel(BaseModel):
|
|
|
200
234
|
|
|
201
235
|
self.rows = rows
|
|
202
236
|
|
|
203
|
-
|
|
204
237
|
def color_cell(self, row_index: int, col_index: int, background_color: str) -> None:
|
|
205
238
|
# Validate row_index and col_index
|
|
206
239
|
if row_index < 0 or row_index >= len(self.rows):
|
|
@@ -209,21 +242,20 @@ class WordTableModel(BaseModel):
|
|
|
209
242
|
row = rows[row_index]
|
|
210
243
|
if col_index < 0 or col_index >= len(row.cells):
|
|
211
244
|
raise IndexError(f"Column index {col_index} out of range")
|
|
212
|
-
|
|
245
|
+
|
|
213
246
|
cell = row.cells[col_index]
|
|
214
247
|
if isinstance(cell, str) and cell == "merge":
|
|
215
248
|
raise ValueError("Cannot color a merged cell")
|
|
216
|
-
|
|
249
|
+
|
|
217
250
|
cell.background_color = background_color
|
|
218
251
|
|
|
219
252
|
self.rows = rows
|
|
220
253
|
|
|
221
|
-
|
|
222
254
|
def align_row(self, row_index: int, alignment: AlignmentEnum) -> None:
|
|
223
255
|
# Validate row_index
|
|
224
256
|
if row_index < 0 or row_index >= len(self.rows):
|
|
225
257
|
raise IndexError(f"Row index {row_index} out of range")
|
|
226
|
-
|
|
258
|
+
|
|
227
259
|
rows = self.rows.copy()
|
|
228
260
|
row = rows[row_index]
|
|
229
261
|
|
|
@@ -235,8 +267,9 @@ class WordTableModel(BaseModel):
|
|
|
235
267
|
|
|
236
268
|
self.rows = rows
|
|
237
269
|
|
|
238
|
-
|
|
239
|
-
|
|
270
|
+
def align_cell(
|
|
271
|
+
self, row_index: int, col_index: int, alignment: AlignmentEnum
|
|
272
|
+
) -> None:
|
|
240
273
|
# Validate row_index and col_index
|
|
241
274
|
if row_index < 0 or row_index >= len(self.rows):
|
|
242
275
|
raise IndexError(f"Row index {row_index} out of range")
|
|
@@ -244,22 +277,19 @@ class WordTableModel(BaseModel):
|
|
|
244
277
|
row = rows[row_index]
|
|
245
278
|
if col_index < 0 or col_index >= len(row.cells):
|
|
246
279
|
raise IndexError(f"Column index {col_index} out of range")
|
|
247
|
-
|
|
280
|
+
|
|
248
281
|
cell = row.cells[col_index]
|
|
249
282
|
if isinstance(cell, str) and cell == "merge":
|
|
250
283
|
raise ValueError("Cannot align a merged cell")
|
|
251
|
-
|
|
284
|
+
|
|
252
285
|
for paragraph in cell.paragraphs:
|
|
253
286
|
paragraph.alignment = alignment
|
|
254
287
|
|
|
255
288
|
self.rows = rows
|
|
256
289
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
col_index: int,
|
|
261
|
-
table: WordTableModel
|
|
262
|
-
) -> None:
|
|
290
|
+
def add_table_to_cell(
|
|
291
|
+
self, row_index: int, col_index: int, table: WordTableModel
|
|
292
|
+
) -> None:
|
|
263
293
|
# Validate row_index and col_index
|
|
264
294
|
if row_index < 0 or row_index >= len(self.rows):
|
|
265
295
|
raise IndexError(f"Row index {row_index} out of range")
|
|
@@ -271,12 +301,11 @@ class WordTableModel(BaseModel):
|
|
|
271
301
|
|
|
272
302
|
if isinstance(cell, str) and cell == "merge":
|
|
273
303
|
raise ValueError("Cannot add table to a merged cell")
|
|
274
|
-
|
|
304
|
+
|
|
275
305
|
cell.table = table
|
|
276
306
|
|
|
277
307
|
self.rows = rows
|
|
278
308
|
|
|
279
|
-
|
|
280
309
|
def delete_row(self, row_index: int) -> None:
|
|
281
310
|
if row_index < 0 or row_index >= len(self.rows):
|
|
282
311
|
raise IndexError(f"Row index {row_index} out of range")
|
|
@@ -284,12 +313,9 @@ class WordTableModel(BaseModel):
|
|
|
284
313
|
del rows[row_index]
|
|
285
314
|
self.rows = rows
|
|
286
315
|
|
|
287
|
-
|
|
288
316
|
def pretty_print(self) -> None:
|
|
289
317
|
print(json.dumps(self.model_dump(), indent=4))
|
|
290
318
|
|
|
291
|
-
|
|
292
319
|
def write(self, filepath: str) -> None:
|
|
293
|
-
with open(filepath,
|
|
320
|
+
with open(filepath, "w") as f:
|
|
294
321
|
json.dump(self.model_dump(), f, indent=4)
|
|
295
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: wordhelpers
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Helpers for working with python-docx
|
|
5
5
|
Author: AJ Cruz
|
|
6
6
|
Author-email: 15045766-a-cruz@users.noreply.gitlab.com
|
|
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.12
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
13
|
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
13
14
|
Requires-Dist: python-docx (>=1.2.0,<2.0.0)
|
|
14
15
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
wordhelpers/__init__.py,sha256=9gR-TRwfToOc3MtkV7gbubbbZs79H6LBJQ-pgdpKV_w,11648
|
|
2
|
+
wordhelpers/pydantic_models.py,sha256=tg2PXpbVWlym3XmF0KGM1LZDxO-oItzhkTTXEQ11x2g,11211
|
|
3
|
+
wordhelpers-0.1.4.dist-info/METADATA,sha256=si1K6RFlC7G4KypZkyVo0KqrWY8NnendWw3cvXIJGl4,11529
|
|
4
|
+
wordhelpers-0.1.4.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
5
|
+
wordhelpers-0.1.4.dist-info/RECORD,,
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
wordhelpers/__init__.py,sha256=pQuRcSdSU13yUv772gUb4mWmQZxGxynPHpZwTMOQzjA,11466
|
|
2
|
-
wordhelpers/pydantic_models.py,sha256=PWKg1tROYLRi-C2ebog3GuWFLzLi3-tFkp8B4o2DipU,10906
|
|
3
|
-
wordhelpers-0.1.3.dist-info/METADATA,sha256=VWXL0Q16WoLwbYSDBKytB5ZdUYI6dKD3R6O9JoTwX2s,11478
|
|
4
|
-
wordhelpers-0.1.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
5
|
-
wordhelpers-0.1.3.dist-info/RECORD,,
|