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

@@ -5,89 +5,33 @@ from pathlib import Path
5
5
  from argparse import ArgumentParser
6
6
  import json
7
7
  import numpy as np
8
- from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
9
- from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
8
+
10
9
  import itkdb_gtk
11
10
  import itkdb_gtk.ITkDButils
12
11
  import itkdb_gtk.dbGtkUtils
13
12
  import itkdb_gtk.UploadTest
14
- from petal_qc.thermal.IRPetalParam import IRPetalParam
15
- from petal_qc.thermal.create_IRCore import create_IR_core
16
- from petal_qc.thermal.analyze_IRCore import analyze_IRCore, golden_from_json, show_golden_average
17
- from petal_qc.utils.utils import output_folder
18
-
19
- import gi
20
- gi.require_version("Gtk", "3.0")
21
- from gi.repository import Gtk, GObject, Gio, GLib
22
-
23
-
24
- def create_canvas(fig, sx=400, sy=300):
25
- """Creates a canvas."""
26
- sw = Gtk.ScrolledWindow() # Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
27
- sw.set_border_width(10)
28
- sw.set_size_request(310, 310)
29
- canvas = FigureCanvas(fig) # a Gtk.DrawingArea
30
- canvas.set_size_request(sx, sy)
31
- sw.add(canvas)
32
- return sw
33
-
34
- class ShowThermalResults(Gtk.Window):
35
- """Show thermal results."""
36
- def __init__(self, golden, results):
37
- """Create the window."""
38
- super().__init__(title="Thermal Results")
39
-
40
- # Create main content box
41
- self.mainBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
42
- self.add(self.mainBox)
43
-
44
- # The notebook
45
- self.notebook = Gtk.Notebook()
46
- self.notebook.set_tab_pos(Gtk.PositionType.LEFT)
47
- self.mainBox.pack_start(self.notebook, True, True, 20)
48
13
 
49
- # thermal path
50
- box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
51
- box.set_border_width(5)
52
- label = Gtk.Label(label="Path Temp,")
53
- self.notebook.append_page(box, label)
54
14
 
55
- figs = show_golden_average(golden, results, "path_temp")
56
- sw = create_canvas(figs[0])
57
- box.pack_start(sw, True, True, 0)
15
+ try:
16
+ import petal_qc
58
17
 
59
- sw = create_canvas(figs[q])
60
- box.pack_start(sw, True, True, 0)
18
+ except ImportError:
19
+ cwd = Path(__file__).parent.parent.parent
20
+ sys.path.append(cwd.as_posix())
61
21
 
62
- box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
63
- box.set_border_width(5)
64
- label = Gtk.Label(label="Sensor Avg,")
65
- self.notebook.append_page(box, label)
66
22
 
67
- figs = show_golden_average(golden, results, "sensor_avg")
68
- sw = create_canvas(figs[0])
69
- box.pack_start(sw, True, True, 0)
70
-
71
- sw = create_canvas(figs[q])
72
- box.pack_start(sw, True, True, 0)
73
-
74
-
75
-
76
- # The button box
77
- btnBox = Gtk.ButtonBox(orientation=Gtk.Orientation.HORIZONTAL)
78
-
79
- btn = Gtk.Button(label="Quit")
80
- btn.connect("clicked", self.quit)
81
- btnBox.add(btn)
23
+ from petal_qc.thermal.IRPetalParam import IRPetalParam
24
+ from petal_qc.thermal import IRBFile
25
+ from petal_qc.thermal.IRDataGetter import IRDataGetter
82
26
 
83
- self.mainBox.pack_start(btnBox, False, True, 0)
27
+ from petal_qc.thermal.analyze_IRCore import golden_from_json
28
+ from petal_qc.utils.readGraphana import ReadGraphana
84
29
 
85
- self.show_all()
30
+ import gi
31
+ gi.require_version("Gtk", "3.0")
32
+ from gi.repository import Gtk, GObject, Gio, GLib
86
33
 
87
- def quit(self, *args):
88
- """Close window."""
89
- self.hide()
90
- self.destroy()
34
+ from petal_qc.thermal.create_core_report import create_report
91
35
 
92
36
  class CoreThermal(itkdb_gtk.dbGtkUtils.ITkDBWindow):
93
37
  """Application window."""
@@ -146,7 +90,7 @@ class CoreThermal(itkdb_gtk.dbGtkUtils.ITkDBWindow):
146
90
  self.SN.connect("text-changed", self.on_SN_changed)
147
91
 
148
92
  self.run = Gtk.Button(label="Run")
149
- self.run.connect("clicked", self.run_analysis)
93
+ self.run.connect("clicked", self.create_report)
150
94
 
151
95
  self.btn_state = Gtk.Button(label="Undef")
152
96
  self.btn_state.set_name("btnState")
@@ -222,6 +166,18 @@ class CoreThermal(itkdb_gtk.dbGtkUtils.ITkDBWindow):
222
166
 
223
167
  self.irbfile = PSF
224
168
  self.param.files =[PSF,]
169
+
170
+ DB = ReadGraphana("localhost")
171
+ irbf = IRBFile.open_file(self.irbfile)
172
+ getter = IRDataGetter.factory(self.param.institute, self.param)
173
+ frames = getter.get_analysis_frame(irbf)
174
+ try:
175
+ X, val = DB.get_temperature(frames[0].timestamp, 3)
176
+ inlet = np.min(val)
177
+ self.entryTemp.set_text("{:.1f}".format(inlet))
178
+
179
+ except ValueError:
180
+ pass
225
181
 
226
182
  def on_golden_set(self, *args):
227
183
  """File chosen from FileChooser."""
@@ -338,8 +294,8 @@ class CoreThermal(itkdb_gtk.dbGtkUtils.ITkDBWindow):
338
294
  return
339
295
  uploadW = itkdb_gtk.UploadTest.UploadTest(self.session, payload=self.outDB)
340
296
 
341
- def run_analysis(self, *args):
342
- """Run analysis."""
297
+ def create_report(self, *args):
298
+ """Creates the thermal report."""
343
299
  if self.irbfile is None:
344
300
  self.write_message("Missing IRB file\n")
345
301
  return
@@ -349,16 +305,13 @@ class CoreThermal(itkdb_gtk.dbGtkUtils.ITkDBWindow):
349
305
  self.param.alias = self.alternativeID
350
306
  self.param.SN = self.SN.get_text()
351
307
 
352
- core = create_IR_core(self.param)
353
-
354
- self.param.files[0] = output_folder(self.param.folder, self.param.out)
355
- self.param.out = None
356
- out = analyze_IRCore(self.param)
357
- self.outDB = out[0] if len(out) else None
358
- self.param.files = []
359
- self.write_message("Done\n")
308
+ self.param.tco2 = float(self.entryTemp.get_text())
309
+ self.param.distance = int(self.entryDist.get_text())
310
+ self.param.thrs = float(self.entryTHrs.get_text())
311
+ self.param.debug = False
312
+ self.param.report = True
360
313
 
361
- outW = ShowThermalResults(self.golden, [core, ])
314
+ self.outDB = create_report(self.param)
362
315
  if self.outDB:
363
316
  if len(self.outDB["defects"]) > 0:
364
317
  itkdb_gtk.dbGtkUtils.set_button_color(self.btn_state, "red", "white")
@@ -7,8 +7,16 @@ from datetime import datetime
7
7
  from pathlib import Path
8
8
 
9
9
  import dateutil
10
+ import numpy as np
10
11
  import matplotlib.pyplot as plt
11
12
 
13
+ try:
14
+ import petal_qc
15
+
16
+ except ImportError:
17
+ cwd = Path(__file__).parent.parent.parent
18
+ sys.path.append(cwd.as_posix())
19
+
12
20
  from petal_qc.thermal import IRBFile
13
21
  from petal_qc.thermal import IRCore
14
22
  from petal_qc.thermal import IRPetal
@@ -16,6 +24,7 @@ from petal_qc.thermal import Petal_IR_Analysis
16
24
  from petal_qc.thermal import PipeFit
17
25
  from petal_qc.thermal.IRDataGetter import IRDataGetter
18
26
  from petal_qc.thermal.IRPetalParam import IRPetalParam
27
+ from petal_qc.utils.readGraphana import ReadGraphana
19
28
  from petal_qc.utils.utils import output_folder
20
29
 
21
30
 
@@ -50,8 +59,46 @@ def get_db_date(timestamp=None):
50
59
  return out
51
60
 
52
61
 
62
+ __figures__ = {}
63
+ def get_IRcore_plots():
64
+ """Return global list of figures."""
65
+ return __figures__
66
+
67
+ def clean_figures():
68
+ global __figures__
69
+ for key, fig in __figures__:
70
+ fig.clean()
71
+ plt.close(fig)
72
+
73
+ __figures__ = {}
74
+
75
+ def get_inlet_temp(irbf, param):
76
+ """Gets the value of the inlet temperature from Graphana."""
77
+
78
+ out = -9999
79
+ getter = IRDataGetter.factory(param.institute, param)
80
+ DB = ReadGraphana("localhost")
81
+ frames = getter.get_analysis_frame(irbf)
82
+ the_time = frames[0].timestamp
83
+ try:
84
+ X, val = DB.get_temperature(the_time, 5)
85
+ for x, y in zip(X, val):
86
+ print("{} - {:.1f}".format(x, y))
87
+
88
+ imin = np.argmin(val)
89
+ out = val[imin]
90
+ print("{}: T_co2 = {:.1f}".format(X[imin], out))
91
+
92
+ except ValueError:
93
+ out = -9999
94
+
95
+ return out
96
+
53
97
  def create_IR_core(options):
54
98
  """Entry point."""
99
+ global __figures__
100
+ clean_figures()
101
+
55
102
  # Obtain the Data getter.
56
103
  try:
57
104
  getter = IRDataGetter.factory(options.institute, options)
@@ -60,13 +107,25 @@ def create_IR_core(options):
60
107
  print("*** Invalid institute name. ***")
61
108
  return None
62
109
 
63
- # Set parameters from command line
64
- params = IRPetal.IRPetalParam(options)
65
- params.debug = False
66
-
67
110
  # Open the sequence file
68
111
  print("## ", options.files)
69
112
  irbf = IRBFile.open_file(options.files)
113
+ if irbf is None:
114
+ print("Could not fine input file: {}".format(options.files))
115
+ return
116
+
117
+ if options.tco2 <= -999:
118
+ out = get_inlet_temp(irbf, options)
119
+ if out <= -999:
120
+ print("### Cannot get Tcos. Setting to default.")
121
+ P = IRPetalParam()
122
+ out = P.tco2
123
+
124
+ options.tco2 = out
125
+
126
+ # Set parameters from command line
127
+ params = IRPetal.IRPetalParam(options)
128
+ params.debug = False
70
129
 
71
130
  # FInd first image below the threshold or the corresponding frame
72
131
  # We will use the pipe obtained from here as the reference
@@ -76,8 +135,9 @@ def create_IR_core(options):
76
135
  min_T, i_min, values = getter.find_reference_image(irbf, params.thrs, nframes=10)
77
136
  print("Image size: {} x {}".format(values[0].shape[0], values[0].shape[1]))
78
137
 
79
- if options.debug:
80
- Petal_IR_Analysis.show_2D_image(values)
138
+ if options.debug or options.report:
139
+ fig, ax = Petal_IR_Analysis.show_2D_image(values)
140
+ __figures__["original"] = fig
81
141
 
82
142
  except LookupError as e:
83
143
  print(e)
@@ -99,8 +159,9 @@ def create_IR_core(options):
99
159
  pipe_order[i] = pipe_type
100
160
  PF = PipeFit.PipeFit(pipe_type)
101
161
  R = PF.fit_ex(pipes[i], factor=getter.factor)
102
- if options.debug:
103
- PF.plot()
162
+ if options.debug or options.report:
163
+ fig, _ =PF.plot()
164
+ __figures__["fit_{}".format(i)] = fig
104
165
 
105
166
  transforms[pipe_type] = R
106
167
  fitter[pipe_type] = PF
@@ -159,7 +220,7 @@ def create_IR_core(options):
159
220
 
160
221
  if options.debug:
161
222
  plt.show()
162
-
223
+
163
224
  return core
164
225
 
165
226
 
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env python3
2
+ """Creates the report of a core."""
3
+
4
+ import math
5
+ import sys
6
+ import json
7
+ from argparse import ArgumentParser
8
+ from pathlib import Path
9
+
10
+ import matplotlib.path as mplPath
11
+ import matplotlib.pyplot as plt
12
+ import numpy as np
13
+
14
+
15
+ try:
16
+ import petal_qc
17
+
18
+ except ImportError:
19
+ cwd = Path(__file__).parent.parent.parent
20
+ sys.path.append(cwd.as_posix())
21
+
22
+ from petal_qc.thermal.IRPetalParam import IRPetalParam
23
+ from petal_qc.thermal.create_IRCore import create_IR_core, get_IRcore_plots
24
+ from petal_qc.thermal.analyze_IRCore import analyze_IRCore, golden_from_json, get_golden_axis, plot_profile_and_golden
25
+
26
+ import petal_qc.utils.docx_utils as docx_utils
27
+ import petal_qc.utils.utils as utils
28
+
29
+
30
+
31
+ def create_report(options):
32
+ """Create a writen report.
33
+ """
34
+ ifile = Path(options.files[0]).expanduser().resolve()
35
+ if not ifile.exists():
36
+ print("input file {} does not exist.".format(ifile))
37
+ return
38
+
39
+ goldenFile = Path(options.golden).expanduser().resolve()
40
+ if not goldenFile.exists():
41
+ print("I need a golden file.")
42
+
43
+ with open(goldenFile, "r", encoding='utf-8') as fp:
44
+ golden = golden_from_json(json.load(fp))
45
+
46
+
47
+
48
+ document = docx_utils.Document()
49
+ document.add_page_numbers()
50
+ document.styles['Normal'].font.name = "Calibri"
51
+ document.add_heading(options.SN, 0)
52
+
53
+ P = document.add_paragraph(ifile.name, "Subtitle")
54
+ P.alignment = docx_utils.paragraph_align_center()
55
+
56
+ P = document.add_paragraph("Golden: {}".format(goldenFile.name), "Subtitle")
57
+ P.alignment = docx_utils.paragraph_align_center()
58
+
59
+
60
+ core = create_IR_core(options)
61
+
62
+ figures = get_IRcore_plots()
63
+ document.add_heading('Original image', level=1)
64
+ document.add_picture(figures["original"], True, 14, caption="Original Thermal image.")
65
+
66
+ document.add_heading('Result of Pipe Fit', level=1)
67
+ document.add_picture(figures["fit_0"], True, 10, caption="Size 0 fit.")
68
+ document.add_picture(figures["fit_1"], True, 10, caption="Size 1 fit.")
69
+
70
+ options.add_attachments = True
71
+ options.create_golden = False
72
+
73
+ options.files[0] = utils.output_folder(options.folder, options.out)
74
+ options.out = None
75
+ out = analyze_IRCore(options, show=False)
76
+ outDB = out[0] if len(out) else None
77
+ options.files = []
78
+
79
+ get_golden_axis(core, golden)
80
+
81
+ figures = plot_profile_and_golden(golden, core, "path_temp")
82
+ document.add_heading('Temperature along path', level=1)
83
+ document.add_picture(figures[0], True, 12, caption="Petal core .vs. Golden (side 0).")
84
+ document.add_picture(figures[0], True, 12, caption="Petal core .vs. Golden (side 1).")
85
+ for F in figures:
86
+ plt.close(F)
87
+
88
+ figures = plot_profile_and_golden(golden, core, "sensor_avg")
89
+ document.add_heading('Average Temperatur on sensors areas.', level=1)
90
+ document.add_picture(figures[0], True, 12, caption="Sensors .vs. Golden (side 0).")
91
+ document.add_picture(figures[0], True, 12, caption="Sensors .vs. Golden (side 1).")
92
+ for F in figures:
93
+ plt.close(F)
94
+
95
+ ofile = utils.output_folder(options.folder, "{}-thermal.docx".format(options.SN))
96
+ document.save(ofile)
97
+
98
+ return outDB
99
+
100
+ if __name__ == "__main__":
101
+ P = IRPetalParam()
102
+ parser = ArgumentParser()
103
+ parser.add_argument('files', nargs='*', help="Input files")
104
+ parser.add_argument("--orig", action="store_true", default=False, help="plot the original image")
105
+ parser.add_argument('--frame', default=-1, type=int, help="Frame to analize")
106
+ parser.add_argument("--out", default="core.json", help="Output file name")
107
+ parser.add_argument("--alias", default="", help="Alias")
108
+ parser.add_argument("--SN", default="", help="serial number")
109
+ parser.add_argument("--folder", default=None, help="Folder to store output files. Superseeds folder in --out")
110
+ parser.add_argument("--add_attachments", action="store_true", default=False, help="If true add the attachments section os DB file.")
111
+ parser.add_argument("--golden", default=None, help="The golden to compare width")
112
+
113
+
114
+ IRPetalParam.add_parameters(parser)
115
+
116
+ options = parser.parse_args()
117
+
118
+ if len(options.files) == 0:
119
+ print("I need an input file")
120
+ sys.exit()
121
+ else:
122
+ ifile = options.files[0]
123
+
124
+ options.debug = False
125
+ options.report = True
126
+ create_report(options)
@@ -12,6 +12,14 @@ from pathlib import PurePath
12
12
  import matplotlib.pyplot as plt
13
13
  import matplotlib.ticker as ticker
14
14
  import numpy as np
15
+
16
+ try:
17
+ import petal_qc
18
+
19
+ except ImportError:
20
+ cwd = Path(__file__).parent.parent.parent
21
+ sys.path.append(cwd.as_posix())
22
+
15
23
  from petal_qc.thermal import contours
16
24
  from petal_qc.thermal import IRBFile
17
25
  from petal_qc.thermal import IRPetal
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env python3
2
+ """Test the connection with graphana to get the value at the peak."""
3
+ import sys
4
+ from pathlib import Path
5
+ import datetime
6
+ import numpy as np
7
+ try:
8
+ import petal_qc
9
+
10
+ except ImportError:
11
+ cwd = Path(__file__).parent.parent.parent
12
+ sys.path.append(cwd.as_posix())
13
+
14
+ from petal_qc.utils.readGraphana import ReadGraphana
15
+ from petal_qc.thermal.IRDataGetter import IRDataGetter
16
+ from petal_qc.thermal.IRPetalParam import IRPetalParam
17
+ from petal_qc.thermal import IRBFile
18
+
19
+
20
+
21
+ options = IRPetalParam()
22
+ options.files = [Path("~/tmp/thermal/PPC.008.irb").expanduser().resolve()]
23
+ getter = IRDataGetter.factory(options.institute, options)
24
+ DB = ReadGraphana("localhost")
25
+ irbf = IRBFile.open_file(options.files)
26
+
27
+ frames = getter.get_analysis_frame(irbf)
28
+ print(frames[0].timestamp)
29
+ the_time = frames[0].timestamp
30
+ try:
31
+ X, val = DB.get_temperature(the_time, 5)
32
+ for x, y in zip(X, val):
33
+ print("{} - {:.1f}".format(x, y))
34
+
35
+ print("{:.1f}".format(np.min(val)))
36
+ except ValueError as e:
37
+ print(e)
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env python3
2
+ """Probando."""
3
+ import sys
4
+ import json
5
+ from pathlib import Path
6
+
7
+ try:
8
+ import petal_qc
9
+
10
+ except ImportError:
11
+ cwd = Path(__file__).parent.parent.parent
12
+ sys.path.append(cwd.as_posix())
13
+
14
+
15
+ import numpy as np
16
+ import matplotlib.pyplot as plt
17
+
18
+ from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
19
+ from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
20
+
21
+ import petal_qc
22
+ from petal_qc.thermal.IRPetalParam import IRPetalParam
23
+ from petal_qc.thermal.create_IRCore import create_IR_core
24
+ from petal_qc.thermal.analyze_IRCore import analyze_IRCore, golden_from_json, get_golden_axis, plot_profile_and_golden
25
+ from petal_qc.utils.utils import output_folder
26
+
27
+
28
+ irbFile="/private/tmp/thermal/PPB08.irb"
29
+ goldenFIle = "/private/tmp/thermal/golden-PPB.json"
30
+ outFolder = "/private/tmp/thermal"
31
+
32
+ param = IRPetalParam()
33
+ param.alias = "PPB_008"
34
+ param.SN = "20USEBC1000028"
35
+ param.tco2 = -31.7
36
+ param.out = "{}.json".format(param.alias)
37
+ param.files = [irbFile, ]
38
+ param.golden = goldenFIle
39
+ param.folder = outFolder
40
+ param.create_golden = False
41
+ param.add_attachments = True
42
+
43
+ with open(goldenFIle, "r", encoding='utf-8') as fp:
44
+ golden = golden_from_json(json.load(fp))
45
+
46
+ core = create_IR_core(param)
47
+
48
+ param.files[0] = output_folder(outFolder, param.out)
49
+ param.out = None
50
+ out = analyze_IRCore(param)
51
+ outDB = out[0] if len(out) else None
52
+ param.files = []
53
+
54
+ get_golden_axis(core, golden)
55
+
56
+ plot_profile_and_golden(golden, core, "path_temp")
57
+
58
+ plt.show()
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env python3
2
+ """Class to access graphana data."""
3
+
4
+ import datetime
5
+ from dateutil.parser import parse
6
+ import influxdb
7
+ import numpy as np
8
+
9
+
10
+ class ReadGraphana(object):
11
+ """Connect to the GraphanaDB"""
12
+
13
+ def __init__(self, host="silab15.ific.uv.es", port=8086):
14
+ """Connect to Graphan server
15
+
16
+ Args:
17
+ host (str, optional): The IP of the server.
18
+ port (int, optional): The port to the server.
19
+ """
20
+ self.client = influxdb.InfluxDBClient(host=host, port=port,
21
+ username="matlab", password="matlab",
22
+ database="clean_room")
23
+
24
+ def get_temperature(self, the_time, window=10):
25
+ """REturns the temperature
26
+
27
+ Args:
28
+ teh_time: the time to query the DB.
29
+ window: the time window, in minutes, around the given time.
30
+ """
31
+ if not isinstance(the_time, datetime.datetime):
32
+ the_time = parse(the_time)
33
+
34
+ wnd = int(window/2.0+0.5)
35
+ td = datetime.timedelta(minutes=wnd)
36
+ t1 = the_time - td
37
+ t2 = the_time + td
38
+
39
+ measure="Temp"
40
+ setup="MARTA_APP|MARTA_tt06"
41
+ query = "select location,value from {measure} where (location =~ /.*{location}*/ and time>'{t1}' and time < '{t2}') group by \"location\"".format(
42
+ measure=measure,
43
+ location=setup,
44
+ t1=t1.astimezone(datetime.UTC).isoformat(),
45
+ t2=t2.astimezone(datetime.UTC).isoformat()
46
+ )
47
+
48
+ ss = self.client.query(query)
49
+ nitems = 0
50
+ for s in ss:
51
+
52
+ nitems += len(s)
53
+
54
+ if nitems==0:
55
+ raise ValueError(("No data found"))
56
+
57
+ T = []
58
+ X = []
59
+ for s in ss:
60
+ for v in s:
61
+ T.append(v['value'])
62
+ X.append(v['time'])
63
+
64
+ return X, T
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: petal_qc
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: A collection of scripts for Petal CORE QC.
5
5
  Author-email: Carlos Lacasta <carlos.lacasta@cern.ch>
6
6
  Project-URL: Homepage, https://gitlab.cern.ch/atlas-itk/sw/db/itk-pdb-gtk-gui-utils