def _get_instance_end_text(self, instance_data, jobs_data): """Format text version of the workflow instance completion message. Args: instance_data: The data of the completed workflow instance. jobs_data: The list of data describing jobs in the workflow instance. Returns: Text message describing the workflow instance. """ TEXT_TEMPLATE = """\ Workflow %(workflow)s instance %(instance)s started at %(start_time)s \ finished after %(run_time)s at %(end_time)s with status %(status)s. Details are available at http://%(ui_host)s:%(ui_port)s/jobs/?\ workflow=%(workflow)s&instance=%(instance)s Jobs: Name | Last start | Last end | Run time | Status | Url %(jobs)s """ jobs = '' for job_data in jobs_data: jobs += '%s | ' % job_data.job if not job_data.last_start_time: jobs += '| | ' else: jobs += '%s | ' % timestamp_to_str(job_data.last_start_time) if not job_data.last_end_time: jobs += '| | ' else: jobs += '%s | ' % timestamp_to_str(job_data.last_end_time) delta = int(job_data.last_end_time - job_data.last_start_time) jobs += '%s | ' % datetime.timedelta(seconds=delta) jobs += '%s | ' % Status.to_string(job_data.status) jobs += ('http://%s:%s/executions/?workflow=%s&instance=%s&' 'job=%s\n' % (self._ui_host, self._ui_port, job_data.workflow, job_data.instance, job_data.job)) start_time = timestamp_to_str(instance_data.start_time) end_time = timestamp_to_str(instance_data.end_time) delta = int(instance_data.end_time - instance_data.start_time) run_time = datetime.timedelta(seconds=delta) status = Status.to_string(instance_data.status) return TEXT_TEMPLATE % {'workflow': instance_data.workflow, 'instance': instance_data.instance, 'start_time': start_time, 'end_time': end_time, 'run_time': run_time, 'status': status, 'ui_host': self._ui_host, 'ui_port': self._ui_port, 'jobs': jobs}
def _get_instance_end_text(self, instance_data, jobs_data): """Format text version of the workflow instance completion message. Args: instance_data: The data of the completed workflow instance. jobs_data: The list of data describing jobs in the workflow instance. Returns: Text message describing the workflow instance. """ TEXT_TEMPLATE = """\ Workflow %(workflow)s instance %(instance)s started at %(start_time)s \ finished after %(run_time)s at %(end_time)s with status %(status)s. Details are available at http://%(ui_host)s:%(ui_port)s/jobs/?\ workflow=%(workflow)s&instance=%(instance)s Jobs: Name | Last start | Last end | Run time | Status | Url %(jobs)s """ jobs = '' for job_data in jobs_data: jobs += '%s | ' % job_data.job if not job_data.last_start_time: jobs += '| | ' else: jobs += '%s | ' % timestamp_to_str(job_data.last_start_time) if not job_data.last_end_time: jobs += '| | ' else: jobs += '%s | ' % timestamp_to_str(job_data.last_end_time) delta = int(job_data.last_end_time - job_data.last_start_time) jobs += '%s | ' % datetime.timedelta(seconds=delta) jobs += '%s | ' % Status.to_string(job_data.status) jobs += ('http://%s:%s/executions/?workflow=%s&instance=%s&' 'job=%s\n' % (self._ui_host, self._ui_port, job_data.workflow, job_data.instance, job_data.job)) start_time = timestamp_to_str(instance_data.start_time) end_time = timestamp_to_str(instance_data.end_time) delta = int(instance_data.end_time - instance_data.start_time) run_time = datetime.timedelta(seconds=delta) status = Status.to_string(instance_data.status) return TEXT_TEMPLATE % { 'workflow': instance_data.workflow, 'instance': instance_data.instance, 'start_time': start_time, 'end_time': end_time, 'run_time': run_time, 'status': status, 'ui_host': self._ui_host, 'ui_port': self._ui_port, 'jobs': jobs }
def _get_progress(execution_history, instance_start_time, instance_end_time): """Extract info required to construct job execution progress bar. Args: execution_history: The list of execution records describing runs of this job. instance_start_time: The start time of the workflow instance that this job belongs to. instance_end_time: The end time of the workflow instance that this job belongs to or the current time if the instance did not yet finish. Returns: The list of (percentage, status) tuples where status describes the state that the job was in on the timeline of the instance, while the percentage tells for how long this status lasted as a percentage of the total instance run time. Ordering of the tuples on the list follows the job state transitions so the job execution timeline can be reconstructed from its contents. """ result = [] instance_duration = instance_end_time - instance_start_time if instance_duration == 0: return result still_running = False first_start_time = None for record in execution_history: assert record.start_time, record if not first_start_time: first_start_time = record.start_time if record.end_time: execution_duration = record.end_time - record.start_time status = (Status.to_string(Status.SUCCESS) if record.exit_code == 0 else Status.to_string(Status.FAILURE)) else: assert not still_running still_running = True execution_duration = instance_end_time - record.start_time status = Status.to_string(Status.RUNNING) percentage = int(100. * execution_duration / instance_duration) percentage = max(1, percentage) result.append((percentage, status)) if first_start_time: duration = first_start_time - instance_start_time else: duration = instance_duration result.insert(0, (int(100. * duration / instance_duration), '')) return result
def send_instance_end_message(self, to, instance_data, jobs_data): """Send a message describing workflow instance run. Args: to: The list of recipient email addresses. instance_data: The data of the completed workflow instance. jobs_data: The list of data describing jobs in the workflow instance. """ jobs_data = Emailer._sort_jobs(jobs_data) text = self._get_instance_end_text(instance_data, jobs_data) html = self._get_instance_end_html(instance_data, jobs_data) status = Status.to_string(instance_data.status) subject = '%s for workflow %s' % (status, instance_data.workflow) self._send_message(subject, to, text, html)
def _get_instance_end_html(self, instance_data, jobs_data): """Format html version of the workflow instance completion message. Args: instance_data: The data of the completed workflow instance. jobs_data: The list of data describing jobs in the workflow instance. Returns: Html message describing the workflow instance. """ HTML_TEMPLATE = """\ <html> <head></head> <body> <p> Workflow %(workflow)s instance %(instance)s started at %(start_time)s finished after %(run_time)s at %(end_time)s with status <span style="background-color:%(instance_status_color)s;">%(status)s</span>. Click <a href="http://%(ui_host)s:%(ui_port)s/jobs/?\ workflow=%(workflow)s&instance=%(instance)s">here</a> for details. </p> <p>Jobs:<br/> <table style="border-collapse:collapse;"> <tr> <th style="border:1px dotted grey;">Name</th> <th style="border:1px dotted grey;">Last start</th> <th style="border:1px dotted grey;">Last end</th> <th style="border:1px dotted grey;">Run time</th> <th style="border:1px dotted grey;">Status</th> </tr> %(jobs)s </table> </p> </body> </html> """ jobs = '' for job_data in jobs_data: jobs += '<tr>' jobs += ('<td style="border:1px dotted grey;">' '<a href="http://%s:%s/executions/?workflow=%s&' 'instance=%s&job=%s">%s</a></td>' % (self._ui_host, self._ui_port, job_data.workflow, job_data.instance, job_data.job, job_data.job)) if not job_data.last_start_time: jobs += ('<td style="border:1px dotted grey;"></td>' '<td style="border:1px dotted grey;"></td>' '<td style="border:1px dotted grey;"></td>') else: jobs += ('<td style="border:1px dotted grey;">%s</td>' % timestamp_to_str(job_data.last_start_time)) if not job_data.last_end_time: jobs += ('<td style="border:1px dotted grey;"></td>' '<td style="border:1px dotted grey;"></td>') else: delta = int(job_data.last_end_time - job_data.last_start_time) jobs += ('<td style="border:1px dotted grey;">%s</td>' '<td style="border:1px dotted grey;">%s</td>' % (timestamp_to_str(job_data.last_end_time), datetime.timedelta(seconds=delta))) jobs += ('<td style="border:1px dotted grey;background-color:%s;' 'text-align: center;">%s</td>\n' % (Status.COLORS[job_data.status], Status.to_string(job_data.status))) jobs += '</tr>' start_time = timestamp_to_str(instance_data.start_time) end_time = timestamp_to_str(instance_data.end_time) delta = int(instance_data.end_time - instance_data.start_time) run_time = datetime.timedelta(seconds=delta) instance_status_color = Status.COLORS[instance_data.status] status = Status.to_string(instance_data.status) return HTML_TEMPLATE % { 'workflow': instance_data.workflow, 'instance': instance_data.instance, 'start_time': start_time, 'end_time': end_time, 'run_time': run_time, 'instance_status_color': instance_status_color, 'status': status, 'ui_host': self._ui_host, 'ui_port': self._ui_port, 'jobs': jobs }
def _get_instance_end_html(self, instance_data, jobs_data): """Format html version of the workflow instance completion message. Args: instance_data: The data of the completed workflow instance. jobs_data: The list of data describing jobs in the workflow instance. Returns: Html message describing the workflow instance. """ HTML_TEMPLATE = """\ <html> <head></head> <body> <p> Workflow %(workflow)s instance %(instance)s started at %(start_time)s finished after %(run_time)s at %(end_time)s with status <span style="background-color:%(instance_status_color)s;">%(status)s</span>. Click <a href="http://%(ui_host)s:%(ui_port)s/jobs/?\ workflow=%(workflow)s&instance=%(instance)s">here</a> for details. </p> <p>Jobs:<br/> <table style="border-collapse:collapse;"> <tr> <th style="border:1px dotted grey;">Name</th> <th style="border:1px dotted grey;">Last start</th> <th style="border:1px dotted grey;">Last end</th> <th style="border:1px dotted grey;">Run time</th> <th style="border:1px dotted grey;">Status</th> </tr> %(jobs)s </table> </p> </body> </html> """ jobs = '' for job_data in jobs_data: jobs += '<tr>' jobs += ('<td style="border:1px dotted grey;">' '<a href="http://%s:%s/executions/?workflow=%s&' 'instance=%s&job=%s">%s</a></td>' % (self._ui_host, self._ui_port, job_data.workflow, job_data.instance, job_data.job, job_data.job)) if not job_data.last_start_time: jobs += ('<td style="border:1px dotted grey;"></td>' '<td style="border:1px dotted grey;"></td>' '<td style="border:1px dotted grey;"></td>') else: jobs += ('<td style="border:1px dotted grey;">%s</td>' % timestamp_to_str(job_data.last_start_time)) if not job_data.last_end_time: jobs += ('<td style="border:1px dotted grey;"></td>' '<td style="border:1px dotted grey;"></td>') else: delta = int(job_data.last_end_time - job_data.last_start_time) jobs += ('<td style="border:1px dotted grey;">%s</td>' '<td style="border:1px dotted grey;">%s</td>' % ( timestamp_to_str(job_data.last_end_time), datetime.timedelta(seconds=delta))) jobs += ('<td style="border:1px dotted grey;background-color:%s;' 'text-align: center;">%s</td>\n' % ( Status.COLORS[job_data.status], Status.to_string(job_data.status))) jobs += '</tr>' start_time = timestamp_to_str(instance_data.start_time) end_time = timestamp_to_str(instance_data.end_time) delta = int(instance_data.end_time - instance_data.start_time) run_time = datetime.timedelta(seconds=delta) instance_status_color = Status.COLORS[instance_data.status] status = Status.to_string(instance_data.status) return HTML_TEMPLATE % {'workflow': instance_data.workflow, 'instance': instance_data.instance, 'start_time': start_time, 'end_time': end_time, 'run_time': run_time, 'instance_status_color': instance_status_color, 'status': status, 'ui_host': self._ui_host, 'ui_port': self._ui_port, 'jobs': jobs}