Coverage for python/lsst/ctrl/bps/report.py : 9%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of ctrl_bps.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22"""Supporting functions for reporting on runs submitted to a WMS
24Note: Expectations are that future reporting effort will revolve around
25 LSST oriented database tables.
26"""
28import logging
30from .wms_service import WmsStates
33SUMMARY_FMT = "{:1} {:>10} {:>3} {:>9} {:10} {:10} {:20} {:20} {:<60}"
35# logging properties
36_LOG_PROP = """\
37log4j.rootLogger=INFO, A1
38log4j.appender.A1=ConsoleAppender
39log4j.appender.A1.Target=System.err
40log4j.appender.A1.layout=PatternLayout
41log4j.appender.A1.layout.ConversionPattern={}
42"""
44_LOG = logging.getLogger()
47def print_headers():
48 """Print headers.
49 """
50 print(SUMMARY_FMT.format("X", "STATE", "%S", "ID", "OPERATOR", "PRJ", "CMPGN", "PAYLOAD", "RUN"))
51 print("-" * 156)
54def print_run(run_report):
55 """Print single run info.
57 Parameters
58 ----------
59 run_report : `WmsRunReport`
60 Information for single run.
61 """
62 # Flag any running workflow that might need human attention
63 run_flag = " "
64 if run_report.state == WmsStates.RUNNING:
65 if run_report.job_state_counts.get(WmsStates.FAILED, 0):
66 run_flag = "F"
67 elif run_report.job_state_counts.get(WmsStates.DELETED, 0):
68 run_flag = "D"
69 elif run_report.job_state_counts.get(WmsStates.HELD, 0):
70 run_flag = "H"
72 percent_succeeded = "UNK"
73 _LOG.debug("total_number_jobs = %s", run_report.total_number_jobs)
74 _LOG.debug("run_report.job_state_counts = %s", run_report.job_state_counts)
75 if run_report.total_number_jobs:
76 succeeded = run_report.job_state_counts.get(WmsStates.SUCCEEDED, 0)
77 _LOG.debug("succeeded = %s", succeeded)
78 percent_succeeded = f"{int(succeeded / run_report.total_number_jobs * 100)}"
80 print(SUMMARY_FMT.format(run_flag, run_report.state.name, percent_succeeded, run_report.wms_id,
81 run_report.operator[:10], run_report.project[:10], run_report.campaign[:20],
82 run_report.payload[:20], run_report.run[:60]))
85def group_jobs_by_state(jobs):
86 """Divide given jobs into groups based on their state value.
88 Parameters
89 ----------
90 jobs : `list` of `~lsst.ctrl.bps.wms_service.WmsJobReport`
91 Jobs to divide into groups based on state.
93 Returns
94 -------
95 by_state : `dict`
96 Mapping of job state to a list of jobs.
97 """
98 _LOG.debug("group_jobs_by_state: jobs=%s", jobs)
99 by_state = dict.fromkeys(WmsStates)
100 for state in by_state:
101 by_state[state] = [] # Note: If added [] to fromkeys(), they shared single list.
103 for job in jobs:
104 by_state[job.state].append(job)
105 return by_state
108def group_jobs_by_label(jobs):
109 """Divide given jobs into groups based on their label value.
111 Parameters
112 ----------
113 jobs : `list` of `~lsst.ctrl.bps.wms_service.WmsJobReport`
114 Jobs to divide into groups based on label.
116 Returns
117 -------
118 by_label : `dict`
119 Mapping of job state to a list of jobs .
120 """
121 by_label = {}
122 for job in jobs:
123 if job.label not in by_label:
124 by_label[job.label] = []
125 by_label[job.label].append(job)
126 return by_label
129def print_single_run_summary(run_report):
130 """Print runtime info for single run including job summary per task abbrev.
132 Parameters
133 ----------
134 run_report : `~lsst.ctrl.bps.wms_service.WmsRunReport`
135 Summary runtime info for a run + runtime info for jobs.
136 """
137 # Print normal run summary.
138 print_headers()
139 print_run(run_report)
140 print("\n\n")
142 # Print more run information.
143 print(f"Path: {run_report.path}\n")
145 print(f"{'':35} {' | '.join([f'{s.name[:6]:6}' for s in WmsStates])}")
146 print(f"{'Total':35} {' | '.join([f'{run_report.job_state_counts[s]:6}' for s in WmsStates])}")
147 print("-" * (35 + 3 + (6 + 2) * (len(run_report.job_state_counts) + 1)))
149 by_label = group_jobs_by_label(run_report.jobs)
151 # Print job level info by print counts of jobs by label and WMS state.
152 label_order = []
153 by_label_totals = {}
154 if run_report.run_summary:
155 # Workaround until get pipetaskInit job into run_summary
156 if not run_report.run_summary.startswith("pipetaskInit"):
157 label_order.append("pipetaskInit")
158 by_label_totals["pipetaskInit"] = 1
159 for part in run_report.run_summary.split(";"):
160 label, count = part.split(":")
161 label_order.append(label)
162 by_label_totals[label] = int(count)
163 else:
164 print("Warning: Cannot determine order of pipeline. Instead printing alphabetical.")
165 label_order = sorted(by_label.keys())
167 for label in label_order:
168 counts = dict.fromkeys(WmsStates, 0)
169 if label in by_label:
170 by_label_state = group_jobs_by_state(by_label[label])
171 _LOG.debug("by_label_state = %s", by_label_state)
172 counts = dict.fromkeys(WmsStates)
173 for state in WmsStates:
174 counts[state] = len(by_label_state[state])
175 elif label in by_label_totals:
176 already_counted = sum(counts.values())
177 if already_counted != by_label_totals[label]:
178 counts[WmsStates.UNREADY] += by_label_totals[label] - already_counted
179 else:
180 counts = dict.fromkeys(WmsStates, -1)
181 print(f"{label[:35]:35} {' | '.join([f'{counts[s]:6}' for s in WmsStates])}")
182 print("\n")