@@ -305,6 +305,41 @@ def get_default_caldb_root():
305305 return _default_caldb_root
306306
307307
308+ def _summarise_mod_names (l ):
309+ """Group module names like 'LPD03' into contiguous numeric ranges
310+
311+ E.g. ['LPD00', 'LPD01', 'LPD03'] -> ['LPD00', 'LPD01'], ['LPD03']
312+ """
313+ grp = []
314+ num_re = re .compile ('(\d+)' )
315+
316+ def extends_grp (split ):
317+ if len (split ) != len (grp [0 ]):
318+ return False
319+
320+ diffs_at = [i for i , (p1 , p2 ) in enumerate (zip (grp [- 1 ], split ))
321+ if p1 != p2 ]
322+ if len (diffs_at ) != 1 or ((diff_at := diffs_at [0 ]) % 2 == 0 ):
323+ return False # >1 part changed, or non-numeric part changed
324+
325+ if len (grp ) > 2 and (grp [- 1 ][diff_at ] == grp [0 ][diff_at ]):
326+ return False # different part changing from current seq
327+
328+ return int (grp [- 1 ][diff_at ]) + 1 == int (split [diff_at ])
329+
330+ for mod in l :
331+ parts = num_re .split (mod ) # parts 1, 3, ... are numeric
332+
333+ if grp and not extends_grp (parts ):
334+ yield ['' .join (p ) for p in grp ]
335+ grp = [parts ]
336+ else :
337+ grp .append (parts )
338+
339+ if grp :
340+ yield ['' .join (p ) for p in grp ]
341+
342+
308343@dataclass
309344class SingleConstant :
310345 """A calibration constant for one detector module
@@ -1161,6 +1196,34 @@ def display_markdown_table(self, module_naming="modnum"):
11611196 from IPython .display import display , Markdown
11621197 display (Markdown (self .markdown_table (module_naming = module_naming )))
11631198
1199+ def reports_info (self ):
1200+ """Display information about the reports of found constants
1201+ """
1202+ by_rept_id = {}
1203+ for cal , mmc in self .constant_groups .items ():
1204+ for mod , sc in mmc .items ():
1205+ rid = sc .metadata ('report_id' )
1206+ by_rept_id .setdefault (sc .metadata ('report_id' ), []).append ((sc , cal , mod ))
1207+
1208+ tbl = [['Report ID' , 'Calibration types' , 'Modules' , '# constants' , ]]
1209+ for report_id , consts in by_rept_id .items ():
1210+ cals = sorted (set (t [1 ] for t in consts ))
1211+ mods = sorted (set (t [2 ] for t in consts ))
1212+
1213+ if report_id is not None :
1214+ # This is ugly, but avoids assuming production CalCat
1215+ ccv_url = consts [0 ][0 ].metadata ('view_url' )
1216+ calcat_base_url = ccv_url .split ('/calibration_constant_versions/' )[0 ]
1217+ report_details = str (report_id ), f"{ calcat_base_url } /reports/{ report_id } "
1218+ else :
1219+ report_details = "(No report)"
1220+
1221+ mods_summary = [f"{ g [0 ]} –{ g [- 1 ]} " if len (g ) > 1 else g [0 ]
1222+ for g in _summarise_mod_names (mods )]
1223+
1224+ tbl .append ([report_details , ', ' .join (cals ), ', ' .join (mods_summary ), str (len (consts ))])
1225+
1226+ return DisplayTables ([tbl ])
11641227
11651228class ConditionsBase :
11661229 calibration_types = {} # For subclasses: {calibration: [parameter names]}
0 commit comments