@@ -28,6 +28,42 @@ def _is_ongoing(doc):
2828 return False
2929
3030
31+ def _agregate_status (statuses ):
32+ """
33+ From https://github.com/SciLifeLab/genomics-status/blob/38e1b946b40cc4b25ff75621f4aa9ff63e0e7bb9/status/bioinfo_analysis.py#L195
34+ Helper function, agregates status from the lower levels
35+ """
36+
37+ # my guess here agregation is already done from flowcell status
38+ # so this condition will most probably always be true
39+ if len (set (statuses )) == 1 :
40+ status = statuses [0 ]
41+ elif 'Sequencing' in statuses :
42+ status = 'Sequencing'
43+ elif 'Demultiplexing' in statuses :
44+ status = 'Demulitplexing'
45+ elif 'Transferring' in statuses :
46+ status = 'Transferring'
47+ elif 'New' in statuses :
48+ status = 'New'
49+ elif 'QC-ongoing' in statuses :
50+ status = 'QC-ongoing'
51+ elif 'QC-done' in statuses :
52+ status = 'QC-done'
53+ elif 'BP-ongoing' in statuses :
54+ status = 'BP-ongoing'
55+ elif 'BP-done' in statuses :
56+ status = 'BP-done'
57+ elif 'Failed' in statuses :
58+ status = 'Failed'
59+ elif 'Delivered' in statuses :
60+ status = 'Delivered'
61+ else :
62+ pass
63+ # unknown status, if happens it will fail
64+ return status # may fail here, if somebody defined a new status without updating this function
65+
66+
3167class KPIBase (object ):
3268
3369 def __init__ (self ):
@@ -105,8 +141,44 @@ def __call__(self, doc):
105141 self .prep_passed += 1.0
106142
107143 def summary (self ):
108- return round (self .prep_passed / self .prep_finished , 2 )
144+ if self .prep_finished > 0 :
145+ return round (self .prep_passed / self .prep_finished , 2 )
146+ else :
147+ return None
109148
149+ class SuccessBioinfo (KPIBase ):
150+ """
151+ Definition: Samples passed in final report/total samples
152+ Approximation Passed run-lane-samples passed / total run-lane-samples
153+ """
154+ def __init__ (self ):
155+ super (SuccessBioinfo , self ).__init__ ()
156+ self .failed = 0.0
157+ self .total = 0.0
158+
159+ def __call__ (self , doc ):
160+ details = doc .get ("details" ,{})
161+ ptype = details .get ("type" , "" )
162+ for sample , run_lane in doc .get ("bioinfo" , []):
163+ if sample .get ("sample_status" ,"" ) == "Delivered" and ptype == "Production" :
164+ deliver_date = datetime .strptime (sample ["datadelivered" ], "%Y-%m-%d" )
165+ if deliver_date > self .start_date :
166+ self .total += 1.0
167+ isfail = False
168+ for key , value in sample .get ("qc" , {}).items ():
169+ if value == "Fail" :
170+ isfail = True
171+ for key , value in sample .get ("bp" , {}).items ():
172+ if value == "Fail" :
173+ isfail = True
174+ if isfail :
175+ self .failed += 1.0
176+
177+ def summary (self ):
178+ if self .total > 0 :
179+ return round (1 - (self .failed / self .total ),2 )
180+ else :
181+ return None
110182
111183### Projects
112184
@@ -301,6 +373,56 @@ def __call__(self, doc):
301373
302374 self .state += sum (osamples .values ())
303375
376+ class LoadBioinfoQueue (ProcessLoadBase ):
377+ """
378+ Definition: total number of lanes with lane QC, but no action in bioinformatic checklist
379+ Approximation: # project-run-lanes with status `Demultiplexing`, `New` or `Transferring`
380+ """
381+
382+ def __call__ (self , doc ):
383+ super (LoadBioinfoQueue , self ).__call__ (doc )
384+ run_lanes = {}
385+ ongoing = 0
386+ if _is_ongoing (doc ) and self .ptype == "Production" :
387+ for sample , run_lane in doc .get ("bioinfo" , []):
388+ status = sample .get (u'sample_status' , None )
389+ if run_lane in run_lanes .keys ():
390+ run_lanes [run_lane ].append (status )
391+ else :
392+ run_lanes [run_lane ] = [status ]
393+
394+ for run_lane , statuses in run_lanes .items ():
395+ lane_status = _agregate_status (statuses )
396+ if lane_status in [u'Demultiplexing' , u'New' , u'Transferring' ]:
397+ ongoing += 1
398+
399+ self .state += ongoing
400+
401+
402+ class LoadBioinfo (ProcessLoadBase ):
403+ """
404+ Definition: Total number of lanes with an action taken in bioinformatic checklist, in projects without close date.
405+ Approximation: # project-run-lanes with status `BP-ongoing`, `BP-done`, `QC-ongoing`, `QC-done`
406+ """
407+
408+ def __call__ (self , doc ):
409+ super (LoadBioinfo , self ).__call__ (doc )
410+ run_lanes = {}
411+ ongoing = 0
412+ if _is_ongoing (doc ) and self .ptype == "Production" :
413+ for sample , run_lane in doc .get ("bioinfo" , []):
414+ status = sample .get (u'sample_status' , None )
415+ if run_lane in run_lanes .keys ():
416+ run_lanes [run_lane ].append (status )
417+ else :
418+ run_lanes [run_lane ] = [status ]
419+
420+ for run_lane , statuses in run_lanes .items ():
421+ lane_status = _agregate_status (statuses )
422+ if lane_status in [u'BP-ongoing' , u'BP-done' , u'QC-ongoing' , u'QC-done' ]:
423+ ongoing += 1
424+
425+ self .state += ongoing
304426
305427### Turn-around times
306428
0 commit comments