-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathservice_bulletin.py
289 lines (217 loc) · 8.93 KB
/
service_bulletin.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
282
283
284
285
286
287
288
289
from docx import Document
from docx.shared import Inches
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
from docx.enum.table import WD_ALIGN_VERTICAL
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.enum.text import WD_BREAK
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.section import WD_SECTION
from docx.shared import Pt
class Service_Bulletin():
def __init__(self, mainapp, change):
self.change = change
self.mainapp = mainapp
self.file_path = r'C:\Users\domhn\Documents\Python\Pycabin_Tkinter\V0.08\SB_Test.docx'
self.determine_ac_groups()
self.gen_parts_table()
self.gen_document_references()
self.get_oem_docs()
def determine_ac_groups(self):
#group in format {'MSN', 'A/C Type'}
self.groups = {}
ac = []
for mod in self.change.mods:
for a in mod[-1].split(','):
if a not in ac:
ac.append(a)
self.groups = {1: ac}
def gen_parts_table(self):
self.parts_table = []
for mod in self.change.mods:
mod = self.mainapp.frames[mod[1]].backend
pt = mod.gen_parts_table()
for p in pt:
self.parts_table.append(p)
def gen_document_references(self):
self.drawings = self.change.get_change_drawings()
for mod in self.change.mods:
if mod[0] == 'LOPA':
#get seat CMM
pass
def get_oem_docs(self):
print('here')
self.oem_docs = self.change.get_change_oem_docs()
def gen_steps_required(self):
pass
class Service_Bulletin_Writer():
def __init__(self, sb):
self.sb = sb
self.setup_styles()
self.create_doc()
self.write_parts_section()
self.write_references_section()
self.document.save(self.sb.file_path)
def create_doc(self):
self.document = Document(r'C:\Users\domhn\Documents\Python\Pycabin_Tkinter\Resources\archive\SB_Template.docx')
def setup_styles(self):
self.hdr_border = {"sz": 6, "val": "single", "color": "#000000", "space": "0"}
def write_parts_section(self):
self.document.add_heading('Provisioning', level=1)
self.write_table(['Qty', 'Part Number', 'Description'], self.sb.parts_table)
p = self.document.add_paragraph()
run = p.add_run()
run.add_break(WD_BREAK.PAGE)
def write_references_section(self):
self.document.add_heading('References', level=1)
self.document.add_heading('Documents Not Supplied', level=2)
# p = self.document.add_paragraph()
# run = p.add_run()
p = self.document.add_paragraph('Aircraft Maintenace Manual (AMM)', style='Bullet Point Level 2')
p = self.document.add_paragraph('Aircraft Illustrated Part Catalogue (IPC)', style='Bullet Point Level 2')
self.document.add_heading('Documents Supplied', level=2)
self.write_table(['Document No.', 'Issue', 'Title'], self.sb.drawings)
self.document.add_heading('OEM Documents', level=2)
self.write_table(['Title', 'Date', 'Ref', 'Version'], self.sb.oem_docs)
def write_table(self, headers, data):
table = self.document.add_table(rows=1, cols=len(headers)+1)
#Add Headers
hdr_cells = table.rows[0].cells
for index, h in enumerate(headers):
print(h)
hdr_cells[index].text = h
self.set_cell_border(hdr_cells[index], top=self.hdr_border, bottom=self.hdr_border, start=self.hdr_border, end=self.hdr_border)
shading_elm_1 = parse_xml(r'<w:shd {} w:fill="b1b5b2"/>'.format(nsdecls('w')))
hdr_cells[index]._tc.get_or_add_tcPr().append(shading_elm_1)
for row in data:
new_row = table.add_row()
row_cells = new_row.cells
for index, c in enumerate(row):
run = row_cells[index].paragraphs[0].add_run(str(c))
self.set_cell_border(row_cells[index], bottom = self.hdr_border, start=self.hdr_border, end=self.hdr_border)
def set_cell_border(self, cell, **kwargs):
"""
Set cell`s border
Usage:
set_cell_border(
cell,
top={"sz": 12, "val": "single", "color": "#FF0000", "space": "0"},
bottom={"sz": 12, "color": "#00FF00", "val": "single"},
start={"sz": 24, "val": "dashed", "shadow": "true"},
end={"sz": 12, "val": "dashed"},
)
"""
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
# check for tag existnace, if none found, then create one
tcBorders = tcPr.first_child_found_in("w:tcBorders")
if tcBorders is None:
tcBorders = OxmlElement('w:tcBorders')
tcPr.append(tcBorders)
# list over all available tags
for edge in ('start', 'top', 'end', 'bottom', 'insideH', 'insideV'):
edge_data = kwargs.get(edge)
if edge_data:
tag = 'w:{}'.format(edge)
# check for tag existnace, if none found, then create one
element = tcBorders.find(qn(tag))
if element is None:
element = OxmlElement(tag)
tcBorders.append(element)
# looks like order of attributes is important
for key in ["sz", "val", "color", "space", "shadow"]:
if key in edge_data:
element.set(qn('w:{}'.format(key)), str(edge_data[key]))
def list_number(doc, par, prev=None, level=None, num=True):
"""
Makes a paragraph into a list item with a specific level and
optional restart.
An attempt will be made to retreive an abstract numbering style that
corresponds to the style of the paragraph. If that is not possible,
the default numbering or bullet style will be used based on the
``num`` parameter.
Parameters
----------
doc : docx.document.Document
The document to add the list into.
par : docx.paragraph.Paragraph
The paragraph to turn into a list item.
prev : docx.paragraph.Paragraph or None
The previous paragraph in the list. If specified, the numbering
and styles will be taken as a continuation of this paragraph.
If omitted, a new numbering scheme will be started.
level : int or None
The level of the paragraph within the outline. If ``prev`` is
set, defaults to the same level as in ``prev``. Otherwise,
defaults to zero.
num : bool
If ``prev`` is :py:obj:`None` and the style of the paragraph
does not correspond to an existing numbering style, this will
determine wether or not the list will be numbered or bulleted.
The result is not guaranteed, but is fairly safe for most Word
templates.
"""
xpath_options = {
True: {'single': 'count(w:lvl)=1 and ', 'level': 0},
False: {'single': '', 'level': level},
}
def style_xpath(prefer_single=True):
"""
The style comes from the outer-scope variable ``par.style.name``.
"""
style = par.style.style_id
return (
'w:abstractNum['
'{single}w:lvl[@w:ilvl="{level}"]/w:pStyle[@w:val="{style}"]'
']/@w:abstractNumId'
).format(style=style, **xpath_options[prefer_single])
def type_xpath(prefer_single=True):
"""
The type is from the outer-scope variable ``num``.
"""
type = 'decimal' if num else 'bullet'
return (
'w:abstractNum['
'{single}w:lvl[@w:ilvl="{level}"]/w:numFmt[@w:val="{type}"]'
']/@w:abstractNumId'
).format(type=type, **xpath_options[prefer_single])
def get_abstract_id():
"""
Select as follows:
1. Match single-level by style (get min ID)
2. Match exact style and level (get min ID)
3. Match single-level decimal/bullet types (get min ID)
4. Match decimal/bullet in requested level (get min ID)
3. 0
"""
for fn in (style_xpath, type_xpath):
for prefer_single in (True, False):
xpath = fn(prefer_single)
ids = numbering.xpath(xpath)
if ids:
return min(int(x) for x in ids)
return 0
if (prev is None or
prev._p.pPr is None or
prev._p.pPr.numPr is None or
prev._p.pPr.numPr.numId is None):
if level is None:
level = 0
numbering = doc.part.numbering_part.numbering_definitions._numbering
# Compute the abstract ID first by style, then by num
anum = get_abstract_id()
# Set the concrete numbering based on the abstract numbering ID
num = numbering.add_num(anum)
# Make sure to override the abstract continuation property
num.add_lvlOverride(ilvl=level).add_startOverride(1)
# Extract the newly-allocated concrete numbering ID
num = num.numId
else:
if level is None:
level = prev._p.pPr.numPr.ilvl.val
# Get the previous concrete numbering ID
num = prev._p.pPr.numPr.numId.val
par._p.get_or_add_pPr().get_or_add_numPr().get_or_add_numId().val = num
par._p.get_or_add_pPr().get_or_add_numPr().get_or_add_ilvl().val = level