petal-qc 0.0.9__py3-none-any.whl → 0.0.11__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.

Potentially problematic release.


This version of petal-qc might be problematic. Click here for more details.

Files changed (36) hide show
  1. petal_qc/BTreport/CheckBTtests.py +2 -6
  2. petal_qc/__init__.py +17 -1
  3. petal_qc/metrology/PetalMetrology.py +0 -5
  4. petal_qc/metrology/compare_Cores.py +27 -15
  5. petal_qc/metrology/coreMetrology.py +20 -10
  6. petal_qc/metrology/do_Metrology.py +0 -5
  7. petal_qc/metrology/petal_flatness.py +0 -4
  8. petal_qc/metrology/readAVSdata.py +762 -0
  9. petal_qc/metrology/uploadPetalInformation.py +769 -0
  10. petal_qc/test/checkAVStests.py +181 -0
  11. petal_qc/test/compare_golden.py +44 -0
  12. petal_qc/test/getAVSjson.py +27 -0
  13. petal_qc/test/getAVStests.py +263 -0
  14. petal_qc/test/getPetalCoreTestSummary.py +89 -0
  15. petal_qc/test/listPetalCoreComponents.py +89 -0
  16. petal_qc/test/prepareDESYfiles.py +25 -8
  17. petal_qc/thermal/DESYdata.py +58 -0
  18. petal_qc/thermal/IRBFile.py +51 -7
  19. petal_qc/thermal/IRCore.py +1 -1
  20. petal_qc/thermal/IRDataGetter.py +43 -24
  21. petal_qc/thermal/IRPetal.py +84 -7
  22. petal_qc/thermal/IRPetalParam.py +1 -1
  23. petal_qc/thermal/Petal_IR_Analysis.py +4 -3
  24. petal_qc/thermal/PipeFit.py +12 -3
  25. petal_qc/thermal/analyze_IRCore.py +24 -15
  26. petal_qc/thermal/coreThermal.py +124 -28
  27. petal_qc/thermal/create_IRCore.py +35 -9
  28. petal_qc/thermal/create_core_report.py +31 -8
  29. petal_qc/utils/Geometry.py +2 -2
  30. petal_qc/utils/readGraphana.py +2 -1
  31. {petal_qc-0.0.9.dist-info → petal_qc-0.0.11.dist-info}/METADATA +2 -2
  32. petal_qc-0.0.11.dist-info/RECORD +70 -0
  33. {petal_qc-0.0.9.dist-info → petal_qc-0.0.11.dist-info}/WHEEL +1 -1
  34. {petal_qc-0.0.9.dist-info → petal_qc-0.0.11.dist-info}/entry_points.txt +3 -0
  35. petal_qc-0.0.9.dist-info/RECORD +0 -61
  36. {petal_qc-0.0.9.dist-info → petal_qc-0.0.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,762 @@
1
+ #!/usr/bin/env pythoon3
2
+ """Read AVS dats file."""
3
+ import sys
4
+ import re
5
+ import numpy as np
6
+ from argparse import ArgumentParser
7
+ from pathlib import Path
8
+
9
+ import dateutil.parser
10
+ import openpyxl as XL
11
+ from openpyxl.cell.cell import MergedCell
12
+ from openpyxl.utils.exceptions import InvalidFileException
13
+
14
+ try:
15
+ import itkdb_gtk
16
+
17
+ except ImportError:
18
+ cwd = Path(__file__).parent.parent
19
+ sys.path.append(cwd.as_posix())
20
+
21
+ from itkdb_gtk import ITkDBlogin, ITkDButils
22
+
23
+ sk_defaults = {
24
+ "institution": "AVS",
25
+ "runNumber": "1",
26
+ }
27
+
28
+
29
+ class AVSDataException(Exception):
30
+ """AVSData exception class."""
31
+
32
+ def __init__(self, message):
33
+ """Call the base class constructor with the parameters it needs."""
34
+ super().__init__(message)
35
+
36
+
37
+ def create_weight(session, SN, the_date=None, manager="", passed=True, problems=False, comments=None):
38
+ """Creates the dictionary for a WEIGHT test.
39
+
40
+ Args:
41
+ session: the DB session
42
+ SN: Serial Number
43
+ the_date: the date of the test
44
+ manager: manager name
45
+ passed: if test passed or not
46
+ problems: if problems were found during test
47
+ comments: list of comments to append to the test
48
+
49
+ """
50
+ if comments is None:
51
+ comments = []
52
+
53
+ the_date = ITkDButils.get_db_date(the_date)
54
+ out = ITkDButils.get_test_skeleton(session, "CORE_PETAL", "WEIGHING", sk_defaults)
55
+ out['component'] = SN
56
+ out['date'] = the_date
57
+ out["institution"] = "AVS"
58
+ out["runNumber"] = "1"
59
+ out['passed'] = passed
60
+ out['problems'] = problems
61
+ out['properties']['PRODUCTION_MANAGER'] = manager
62
+ out['comments'] = comments
63
+ return out
64
+
65
+
66
+ def create_manufacturing(session, SN, the_date=None, manager="", passed=True, problems=False, comments=None):
67
+ """Create the dictionary or the MANUFACTURING test.
68
+
69
+ Args:
70
+ session: the DB session
71
+ SN: Serial Number
72
+ the_date: the date of the test
73
+ manager: manager name
74
+ passed: if test passed or not
75
+ problems: if problems were found during test
76
+ comments: list of comments to append to the test
77
+
78
+ """
79
+ if comments is None:
80
+ comments = []
81
+ the_date = ITkDButils.get_db_date(the_date)
82
+ out = ITkDButils.get_test_skeleton(session, "CORE_PETAL", "MANUFACTURING", sk_defaults)
83
+ out['component'] = SN
84
+ out['date'] = the_date
85
+ out["institution"] = "AVS"
86
+ out["runNumber"] = "1"
87
+ out['passed'] = passed
88
+ out['problems'] = problems
89
+ out['properties']['PRODUCTION_MANAGER'] = manager
90
+ out['comments'] = comments
91
+
92
+ return out
93
+
94
+
95
+ def create_visual_inpection(session, SN, the_date=None, operator="", passed=True, problems=False, comments=None):
96
+ """Create Visual Inspection test skeleton."""
97
+ if comments is None:
98
+ comments = []
99
+ the_date = ITkDButils.get_db_date(the_date)
100
+ out = ITkDButils.get_test_skeleton(session, "CORE_PETAL", "VISUAL_INSPECTION", sk_defaults)
101
+ out['component'] = SN
102
+ out["institution"] = "AVS"
103
+ out["runNumber"] = "1"
104
+ out['date'] = the_date
105
+ out['passed'] = passed
106
+ out['problems'] = problems
107
+ out['properties']['OPERATOR'] = operator
108
+ out['comments'] = comments
109
+
110
+ return out
111
+
112
+
113
+ def create_delamination_test(session, SN, the_date=None, operator="", passed=True, problems=False, comments=None):
114
+ """Create the delamination test JSON.
115
+
116
+ Args:
117
+ session: the DB session
118
+ SN: Serial Number
119
+ the_date: the date of the test
120
+ operator: operator name
121
+ passed: if test passed or not
122
+ problems: if problems were found during test
123
+ comments: list of comments to append to the test
124
+
125
+ """
126
+ if comments is None:
127
+ comments = []
128
+
129
+ the_date = ITkDButils.get_db_date(the_date)
130
+ out = ITkDButils.get_test_skeleton(session, "CORE_PETAL", "DELAMINATION", sk_defaults, {"boolean": False})
131
+ out['component'] = SN
132
+ out['date'] = the_date
133
+ out["institution"] = "AVS"
134
+ out["runNumber"] = "1"
135
+ out['passed'] = passed
136
+ out['problems'] = problems
137
+ out['properties']['OPERATOR'] = operator
138
+ out['comments'] = comments
139
+
140
+ return out
141
+
142
+
143
+ def create_grounding_test(session, SN, the_date=None, operator="", passed=True, problems=False, comments=None):
144
+ """Create grounding test.
145
+
146
+ Args:
147
+ session: the DB session
148
+ SN: Serial Number
149
+ the_date: the date of the test
150
+ operator: operator name
151
+ passed: if test passed or not
152
+ problems: if problems were found during test
153
+ comments: list of comments to append to the test
154
+
155
+ """
156
+ if comments is None:
157
+ comments = []
158
+ the_date = ITkDButils.get_db_date(the_date)
159
+ out = ITkDButils.get_test_skeleton(session, "CORE_PETAL", "GROUNDING_CHECK", sk_defaults, {"boolean": False})
160
+ out['component'] = SN
161
+ out['date'] = the_date
162
+ out["institution"] = "AVS"
163
+ out["runNumber"] = "1"
164
+ out['passed'] = passed
165
+ out['problems'] = problems
166
+ out['properties']['OPERATOR'] = operator
167
+ out['comments'] = comments
168
+
169
+ return out
170
+
171
+
172
+ def create_metrology_test(session, SN, the_date=None, operator="", passed=True, problems=False, comments=None):
173
+ """Metrology test.
174
+
175
+ Args:
176
+ session: the DB session
177
+ SN: Serial Number
178
+ the_date: the date of the test
179
+ operator: operator name
180
+ passed: if test passed or not
181
+ problems: if problems were found during test
182
+ comments: list of comments to append to the test
183
+
184
+ """
185
+ if comments is None:
186
+ comments = []
187
+
188
+ the_date = ITkDButils.get_db_date(the_date)
189
+ out = ITkDButils.get_test_skeleton(session, "CORE_PETAL", "METROLOGY_AVS",
190
+ sk_defaults, {"integer": -1, "float": -1.0})
191
+ out['component'] = SN
192
+ out['date'] = the_date
193
+ out['passed'] = passed
194
+ out["institution"] = "AVS"
195
+ out["runNumber"] = "1"
196
+ out['problems'] = problems
197
+ out['properties']['OPERATOR'] = operator
198
+ out['comments'] = comments
199
+
200
+ return out
201
+
202
+
203
+ def split_comp_list(lst):
204
+ """Split a list of components separated by various possible characters."""
205
+ if lst is None:
206
+ return []
207
+
208
+ if isinstance(lst, float):
209
+ return [lst]
210
+
211
+ if isinstance(lst, int):
212
+ return [lst]
213
+
214
+ out = [lst]
215
+ for sep in ['/', '\\', '\n']:
216
+ if lst.find(sep) >= 0:
217
+ out = [x.strip() for x in lst.split(sep)]
218
+ break
219
+
220
+ return out
221
+
222
+
223
+ def get_comments(txt):
224
+ """Return test DB comment."""
225
+ return split_comp_list(txt)
226
+
227
+
228
+ def get_float(cell, separator=None, default=0.0):
229
+ """Return float from string."""
230
+ txt = cell.value
231
+ if txt is None:
232
+ return default
233
+
234
+ if separator is None:
235
+ if isinstance(txt, float):
236
+ return txt
237
+
238
+ if isinstance(txt, int):
239
+ return float(txt)
240
+
241
+ if isinstance(txt, str):
242
+ try:
243
+ txt = txt.replace(',', '.')
244
+ rr = re.findall(r"[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", txt)
245
+ if len(rr) == 0:
246
+ return default
247
+
248
+ val = float(rr[0])
249
+ return val
250
+ except ValueError:
251
+ return default
252
+
253
+ return default
254
+
255
+ else:
256
+ values = []
257
+ for val in split_comp_list(txt):
258
+ if isinstance(val, float) or isinstance(val, int):
259
+ values.append(val)
260
+ else:
261
+ try:
262
+ v = float(val.strip().replace(',', '.'))
263
+ except ValueError:
264
+ print("get_float: Cannot convert {} in {}".format(val, cell.coordinate))
265
+ v = default
266
+
267
+ values.append(v)
268
+
269
+ return values
270
+
271
+ def get_int(cell, default=None):
272
+ """Get an int from a cell."""
273
+ value = cell.value
274
+ if value is None:
275
+ return default
276
+
277
+ return int(value)
278
+
279
+ def get_boolean(cell):
280
+ """Get a boolean from a cell."""
281
+ value = cell.value
282
+ if value is None:
283
+ return False
284
+
285
+ else:
286
+ txt = value.strip().lower()
287
+ return txt == "pass"
288
+
289
+
290
+ def get_text(cell):
291
+ """Get a string from a cell."""
292
+ value = cell.value
293
+ if value:
294
+ value = value.strip()
295
+
296
+ return value
297
+
298
+
299
+ def get_res_and_accep(sheet, indx):
300
+ """Return result and acceptancee."""
301
+ sval = sheet["g{}".format(indx)].value
302
+ if isinstance(sval, float) or isinstance(sval, int):
303
+ val = sval
304
+
305
+ else:
306
+ sval = ' '.join(sval.strip().split()).split()
307
+
308
+ scale = 1.0
309
+ try:
310
+ rr = re.findall(r"[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", sval[0])
311
+ if len(rr) == 0:
312
+ val = 0.0
313
+ else:
314
+ val = float(rr[0])
315
+ if len(sval)>1:
316
+ U = sval[1].upper()[0]
317
+ if U=='G':
318
+ scale = 1000
319
+
320
+ except ValueError:
321
+ val = 0
322
+
323
+ val = val * scale
324
+ #val = get_float(sheet["g{}".format(indx)])
325
+ pass_val = sheet["h{}".format(indx)]
326
+ if pass_val.value is None:
327
+ # Operator did not set the PASS/FAIL thing
328
+ accept = True
329
+ else:
330
+ accept = get_boolean(pass_val)
331
+
332
+ return val, accept
333
+
334
+
335
+ def find_idx(lst, val):
336
+ """Return index of occurence of val in lst."""
337
+ R = re.compile(val, re.DOTALL|re.IGNORECASE)
338
+ idx = [i for i, x in enumerate(lst) if (x and R.search(x))]
339
+ return idx
340
+
341
+
342
+ def cell_value(sheet, coord):
343
+ """Return the cell value."""
344
+ cell = sheet[coord]
345
+ if not isinstance(cell, MergedCell):
346
+ return cell.value
347
+
348
+ # "Oh no, the cell is merged!"
349
+ for cell_range in sheet.merged_cells.ranges:
350
+ if coord in cell_range:
351
+ return cell_range.start_cell.value
352
+
353
+ return None
354
+
355
+ def distance(P1, P2):
356
+ """Distance between 2 points."""
357
+ P = (P2-P1)
358
+ S = np.sum(np.square(P))
359
+ D = np.sqrt(S)
360
+ return D
361
+
362
+ def find_test_indx(tests, val):
363
+ """Apply brute force."""
364
+ try:
365
+ indx = tests.index(val)
366
+
367
+ except ValueError:
368
+ for ix, key in enumerate(tests):
369
+ if key is None:
370
+ continue
371
+
372
+ if val in key:
373
+ indx = ix
374
+ break
375
+
376
+ return indx
377
+
378
+ def fix_commas(P):
379
+ """Fix commas in cells."""
380
+ for i in range(2):
381
+ if P[i] > 1000:
382
+ P[i] /= 1000
383
+
384
+
385
+ def get_locator_distance(sheet, results, tests, nom, result):
386
+ """Compute distance to nominal."""
387
+ Pnom = np.array([
388
+ get_float(sheet['E{}'.format(find_test_indx(tests, "{}_X".format(nom)))]),
389
+ get_float(sheet['E{}'.format(find_test_indx(tests, "{}_Y".format(nom)))])
390
+ ])
391
+ fix_commas(Pnom)
392
+
393
+ P = np.array([results["{}_X".format(result)], results["{}_Y".format(result)]])
394
+ fix_commas(P)
395
+
396
+ D = distance(Pnom, P)
397
+ return D
398
+
399
+
400
+ def check_locator_positions(sheet, the_test, tests):
401
+ """Check position of locators."""
402
+
403
+ defects = []
404
+ locators = [("PL01", "LOCATOR1"), ("PL02", "LOCATOR2"), ("PL03", "LOCATOR3"),
405
+ ("FD01", "FIDUCIAL1"), ("FD02", "FIDUCIAL2")]
406
+
407
+ for nom, val in locators:
408
+ D = get_locator_distance(sheet, the_test["results"], tests, nom, val)
409
+ if D > 0.075:
410
+ defects.append({
411
+ "name": nom,
412
+ "description": "{:.3f} mm out".format(D)
413
+ })
414
+
415
+ if len(defects)>0:
416
+ the_test["passed"] = False
417
+ the_test["defects"].extend(defects)
418
+
419
+
420
+ def check_for_problems(sheet, the_test, row_range):
421
+ """Finds FAIL massages in the Acceptance column."""
422
+ nfail = 0
423
+ for row in range(row_range[0], row_range[1]):
424
+ txt = get_text(sheet["h{}".format(row)])
425
+ if txt is None:
426
+ continue
427
+
428
+ txt = txt.lower()
429
+ if txt[0] == 'f':
430
+ nfail += 1
431
+ hdr = get_text(sheet["d{}".format(row)])
432
+ result = get_float(sheet["g{}".format(row)])
433
+ reason = "{} [{}]".format(result, cell_value(sheet, "i{}".format(row)))
434
+
435
+ if reason:
436
+ if len(reason) < 1:
437
+ msg = "{}: {}".format(hdr, reason)
438
+ the_test["defects"].append({"name": msg})
439
+ else:
440
+ the_test["defects"].append({"name": hdr,
441
+ "description": reason})
442
+
443
+ #if nfail:
444
+ # the_test["passed"] = False
445
+
446
+
447
+ def get_coreID(bustapeID):
448
+ """Build core SN from bus tape SN."""
449
+ SN = "20USEBC" + bustapeID[-7:]
450
+ return SN
451
+
452
+
453
+ def readFATfile(session, file_path, SN=None):
454
+ """Read data from FAT excel file.
455
+
456
+ Args:
457
+ session: the DB session
458
+ file_path: File path
459
+ SN: COre serial number
460
+
461
+ """
462
+ # Open spreadsheet
463
+ try:
464
+ wb = XL.load_workbook(file_path, data_only=True)
465
+ except InvalidFileException as ee:
466
+ print("Could not open input file: ", file_path)
467
+ print(ee)
468
+ return None
469
+
470
+ # Assume active sheet is the good one, otherwise will have to find in wb.sheetnames
471
+ sheet = wb.active
472
+ if sheet.max_row < 50 or sheet.max_column < 9:
473
+ raise AVSDataException("Wrong FAT file")
474
+
475
+ # Check the SN of the petal core
476
+ if SN is None or len(SN) == 0:
477
+ coreID = split_comp_list(sheet['C6'].value)
478
+ if len(coreID) == 0:
479
+ raise AVSDataException("Cannot figure out core SN in FAT file.")
480
+
481
+ for cID in coreID:
482
+ cmp = ITkDButils.get_DB_component(session, cID)
483
+ if cmp["type"]["code"] == "BT_PETAL_FRONT":
484
+ SN = get_coreID(cID)
485
+ break
486
+
487
+ batch = sheet['E6'].value
488
+ operator = sheet['G6'].value
489
+
490
+ txt = list(map(str.strip, sheet['i4'].value.split(':')))[1]
491
+ test_date = dateutil.parser.parse(txt)
492
+
493
+ # Get the test index
494
+ test_name = [str(sheet[x][1].value) for x in range(1, sheet.max_row)]
495
+ tests = [str(sheet[x][3].value) for x in range(1, sheet.max_row)]
496
+
497
+ # This is to avoid adding 1 for cell names...
498
+ tests.insert(0, None)
499
+ test_name.insert(0, None)
500
+
501
+ #
502
+ # Visual inspection
503
+ vi_text = get_text(sheet['i9'])
504
+ vi_result = get_text(sheet['g9'])
505
+ vi_pass = sheet['h9'].value.strip().lower() == "pass"
506
+ vi_defects = []
507
+ if vi_pass:
508
+ if vi_result and len(vi_result):
509
+ if vi_text and len(vi_text):
510
+ vi_text = vi_result + '\n' + vi_text
511
+ else:
512
+ vi_text = vi_result
513
+
514
+ else:
515
+ vi_defects.append({"name": "PETAL_VI_DEFECT", "description": vi_result})
516
+
517
+ vi_test = create_visual_inpection(session, SN, test_date, operator, vi_pass,
518
+ comments=get_comments(vi_text))
519
+ for df in vi_defects:
520
+ vi_test["defects"].append(df)
521
+
522
+ #
523
+ # Delamination test
524
+ dl_text = get_text(sheet['i10'])
525
+ dl_result = get_text(sheet['g10'])
526
+ dl_pass = sheet['h10'].value.strip().lower() == "pass"
527
+ dl_defects = []
528
+ if dl_pass:
529
+ if dl_result and len(dl_result):
530
+ if dl_text and len(dl_text):
531
+ dl_text = dl_result + '\n' + dl_text
532
+ else:
533
+ dl_text = dl_result
534
+
535
+ else:
536
+ dl_defects.append({"name": "PETAL_DL_DEFECT",
537
+ "description": dl_result})
538
+
539
+ delamination_test = create_delamination_test(session, SN, test_date, operator, dl_pass,
540
+ comments=get_comments(dl_text))
541
+ for df in dl_defects:
542
+ delamination_test["defects"].append(df)
543
+
544
+ #
545
+ # Conductivity
546
+ # TODO: read proper rows
547
+ grounding_test = create_grounding_test(session, SN, test_date, operator)
548
+ cond_val, cond_pass = get_res_and_accep(sheet, tests.index("COND"))
549
+ if "INS_LOOP" in tests:
550
+ loop_val, loop_pass = get_res_and_accep(sheet, tests.index("INS_LOOP"))
551
+ else:
552
+ loop_val, loop_pass = get_res_and_accep(sheet, tests.index("INS"))
553
+
554
+ if "INS_LOOP_GND" in tests:
555
+ loop_gnd_val, loop_gnd_pass = get_res_and_accep(sheet, tests.index("INS_LOOP_GND"))
556
+ else:
557
+ loop_gnd_val, loop_gnd_pass = get_res_and_accep(sheet, tests.index("INS_FACE"))
558
+
559
+ passed = cond_pass and loop_pass and loop_gnd_pass
560
+ grounding_test["passed"] = passed
561
+ grounding_test["results"]["RESISTANCE_FB"] = cond_val
562
+ grounding_test["results"]["RESISTANCE_PIPES"] = loop_val
563
+ grounding_test["results"]["RESISTANCE_PIPE_GND"] = loop_gnd_val
564
+ #check_for_problems(sheet, grounding_test, [tests.index('COND'), tests.index("WEIGH")])
565
+
566
+ #
567
+ # Weight
568
+ petal_weight, weight_pass = get_res_and_accep(sheet, tests.index("WEIGH"))
569
+
570
+ #
571
+ # Metrology AVS
572
+ metrology_test = create_metrology_test(session, SN, test_date, operator)
573
+ metrology_test["results"]["LOCATOR1_DIAMETER"] = get_float(sheet['g{}'.format(tests.index("PL01_DIAM"))])
574
+ metrology_test["results"]["LOCATOR2_DIAMETER"] = get_float(sheet['g{}'.format(tests.index("PL02_DIAM"))])
575
+ metrology_test["results"]["LOCATOR3_DIAMETER"] = get_float(sheet['g{}'.format(tests.index("PL03_DIAM"))])
576
+ metrology_test["results"]["LOCATOR1_X"] = get_float(sheet['g{}'.format(find_idx(tests, "PL01_X")[0])])
577
+ metrology_test["results"]["LOCATOR1_Y"] = get_float(sheet['g{}'.format(find_idx(tests, "PL01_Y")[0])])
578
+ metrology_test["results"]["LOCATOR2_X"] = get_float(sheet['g{}'.format(find_idx(tests, "PL02_X")[0])])
579
+ metrology_test["results"]["LOCATOR2_Y"] = get_float(sheet['g{}'.format(find_idx(tests, "PL02_Y")[0])])
580
+ metrology_test["results"]["LOCATOR3_X"] = get_float(sheet['g{}'.format(find_idx(tests, "PL03_X")[0])])
581
+ metrology_test["results"]["LOCATOR3_Y"] = get_float(sheet['g{}'.format(find_idx(tests, "PL03_Y")[0])])
582
+ metrology_test["results"]["FIDUCIAL1_DIAMETER"] = get_float(sheet["g{}".format(find_idx(tests, "FD01_DIAM")[0])])
583
+ metrology_test["results"]["FIDUCIAL1_X"] = get_float(sheet["g{}".format(find_idx(tests, "FD01_X")[0])])
584
+ metrology_test["results"]["FIDUCIAL1_Y"] = get_float(sheet["g{}".format(find_idx(tests, "FD01_Y")[0])])
585
+ metrology_test["results"]["FIDUCIAL2_DIAMETER"] = get_float(sheet["g{}".format(find_idx(tests, "FD02_DIAM")[0])])
586
+ metrology_test["results"]["FIDUCIAL2_X"] = get_float(sheet["g{}".format(find_idx(tests, "FD02_X")[0])])
587
+ metrology_test["results"]["FIDUCIAL2_Y"] = get_float(sheet["g{}".format(find_idx(tests, "FD02_Y")[0])])
588
+ metrology_test["results"]["ANGLE_VCHANNEL"] = get_float(sheet["g{}".format(find_idx(tests, "VANGL")[0])])
589
+ metrology_test["results"]["ENVELOPE"] = get_float(sheet["g{}".format(find_idx(tests, "ENVEL")[0])])
590
+ metrology_test["results"]["COPLANARITY_FRONT"] = get_float(sheet["g{}".format(find_idx(tests, "F.PL_PLAN")[0])])
591
+ metrology_test["results"]["LOCAL_FLATNESS_FRONT"] = get_float(sheet["g{}".format(find_idx(tests, "F.FS_PLAN")[0])], '/')
592
+ metrology_test["results"]["PARALLELISM_FRONT"] = get_float(sheet["g{}".format(find_idx(tests, "F.PARAL")[0])])
593
+ metrology_test["results"]["COPLANARITY_BACK"] = get_float(sheet["g{}".format(find_idx(tests, "B.PL_PLAN")[0])])
594
+ metrology_test["results"]["LOCAL_FLATNESS_BACK"] = get_float(sheet["g{}".format(find_idx(tests, "B.FS_PLAN")[0])], '/')
595
+ metrology_test["results"]["PARALLELISM_BACK"] = get_float(sheet["g{}".format(find_idx(tests, "B.PARAL")[0])])
596
+
597
+ # Get defects
598
+ check_locator_positions(sheet, metrology_test, tests)
599
+ check_for_problems(sheet, metrology_test, [tests.index("VANGL"), sheet.max_row])
600
+
601
+ return vi_test, delamination_test, grounding_test, metrology_test, batch, petal_weight
602
+
603
+
604
+ def find_label(sheet, label, column, max_row=20):
605
+ """Find label in given column
606
+
607
+ Args:
608
+ sheet (): The spread sheet
609
+ label (): The label to search for
610
+ column (): The column to scan.
611
+
612
+ Return:
613
+ indx (int) - the index. <0 means not found.
614
+ """
615
+ indx = -1
616
+ for i in range(1, 15):
617
+ val = sheet["{}{}".format(column, i)].value
618
+ if val is None:
619
+ continue
620
+
621
+ if val.find(label)>=0:
622
+ indx = i
623
+ break
624
+
625
+ return indx
626
+
627
+ def readProductionSheet(session, file_path, SN):
628
+ """Read data fro AVS PS.
629
+
630
+ Args:
631
+ session: the DB session
632
+ file_path: path of input file
633
+ SN: The serial number
634
+ write_json: if true, test json is writen to file.
635
+
636
+ """
637
+ try:
638
+ wb = XL.load_workbook(file_path, data_only=True)
639
+ except InvalidFileException as ee:
640
+ print("Could not open input file: ", file_path)
641
+ print(ee.message)
642
+ return None
643
+
644
+ # Assume active sheet is the good one, otherwise will have tofind in wb.sheetnames
645
+ sheet = wb.active
646
+ if sheet.max_row > 30 or sheet.max_column > 9:
647
+ raise AVSDataException("Wrong PS file:\nmx row {} mx_col {}".format(sheet.max_row, sheet.max_column))
648
+
649
+ # Find the start
650
+ indx = find_label(sheet, "PETAL", "A")
651
+ if indx < 0:
652
+ # Try with second column
653
+ indx = find_label(sheet, "PETAL", "B")
654
+ if indx < 0:
655
+ print("Wrong Production Sheet.")
656
+ return None
657
+ else:
658
+ icol = ord('B')
659
+ ccol = 'B'
660
+ else:
661
+ icol = ord('A')
662
+ ccol = 'A'
663
+
664
+ i_items = find_label(sheet, "DRAWING", ccol)
665
+ if i_items < 0:
666
+ print("Wrong Production Sheet.")
667
+ return None
668
+
669
+ i_items += 1
670
+
671
+ # Get the SN of the fron facesheet and create the Petal SN
672
+ ID = sheet["{}{}".format(chr(icol+2), i_items)].value.strip()
673
+ SN = get_coreID(ID)
674
+ nn = get_int(sheet["{}{}".format(chr(icol+1), indx)])
675
+ petal_id = "PPC.{:03d}".format(nn)
676
+ mould_id = sheet["{}{}".format(chr(icol+1), indx+3)].value
677
+
678
+ # find the date (use the end date)
679
+ start_date = sheet["{}{}".format(chr(icol+4), indx+2)].value
680
+ end_date = sheet["{}{}".format(chr(icol+4), indx+3)].value
681
+ test_date = start_date
682
+
683
+ manager = sheet["{}{}".format(chr(icol+4), indx+1)].value
684
+
685
+ # Manufacturing
686
+ id_col = chr(icol+2)
687
+ w_col = chr(icol+3)
688
+ n_col = chr(icol+4)
689
+ comments = get_comments(sheet['a25'].value)
690
+ manufacturing = create_manufacturing(session, SN, test_date, manager, comments=comments)
691
+ manufacturing['properties']['START_DATE'] = ITkDButils.get_db_date(start_date)
692
+ manufacturing['properties']['FINISH_DATE'] = ITkDButils.get_db_date(end_date)
693
+ manufacturing["properties"]["MOULD_ID"] = mould_id
694
+ manufacturing["properties"]["PROCESS_DOCUMENT"] = sheet["{}{}".format(chr(icol+4), indx)].value
695
+ manufacturing["results"]["LOCATOR_A"] = sheet["{}{}".format(id_col, i_items+2)].value
696
+ manufacturing["results"]["LOCATOR_B"] = sheet["{}{}".format(id_col, i_items+3)].value
697
+ manufacturing["results"]["LOCATOR_C"] = sheet["{}{}".format(id_col, i_items+4)].value
698
+ manufacturing["results"]["HONEYCOMBSET"] = split_comp_list( sheet["{}{}".format(id_col, i_items+5)].value)
699
+ manufacturing["results"]["EPOXY_ADHESIVE"] = split_comp_list(sheet["{}{}".format(id_col, i_items+8)].value)
700
+ manufacturing["results"]["EPOXY_PUTTY"] = split_comp_list( sheet["{}{}".format(id_col, i_items+9)].value)
701
+ manufacturing["results"]["EPOXY_CONDUCTIVE"] = split_comp_list( sheet["{}{}".format(id_col, i_items+10)].value)
702
+
703
+ # Weighing
704
+ weighing = create_weight(session, SN, test_date, manager)
705
+ scol = "{}{}:{}{}".format(w_col, i_items, w_col, i_items+10)
706
+ comp_weight = [get_float(x[0]) for x in sheet[scol]]
707
+ petal_weight = sum([float(x) for x in comp_weight])
708
+ weighing["results"]["WEIGHT_FACING_FRONT"] = comp_weight[0]
709
+ weighing["results"]["WEIGHT_FACING_BACK"] = comp_weight[1]
710
+ weighing["results"]["WEIGHT_LOCATOR_A"] = comp_weight[2]
711
+ weighing["results"]["WEIGHT_LOCATOR_B"] = comp_weight[3]
712
+ weighing["results"]["WEIGHT_LOCATOR_C"] = comp_weight[4]
713
+ weighing["results"]["WEIGHT_COOLINGLOOPASSEMBLY"] = comp_weight[6]
714
+ weighing["results"]["WEIGHT_HONEYCOMBSET"] = comp_weight[5]
715
+ weighing["results"]["WEIGHT_EPOXYADHESIVE"] = comp_weight[8]
716
+ weighing["results"]["WEIGHT_EPOXYPUTTY"] = comp_weight[9]
717
+ weighing["results"]["WEIGHT_EPOXYCONDUCTIVE"] = comp_weight[10]
718
+ weighing["results"]["WEIGHT_CORE"] = petal_weight
719
+
720
+ # Comments
721
+ for i in range(i_items, i_items+11):
722
+ cell_id = sheet['{}{}'.format(ccol, i)].value
723
+ comment = sheet['{}{}'.format(n_col, i)].value
724
+ if comment is not None:
725
+ comment = comment.strip()
726
+ if len(comment):
727
+ msg = "{}: {}".format(cell_id, comment)
728
+ weighing["comments"].append(msg)
729
+
730
+ DESY = {
731
+ "FacingFront": sheet["{}{}".format(id_col, i_items)].value.strip(),
732
+ "FacingBack": sheet["{}{}".format(id_col, i_items+1)].value.strip(),
733
+ "CoolingLoop": sheet["{}{}".format(id_col, i_items+6)].value.strip(),
734
+ "AllcompSet": sheet["{}{}".format(id_col, i_items+7)].value.strip(),
735
+ "HoneyCombSet": manufacturing["results"]["HONEYCOMBSET"]
736
+ }
737
+
738
+ return manufacturing, weighing, DESY, petal_id
739
+
740
+
741
+ if __name__ == "__main__":
742
+ parser = ArgumentParser()
743
+ parser.add_argument('files', nargs='*', help="Input files")
744
+ parser.add_argument("--SN", dest="SN", type=str, default="SNnnn",
745
+ help="Module serial number")
746
+
747
+ options = parser.parse_args()
748
+ if len(options.files) == 0:
749
+ print("I need an input file")
750
+ sys.exit()
751
+
752
+ dlg = ITkDBlogin.ITkDBlogin()
753
+ client = dlg.get_client()
754
+ if client is None:
755
+ print("Could not connect to DB with provided credentials.")
756
+ dlg.die()
757
+ sys.exit()
758
+
759
+ fnam = Path(options.files[0]).expanduser().resolve()
760
+ # readProductionSheet(client, fnam, options.SN)
761
+ readFATfile(client, fnam, options.SN)
762
+ dlg.die()