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

@@ -32,27 +32,27 @@ def save_figure(fig, fnam, prefix=None, dpi=192):
32
32
  name = prefix + P.name
33
33
  out = P.parent / name
34
34
  fnam = out
35
-
35
+
36
36
  print("out: {}".format(fnam))
37
37
  fig.savefig(fnam, dpi=dpi)
38
-
38
+
39
39
 
40
40
  def read_data_files(options):
41
41
  """Read the data files.
42
-
42
+
43
43
  It assumes the data file names are in the format:
44
-
44
+
45
45
  <AlternativeID>-<side>.json
46
-
46
+
47
47
  AlternativeID is PPC.nnn side is either front or back.
48
-
48
+
49
49
  Returns the values for front and back petal tests.
50
-
50
+
51
51
  The results are a dictionary with the file label as key and the requested
52
- value, which can be a number of an array.
53
-
52
+ value, which can be a number of an array.
53
+
54
54
  the file labels are also returned.
55
-
55
+
56
56
  """
57
57
  labels = []
58
58
  front = {}
@@ -60,7 +60,6 @@ def read_data_files(options):
60
60
 
61
61
  for fnam in options.files:
62
62
  ifile = Path(fnam).expanduser().resolve()
63
- print(ifile.name)
64
63
  if not ifile.exists():
65
64
  print("File does not exist: ", fnam)
66
65
  continue
@@ -83,9 +82,9 @@ def read_data_files(options):
83
82
  front[label] = val
84
83
  else:
85
84
  back[label] = val
86
-
85
+
87
86
  labels.sort()
88
-
87
+
89
88
  return front, back, labels
90
89
 
91
90
  def draw_deltas(data, keys, fnam=None, title="Front"):
@@ -97,12 +96,12 @@ def draw_deltas(data, keys, fnam=None, title="Front"):
97
96
  P = [np.zeros([nfiles, 2]),
98
97
  np.zeros([nfiles, 2]),
99
98
  np.zeros([nfiles, 2])]
100
-
101
99
  fig_width = 12.0
102
100
  fig_height = 1.2*fig_width/3.0
103
101
  fig, ax = plt.subplots(nrows=1, ncols=3, tight_layout=True, figsize=(fig_width, fig_height))
104
102
  fig.suptitle(title)
105
103
  for i in range(3):
104
+ LBL = [[],[],[]]
106
105
  ax[i].set_title(keys[i])
107
106
  ax[i].set_aspect('equal', adjustable='box')
108
107
  ax[i].set_xlim(-100, 100)
@@ -121,15 +120,18 @@ def draw_deltas(data, keys, fnam=None, title="Front"):
121
120
  for k in range(3):
122
121
  ky = key_table[keys[k]]
123
122
  P[k][j, :] = 1000*np.array(values[ky])
123
+ LBL[k].append(label.split('.')[1].lstrip('0'))
124
124
 
125
125
  ax[i].scatter(P[i][:,0], P[i][:,1])
126
+ for j in range(len(LBL[i])):
127
+ ax[i].text(P[i][j,0], P[i][j,1], LBL[i][j]) #, ha='center', va='top')
126
128
 
127
129
  save_figure(fig, fnam, prefix=title)
128
130
 
129
131
 
130
132
  def show_positions(options):
131
133
  """Make position plots."""
132
-
134
+
133
135
  if "LOCATION" in options.value:
134
136
  keys = ["Bot.", "Slot", "Top"]
135
137
  elif "REL_POS" in options.value:
@@ -154,58 +156,58 @@ def show_flatness(options):
154
156
  nfiles = len(labels)
155
157
  npts = len(tick_labels)
156
158
  X = np.array([float(x) for x in range(npts)])
157
-
158
-
159
+
160
+
159
161
  fig, ax = plt.subplots(nrows=1, ncols=2, tight_layout=True, figsize=(9., 5.))
160
162
  fig.suptitle(options.value)
161
-
163
+
162
164
  P = [ np.zeros([nfiles, npts]), np.zeros([nfiles, npts]) ]
163
165
  y_lim = []
164
166
  for i, V in enumerate([front, back]):
165
167
  ax[i].set_title(cindx[i])
166
168
  ax[i].set_xticks(range(npts), labels=tick_labels)
167
169
  ax[i].grid()
168
-
170
+
169
171
  for lbl in labels:
170
172
  ax[i].plot(X, V[lbl], '-', label=lbl)
171
-
173
+
172
174
  y_lim.append(ax[i].get_ylim())
173
-
175
+
174
176
  for a in ax:
175
177
  a.set_ylim(0, 1.2*max(y_lim[0][1], y_lim[1][1]))
178
+ x_lim = a.get_xlim()
179
+ a.fill_between(x_lim, 0, 0.050, facecolor="darkseagreen", alpha=0.1)
176
180
  a.legend(ncol=3, fontsize="x-small")
177
-
178
- save_figure(fig, options.out, prefix="zzz-")
181
+
182
+ save_figure(fig, options.out, prefix=opts.prefix)
179
183
 
180
184
  def main(options):
181
185
  """Main entry."""
182
-
186
+
183
187
  if "LOCATION" in options.value or "REL_POS" in options.value:
184
188
  show_positions(options)
185
- return
186
189
 
187
- if "FLATNESS_LOCAL" in options.value:
190
+ elif "FLATNESS_LOCAL" in options.value:
188
191
  show_flatness(options)
189
- return
190
192
 
193
+ else:
194
+ front, back, labels = read_data_files(options)
191
195
 
192
- front, back, labels = read_data_files(options)
196
+ labels.sort()
197
+ X = np.arange(0, len(labels))
198
+ fig, ax = plt.subplots(1, 1, tight_layout=True)
199
+ fig.suptitle(options.value)
200
+ ax.set_xticks(range(len(labels)), labels=labels, rotation="vertical")
201
+ ax.grid()
193
202
 
194
- labels.sort()
195
- X = np.arange(0, len(labels))
196
- fig, ax = plt.subplots(1, 1, tight_layout=True)
197
- fig.suptitle(options.value)
198
- ax.set_xticks(range(len(labels)), labels=labels)
199
- ax.grid()
203
+ vfront = [front[x] for x in labels]
204
+ vback = [back[x] for x in labels]
205
+ ax.plot(X, vfront, '*', label="Front")
206
+ ax.plot(X, vback, 'o', label="Back")
207
+ ax.legend()
200
208
 
201
- vfront = [front[x] for x in labels]
202
- vback = [back[x] for x in labels]
203
- ax.plot(X, vfront, '*', label="Front")
204
- ax.plot(X, vback, 'o', label="Back")
205
- ax.legend()
209
+ save_figure(fig, options.out, prefix=options.prefix)
206
210
 
207
- save_figure(fig, options.out)
208
-
209
211
  plt.show()
210
212
 
211
213
 
@@ -214,6 +216,7 @@ if __name__ == "__main__":
214
216
  parser = argparse.ArgumentParser()
215
217
  parser.add_argument('files', nargs='*', help="Input files")
216
218
  parser.add_argument("--value", default=None, help="Value to plot")
219
+ parser.add_argument("--prefix", default=None, help="prefix for out file")
217
220
  parser.add_argument("--out", default=None, help="File to store the figure.")
218
221
 
219
222
  opts = parser.parse_args()
@@ -221,7 +224,7 @@ if __name__ == "__main__":
221
224
  print("I need at least one input file")
222
225
  sys.exit()
223
226
 
224
- if len(opts.files) == 1:
227
+ elif len(opts.files) == 1:
225
228
  xxx = any(elem in opts.files[0] for elem in r"*?")
226
229
  if xxx:
227
230
  opts.files = glob.glob(opts.files[0])
@@ -10,15 +10,15 @@ import matplotlib.pyplot as plt
10
10
 
11
11
 
12
12
  from petal_qc.metrology import DataFile
13
- from .analyze_locking_points import analyze_locking_point_data
13
+ #from .analyze_locking_points import analyze_locking_point_data
14
14
 
15
15
  def get_data_chunck(flist, data_type, ofile):
16
16
  """Get one data type chunk from file
17
17
 
18
18
  Args:
19
- fname (): input file
20
- data_tyle (): data type
21
- ofile (): file object for output.
19
+ fname: input file
20
+ data_tyle: data type
21
+ ofile: file object for output.
22
22
 
23
23
  """
24
24
  rgx = "Step Name:\\s+({}).*?=".format(data_type)
@@ -41,7 +41,7 @@ def get_data_chunck(flist, data_type, ofile):
41
41
  ofile.write(txt.group(0)+'\n')
42
42
 
43
43
 
44
- def read_smartscope(fnam, ofile, data_type, keep=False,):
44
+ def read_smartscope(fnam, ofile, data_type, keep=False):
45
45
  """Convert a SmartScope txt file into CVS
46
46
 
47
47
  Args:
@@ -58,7 +58,11 @@ def read_smartscope(fnam, ofile, data_type, keep=False,):
58
58
  if isinstance(ofile, io.IOBase):
59
59
  fout = ofile
60
60
  else:
61
- fout = open(ofile, 'w')
61
+ try:
62
+ fout = open(ofile, 'w', encoding="utf-8")
63
+ except TypeError:
64
+ fout = ofile
65
+
62
66
  is_file = True
63
67
 
64
68
  if keep:
@@ -106,7 +110,84 @@ def read_smartscope(fnam, ofile, data_type, keep=False,):
106
110
  values = [ float(x) for x in items[2:5]]
107
111
  fout.write("{:6f}, {:6f}, {:6f}\n".format(values[1], values[0], values[2]))
108
112
 
109
- fout.close()
113
+ if is_file:
114
+ fout.close()
115
+
116
+ def get_smarscope_locator_positions(fnam, ofile, data_type, keep=False):
117
+ """REad DESY 2D file.
118
+
119
+ Args:
120
+ fnam: Input file
121
+ ofile: Output file
122
+ data_type: Label to search
123
+ keep (bool, optional): If tre keep label. Defaults to False.
124
+ """
125
+ ifile = Path(fnam).expanduser().resolve()
126
+
127
+ is_file = False
128
+ if isinstance(ofile, io.IOBase):
129
+ fout = ofile
130
+ else:
131
+ try:
132
+ fout = open(ofile, 'w', encoding="utf-8")
133
+ except TypeError:
134
+ fout = ofile
135
+
136
+ is_file = True
137
+
138
+ if keep:
139
+ fout.write("X,Y,Z, label\n")
140
+ else:
141
+ fout.write("X,Y,Z\n")
142
+
143
+ rgx = "^Step Name:\\s+(?P<label>{})".format(data_type)
144
+ start = re.compile(rgx, re.DOTALL)
145
+ finder = re.compile("^Finder Name")
146
+
147
+ out = {}
148
+ with open(ifile, 'r', encoding='ISO-8859-1') as fin:
149
+ while True:
150
+ line = fin.readline()
151
+ if not line:
152
+ break
153
+
154
+ r = start.match(line)
155
+ if not r:
156
+ continue
157
+
158
+ label = r.group('label').strip()
159
+ eof = False
160
+ out[label] = [0. for i in range(3)]
161
+ for i in range(3):
162
+ line = fin.readline()
163
+
164
+ while True:
165
+ line = fin.readline().strip()
166
+ if len(line)==0:
167
+ break
168
+
169
+ values = line.split()
170
+ val = float(values[3])
171
+ if values[0] in ["Diame", "Width"]:
172
+ out[label][2] = val
173
+ elif values[0] == "X":
174
+ out[label][1] = val
175
+ elif values[0] == "Y":
176
+ out[label][0] = val
177
+
178
+ lbls = ["BottomPL", "SlotPL", "OversizedPL", "Bottom_Fiducial", "Slot_Fiducial"]
179
+ for L in lbls:
180
+ try:
181
+ V = [float(x) for x in out[L]]
182
+ fout.write("{:.6f}, {:.6f}, {:.6f}".format(V[0], V[1], V[2]))
183
+ if keep:
184
+ fout.write(", {}".format(L))
185
+ fout.write('\n')
186
+ except KeyError:
187
+ continue
188
+
189
+ if is_file:
190
+ fout.close()
110
191
 
111
192
  def do_locking_points(args):
112
193
  ifile = args.smartscope_files[0]
@@ -120,14 +201,14 @@ if __name__ == "__main__":
120
201
  import argparse
121
202
 
122
203
  parser = argparse.ArgumentParser()
123
- parser.add_argument("smartscope_files", nargs='*',
204
+ parser.add_argument("files", nargs='*',
124
205
  help="The SmartScope files to parse")
125
206
  parser.add_argument("--label", default="\\w+", help="The label to select")
126
207
  parser.add_argument("--out", help="Output CSV file", default="out.csv")
127
208
  parser.add_argument("--keep_label", dest="keep", default=False, action="store_true", help="Store label in output")
128
209
 
129
210
  args = parser.parse_args()
130
- if len(args.smartscope_files) == 0:
211
+ if len(args.files) == 0:
131
212
  print("I need an input file")
132
213
  sys.exit()
133
214
 
@@ -135,11 +216,13 @@ if __name__ == "__main__":
135
216
 
136
217
  # do_locking_points(args)
137
218
 
138
- fout = open(Path(args.out).expanduser().resolve(), 'w')
139
- get_data_chunck(args.smartscope_files, args.label, fout)
140
- fout.close()
219
+ #fout = open(Path(args.out).expanduser().resolve(), 'w')
220
+ #get_data_chunck(args.smartscope_files, args.label, fout)
221
+ #fout.close()
141
222
 
142
223
 
143
224
  #read_smartscope(args.smartscope_files[0], args.out, args.label, args.keep)
144
225
 
226
+ get_smarscope_locator_positions(args.files[0], args.out, args.label)
227
+
145
228
  plt.show()
@@ -16,7 +16,7 @@ import gi
16
16
  gi.require_version("Gtk", "3.0")
17
17
  from gi.repository import Gtk, GObject, Gio, GLib
18
18
 
19
-
19
+ __HELP__ = "https://petal-qc.docs.cern.ch"
20
20
 
21
21
  class CommaSeparatedListAction(Action):
22
22
  """Create a list from the comma sepparated numbers at imput."""
@@ -41,7 +41,7 @@ class CoreMetrology(itkdb_gtk.dbGtkUtils.ITkDBWindow):
41
41
  pannel_size: size of message panel.
42
42
 
43
43
  """
44
- super().__init__(session=session, title=title)
44
+ super().__init__(session=session, title=title, help=__HELP__)
45
45
 
46
46
  self.data_file = None
47
47
  self.folder = None
@@ -42,13 +42,13 @@ def do_analysis(fnam, prefix, SN, options):
42
42
  """Perform analysis of a file.
43
43
 
44
44
  Args:
45
- fnam (): Input data file
46
- prefix (): Prefix telling if it is front or back
47
- SN (): Core serial number
48
- options (): Options.
49
-
45
+ fnam: Input data file
46
+ prefix: Prefix telling if it is front or back
47
+ SN: Core serial number
48
+ options: Options.
49
+
50
50
  """
51
-
51
+
52
52
  is_front = prefix.lower().find("front") >= 0
53
53
  print(fnam, prefix)
54
54
  options.out = prefix + '.docx'
@@ -63,10 +63,10 @@ def do_analysis(fnam, prefix, SN, options):
63
63
  ofile = output_folder(options.folder, prefix + '.json')
64
64
  with open(ofile, 'w', encoding='UTF-8') as of:
65
65
  json.dump(outDB, of, indent=3)
66
-
66
+
67
67
  return outDB
68
68
 
69
- def main(ifile, options):
69
+ def analyze_files(ifile, options):
70
70
  """Main entry."""
71
71
  with open(ifile, 'r', encoding='UTF-8') as inp:
72
72
 
@@ -86,25 +86,25 @@ def main(ifile, options):
86
86
 
87
87
  do_analysis(fnam, prefix, SN, options)
88
88
 
89
-
90
- if __name__ == "__main__":
89
+ def main():
90
+ "Main entry."
91
91
  parser = ArgumentParser()
92
92
  parser.add_argument('files', nargs='*', help="Input files")
93
- parser.add_argument("--prefix", dest='prefix', default=None)
94
- parser.add_argument("--SN", dest='SN', default=None)
93
+ parser.add_argument("--prefix", dest='prefix', default=None, help="prefix telling if it is front or back.")
94
+ parser.add_argument("--SN", dest='SN', default=None, help="The petal core Serial Number")
95
95
  parser.add_argument("--save", dest='save', action="store_true", default=False)
96
- parser.add_argument("--desy", dest='desy', action="store_true", default=False)
96
+ parser.add_argument("--desy", dest='desy', action="store_true", default=False, help="True if data is from DESY's SmartScope")
97
97
  parser.add_argument("--out", dest="out", default="petal_flatness.docx",
98
- type=str, help="The output fiel name")
98
+ type=str, help="The output file name")
99
99
  parser.add_argument("--bottom-lp", dest='bLP', action=CommaSeparatedListAction, default=None,
100
100
  help="Bottom locking point fiducial coordinates")
101
101
  parser.add_argument("--upper-lp", dest='uLP', action=CommaSeparatedListAction, default=None,
102
102
  help="upper locking point fiducials coordinates")
103
103
 
104
104
  parser.add_argument("--title", dest="title", default=None,
105
- type=str, help="Document title")
105
+ type=str, help="Report Document title")
106
106
  parser.add_argument("--nbins", dest="nbins", default=25,
107
- type=int, help="Number of bins")
107
+ type=int, help="Number of bins in the histograms")
108
108
  parser.add_argument("--folder", default=None, help="Folder to store output files. Superseeds folder in --out")
109
109
  parser.add_argument("--locking_points", action="store_true", default=False)
110
110
 
@@ -119,7 +119,10 @@ if __name__ == "__main__":
119
119
  sys.exit()
120
120
 
121
121
  try:
122
- main(options.files[0], options)
122
+ analyze_files(options.files[0], options)
123
123
 
124
124
  except Exception:
125
125
  print(traceback.format_exc())
126
+
127
+ if __name__ == "__main__":
128
+ main()
@@ -218,7 +218,7 @@ def petal_flatness(orig_data, options, document=None):
218
218
  # add allsensor area
219
219
  cell = table.add_row().cells
220
220
  cell[0].text = "All sensor area"
221
- cell[1].text = "{:.4f}".format(flatness_all_sensor_area)
221
+ cell[1].text = "{:.4f}".format(flatness_all_sensor_area*1000)
222
222
 
223
223
  return TM, avg, Zmean, outF
224
224
 
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env python3
2
+ """Prepara input files from DESY RAW."""
3
+ import os
4
+ import fnmatch
5
+ import re
6
+ from pathlib import Path
7
+
8
+ def all_files(root, patterns='*', single_level=False, yield_folders=False):
9
+ """A generator that reruns all files in the given folder.
10
+
11
+ Args:
12
+ ----
13
+ root (file path): The folder
14
+ patterns (str, optional): The pattern of the files. Defaults to '*'.
15
+ single_level (bool, optional): If true, do not go into sub folders. Defaults to False.
16
+ yield_folders (bool, optional): If True, return folders as well. Defaults to False.
17
+
18
+ Yields
19
+ ------
20
+ str: file path name
21
+
22
+ """
23
+ patterns = patterns.split(';')
24
+ for path, subdirs, files in os.walk(root):
25
+ if yield_folders:
26
+ files.extend(subdirs)
27
+
28
+ files.sort()
29
+ for name in files:
30
+ for pattern in patterns:
31
+ if fnmatch.fnmatch(name, pattern):
32
+ yield os.path.join(path, name)
33
+ break
34
+
35
+ if single_level:
36
+ break
37
+
38
+ class PetalCore:
39
+ def __init__(self, front, back):
40
+ self.front = {}
41
+ self.back = {}
42
+
43
+ def main(folder, out_folder):
44
+ """Main entry point."""
45
+ outF = Path(out_folder).expanduser().resolve()
46
+ if not outF.exists():
47
+ os.mkdir(outF)
48
+
49
+ rgx = re.compile(r"Project Name: (\w+)side_.*AlternativeID=PPC-(\d+)", re.MULTILINE|re.DOTALL)
50
+ petal_cores = {}
51
+ for fnam in all_files(folder, "*.txt"):
52
+ P = Path(fnam).expanduser().resolve()
53
+ with open(fnam, "r", encoding="UTF-8") as ff:
54
+ R = rgx.search(ff.read())
55
+ if R:
56
+ petal_id = "PPC.{}".format(R.group(2))
57
+ side = R.group(1).lower()
58
+ if "_2D_" in P.name:
59
+ test_type = "2D"
60
+ else:
61
+ test_type = "3D"
62
+
63
+ if not petal_id in petal_cores:
64
+ petal_cores[petal_id] = {
65
+ "back":{"2D":None, "3D":None},
66
+ "front":{"2D":None, "3D":None},
67
+ }
68
+
69
+ petal_cores[petal_id][side][test_type] = P
70
+
71
+ list_file = open("{}/Desy-cores.txt".format(outF.as_posix()), "w", encoding="UTF-8")
72
+ for petal_id, sides in petal_cores.items():
73
+ for side, values in sides.items():
74
+ oname = "{}/{}-{}.txt".format(outF.as_posix(), petal_id, side)
75
+ list_file.write("{} {}-{} {}\n".format(oname, petal_id, side, petal_id))
76
+ data = ""
77
+ for data_type, fnam in values.items():
78
+ with open(fnam, "r", encoding="UTF-8") as ifile:
79
+ data += ifile.read()
80
+
81
+ with open(oname, "w", encoding="UTF-8") as ofile:
82
+ ofile.write(data)
83
+
84
+ list_file.close()
85
+ if __name__ == "__main__":
86
+ main("/Users/lacasta/Downloads/DESY_Production", "/tmp/desy")
@@ -3,6 +3,7 @@
3
3
  import json
4
4
  import os
5
5
  import sys
6
+ import bisect
6
7
  from pathlib import Path
7
8
 
8
9
  import matplotlib.dates as pldates
@@ -19,6 +20,14 @@ from petal_qc.thermal import Petal_IR_Analysis
19
20
 
20
21
  CO2_Temp_setting = -35
21
22
 
23
+ def get_array_as_string(A):
24
+ """Return a string rerpresentation."""
25
+ sss="["
26
+ for v in A:
27
+ sss += " {:5.3f}".format(v)
28
+
29
+ sss+=" ]"
30
+ return sss
22
31
 
23
32
  def get_acceptance_band():
24
33
  """returns the width of the acceptance band."""
@@ -362,6 +371,9 @@ def compare_golden(core, golden, value):
362
371
  golden (AnalysisResult): The golden avrage
363
372
  value (str): The name of the variable to compare
364
373
 
374
+ Returns:
375
+ (pass, max difference, array of location of outsiders)
376
+
365
377
  """
366
378
  Y = getattr(core, value)
367
379
  gY = getattr(golden, value)
@@ -373,18 +385,23 @@ def compare_golden(core, golden, value):
373
385
  X = np.array([float(x) for x in range(10)])
374
386
  gX = X
375
387
 
376
- # convert to golden X
377
- spln = CubicSpline(X, Y)
378
- gC = np.array([spln(x) for x in gX])
379
-
380
388
  # compute difference
381
389
  band = get_acceptance_band()
382
- diff = np.abs(gC-gY)
383
- outsiders = np.where(diff > band)
384
- mxval = np.amax(diff)
390
+ outsiders = []
391
+ mxval = -1
392
+ indx = 1
393
+ for x, y in zip(X, Y):
394
+ indx = bisect.bisect_left(gX, x, lo=(indx-1 if indx>1 else 0))
395
+ val = gY[indx]
396
+ diff = abs(y-val)
397
+ if diff > mxval:
398
+ mxval = diff
399
+
400
+ if diff>band:
401
+ outsiders.append(x)
385
402
 
386
403
  rc = mxval < band
387
- return rc, mxval, len(outsiders)
404
+ return rc, mxval, outsiders
388
405
 
389
406
 
390
407
  def analyze_petal_cores(files, golden, options):
@@ -456,15 +473,16 @@ def analyze_petal_cores(files, golden, options):
456
473
  if val[0]:
457
474
  continue
458
475
 
459
- if val[2] < 2:
460
- dbOut["comments"].append(
461
- "{} in side {}: 1 point out of acceptance band.".format(
462
- key, iside)
463
- )
464
- else:
476
+ noutsiders = len(val[2])
477
+ #if noutsiders < 2:
478
+ # dbOut["comments"].append(
479
+ # "{} in side {}: 1 point out of acceptance band. {}".format(
480
+ # key, iside, val[2])
481
+ # )
482
+ if noutsiders>0:
465
483
  dbOut["defects"].append(
466
484
  {"name": key,
467
- "description": "{} in side {}: {} points out of acceptance band".format(key, iside, val[2]),
485
+ "description": "{} in side {}: {} points out of acceptance band. {}".format(key, iside, noutsiders, get_array_as_string(val[2])),
468
486
  "properties": "{} > {}".format(val[1], get_acceptance_band())
469
487
  }
470
488
  )
@@ -11,6 +11,7 @@ import itkdb_gtk.ITkDButils
11
11
  import itkdb_gtk.dbGtkUtils
12
12
  import itkdb_gtk.UploadTest
13
13
 
14
+ __HELP__ = "https://petal-qc.docs.cern.ch"
14
15
 
15
16
  try:
16
17
  import petal_qc
@@ -45,7 +46,7 @@ class CoreThermal(itkdb_gtk.dbGtkUtils.ITkDBWindow):
45
46
  title: Window title.
46
47
  panel_size: size of message pannel.
47
48
  """
48
- super().__init__(session=session, title=title)
49
+ super().__init__(session=session, title=title, help_link=__HELP__ )
49
50
 
50
51
  self.petal_SN = None
51
52
  self.param = params if params else IRPetalParam()