def email_callback(**kwargs): email_template = f""" From {kwargs['yesterday_ds']}to {kwargs['ds']}, the number of trips observed was { kwargs['ti'].xcom_pull(key='xcom_trips', task_ids='computing_stats') } across {kwargs['ti'].xcom_pull(key='xcom_devices', task_ids='computing_stats')} devices. Company Trips Table: { kwargs['ti'].xcom_pull(key='trips_table', task_ids='computing_stats') } Company Devices Table: { kwargs['ti'].xcom_pull(key='device_table', task_ids='computing_stats') } <br> Status Table <br> { kwargs['ti'].xcom_pull(key='sum_table', task_ids='computing_stats')} """ send_email(to=[ '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', ], subject=f"Dockless Stats for { kwargs['yesterday_ds'] }", html_content=email_template) return True
def notify_email_failure(contextDict, **kwargs): """ Send custom email alerts. Params: contextDict : context dictionery from airflow **kwargs : kwargs for extra params. """ if 'file' in (contextDict['params']): files = (contextDict['params']['file']) else: files = [] files.append(get_log_location(contextDict)) email_list = (contextDict['params']['email_list']) # email title. title = "{} DA: {} {}".format(CLIENT, contextDict['task'].task_id, failure_title) # email contents body = "{}<br>{}<br><br>{}<br><br>{}".format( salutation, failure_body, contextDict['params']['task_failure_msg'], regards) files = [f for f in files if os.path.exists(f)] if len(files) == 0: send_email(email_list, title, body) else: send_email(email_list, title, body, files)
def sendemail(to_addr_list, cc_addr_list, subject, message, **kwargs): # xcom_pull alert from last task's return value task_instance = kwargs["task_instance"] cd_alert = task_instance.xcom_pull(task_ids="detect_outliers") # if no outliers found, skip sending email if bool(cd_alert): # prepare email body for cd, alert in cd_alert.items(): logging.info(alert) files = [] for key, value in alert.items(): files.append(prefix + value[1]) html_content = make_html_content(cd, alert, message) send_email( to_addr_list[cd], subject, html_content, files=files, cc=cc_addr_list[cd], ) return "Outliers founded. Alert Email sent. Success!" return "No alert generated. Email not sent."
def airflow_email_report( contact_email: str, org_name: str, email_metadata_xcom_args, ti, **kwargs ): """Send email indicating that data has been uploaded. Includes number of rows uploaded, and info about dropped rows and values. """ subject_header = "[UPLOAD COMPLETE] {} Report for Mission Impact upload on {}".format( org_name, kwargs["execution_date"].strftime("%m/%d/%y") ) dropped_data = ti.xcom_pull(**email_metadata_xcom_args) num_rows_uploaded = dropped_data[data_processor.NUM_ROWS_TO_UPLOAD_KEY] dropped_rows = dropped_data[data_processor.DROPPED_ROWS_KEY] dropped_vals = dropped_data[data_processor.DROPPED_VALUES_KEY] message = format_successful_upload(num_rows_uploaded, dropped_rows, dropped_vals) email_content = HEADER + message log_email(contact_email, email_content) if contact_email: send_email( contact_email, subject_header, email_content, mime_subtype="mixed", mime_charset="utf8", )
def notify_success_email(contextDict, **kwargs): """Send custom email alerts.""" dag_id = contextDict['dag'].dag_id task_id = contextDict['task'].task_id if 'msg' in kwargs: msg = kwargs['msg'] else: msg = "The {} job succeeded for {}.".format( dag_id, contextDict['yesterday_ds']) # email title. title = "Airflow alert: {} succeeded".format(dag_id) # email contents body = """ Hi Everyone, <br> <br> {}<br> <br> Forever yours,<br> Airflow bot <br> """.format(msg) send_email(['*****@*****.**', '*****@*****.**'], title, body)
def airflow_email_column_mapping_validation_failure( contact_email: str, org_name: str, email_metadata_xcom_args: str, column_mapping_sheet_id: str, ti, **kwargs, ): subject_header = "[ACTION REQUIRED] {} Failed column mapping validation for GDI Pipeline ({})".format( org_name, kwargs["execution_date"].strftime("%m/%d/%y") ) message = f"""Your column mappings for the GDI pipeline are invalid. Please fix this issue at: https://docs.google.com/spreadsheets/d/{column_mapping_sheet_id} <br>Note that no data will be uploaded to Gateway until this issue is fixed. Details below:<br>""" message += format_validation_failures(ti.xcom_pull(**email_metadata_xcom_args)) message += "<br>Be aware that column mappings are case sensitive." email_content = HEADER + message log_email(contact_email, email_content) if contact_email: send_email( contact_email, subject_header, email_content, mime_subtype="mixed", mime_charset="utf8", )
def execute(self, context): send_email(self.to, self.subject, self.html_content, files=self.files, cc=self.cc, bcc=self.bcc)
def email_failures(task_instance, ds, **kwargs): status = task_instance.xcom_pull(task_ids="download_data") error_agencies = status["errors"] html_report = pd.DataFrame(error_agencies).to_html(border=False) html_content = f"""\ The following agency GTFS feeds could not be extracted on {ds}: {html_report} """ send_email( to=[ "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", ], html_content=html_content, subject= (f"Operator GTFS Errors for {datetime.datetime.now().strftime('%Y-%m-%d')}" ), )
def on_failure_email(dag_id, task_id, message): """ This function is used to send an email to the registered email id in the configuration variable 'config' :param dag_id: str, name of the dag :param task_id: str, name of the task which failed :param message: str, exception trace that lead to the failure :return: None """ # check for empty if is_empty(dag_id) or is_empty(task_id) or is_empty(message): raise InvalidArguments("dag_id, task_id and message can't be empty") # check for none if dag_id is None or task_id is None: raise InvalidArguments("dag_id, task_id and message can't be None") message = '<img src="https://airflow.apache.org/images/feature-image.png" width="400" height="100"/>' \ '<h2>AIRFLOW TASK FAILURE:</h2><hr/>' \ '<strong>DAG : </strong> {} <br/><hr/>' \ '<strong>TASKS:</strong> {}<br/><hr/>' \ '<strong>Reason:</strong> {}<br/><hr/>' \ .format(dag_id, task_id, message) try: config = json.loads(Variable.get("config")) email = config['email'] except NameError as e: raise ConfigVariableNotFoundException() send_email(to=email, subject='Airflow Notification', html_content=message)
def airflow_email_field_mapping_validation_failure( contact_email: str, org_name: str, email_metadata_xcom_args: str, field_mapping_sheet_id: str, ti, **kwargs, ): subject_header = "[ACTION REQUIRED] {} Failed field mapping validation for GDI Pipeline ({})".format( org_name, kwargs["execution_date"].strftime("%m/%d/%y") ) message = f"""Your field mappings for the GDI pipeline are invalid. Please fix this issue at: https://docs.google.com/spreadsheets/d/{field_mapping_sheet_id}" <br>Note that no data will be uploaded to Gateway until this issue is fixed. Details below:<br><ul>""" failure_map = ti.xcom_pull(**email_metadata_xcom_args) for field_name, failures in failure_map.items(): message += f"<li>{field_name}</li>" message += format_validation_failures(failures) message += "</ul>" email_content = HEADER + message log_email(contact_email, email_content) if contact_email: send_email( contact_email, subject_header, email_content, mime_subtype="mixed", mime_charset="utf8", )
def _send_email(**context): ti = context['ti'] msg = ti.xcom_pull(task_ids='rclone_copy', key='number_of_files') new_folders = ti.xcom_pull(task_ids='new_folders_check', key='new_folders') num_folders = len(new_folders) if new_folders else 0 status = 'Backup Succeeded' if num_folders else 'Nothing New' html_content = """ <h2>SickKids Dropbox Backup Notfication</h2> <p>Status: {status}</p> <p>Date: {date}</p> <p>Number of folders backed up: {num_folders}</p> <p>Folders: {folders}</p> """.format(status=status, date=context['ds'], num_folders=num_folders, folders=new_folders) subject = 'SickKids Airflow: %s (%s)' % (status, context['ds']) print "Sending email: \n\n\tSubject: %s\n%s" % (subject, html_content) send_email(to=['*****@*****.**'], subject=subject, html_content=html_content)
def notify_email(): barcodes = [] if not os.path.exists(VIDEO_LIST): log.error("Video list file '%s' doesn't exists" % VIDEO_LIST) raise Exception with open(VIDEO_LIST, 'r') as video_list_file: video_list = json.load(video_list_file) for barcode, path in video_list.items(): barcodes.append(barcode) if len(barcodes) > 1: email_title = "Task with videos %s has been finished!" % ", ".join( barcodes) else: email_title = "Task with video %s has been finished!" % barcodes[0] email_body = """ Dear AV team, <br> <br> Archival Information Package for the following videos are ready:<br/><br/> <i>%s</i><br/><br/> Your sincerely,<br> AV workflow<br> """ % "<br/>".join(barcodes) emails = AV_STAFF_EMAIL_LIST.split(',') send_email(emails, email_title, email_body)
def _send_email_password_updated(self, new_pass, email_to_list): """Envia email para admins informando a nova senha após atualização """ subject = "Atualização da senha da WS do BACEN (STA)" dag_id = 'update_password_sta_bacen' dag_url = f'http://airflow.seges.mp.intra/tree?dag_id={dag_id}' content = f""" Olá! <br> <br> A senha utilizada pelas dags que acessam arquivos no BACEN via o STA foi atualizada. <br> <br> A nova senha é: <b>{new_pass}</b> <br> O login é: <b>841260010.NITAI</b> <br> Caso queira testar acesse: <a href="https://sta.bcb.gov.br/" target="_blank">https://sta.bcb.gov.br/</a> <br> <br> Esta mensagem foi enviada pela dag <b>{dag_id}</b>. <br> Para acessar: <a href="{dag_url}" target="_blank">{dag_url}</a> <br> <br> Airflow-bot. <br> <br> """ send_email(to=email_to_list, subject=subject, html_content=replace_to_html_encode(content))
def email_failures(task_instance, ds, **kwargs): if is_development(): print("Skipping since in development mode!") return status = task_instance.xcom_pull(task_ids="download_data") error_agencies = status["errors"] if len(error_agencies) > 0: html_report = pd.DataFrame(error_agencies).to_html(border=False) html_content = f"""\ The following agency GTFS feeds could not be extracted on {ds}: {html_report} """ else: html_content = "All feeds were downloaded successfully!" send_email( to=[ "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", ], html_content=html_content, subject= (f"Operator GTFS Errors for {datetime.datetime.now().strftime('%Y-%m-%d')}" ), )
def execute(self, context): ( fetch_record_count, send_data_to_submission, ) = context['ti'].xcom_pull(key='exception', task_ids=('fetch_record_count', 'send_data_to_submission')) if fetch_record_count is None: message = '<img src="https://airflow.apache.org/images/feature-image.png" width="400" height="100"/>' \ '<h2>AIRFLOW TASK FAILURE:</h2><hr/>' \ '<strong>DAG : </strong> {} <br/><hr/>' \ '<strong>TASKS:</strong> {}<br/><hr/>' \ '<strong>Reason:</strong> {}<br/><hr/>' \ .format(self.dag_id, 'send_data_to_submission', send_data_to_submission) elif send_data_to_submission is None: message = '<img src="https://airflow.apache.org/images/feature-image.png" width="400" height="100"/>' \ '<h2>AIRFLOW TASK FAILURE:</h2><hr/>' \ '<strong>DAG : </strong> {} <br/><hr/>' \ '<strong>TASKS:</strong> {}<br/><hr/>' \ '<strong>Reason:</strong> {}<br/><hr/>' \ .format(self.dag_id, 'fetch_record_count', fetch_record_count) try: config = json.loads(Variable.get("config")) email = config['email'] except NameError as e: raise ConfigVariableNotFoundException() send_email(to=email, subject='Airflow Notification', html_content=message)
def mail_files(to, subject, debug_to=None, content='', cc=None, file_task='gen_excel', **kwargs): if not ENABLE_EMAIL and not debug_to: return result_txt = kwargs['ti'].xcom_pull(task_ids=file_task) logging.info(result_txt) result = json.loads(result_txt) if result['status'] != 'success' or \ len(result['data']['files']) == 0: return files = result['data']['files'] subject = subject.format(ds=ds(kwargs['ti'])) cc = cc + EMAIL_DEFAULT_CC if cc else EMAIL_DEFAULT_CC html_content = EMAIL_TMPL.format(content) if debug_to and not IS_PRD: to = debug_to cc = None send_email(to=to , subject=subject , html_content=html_content , files=files , cc=cc , mime_charset='utf-8' )
def email_callback(df): df = pd.read_csv(io.StringIO(df)) files = glob.glob("figures/*.png") content = build_table(df, 'blue_light') send_email(to=["*****@*****.**"], subject='Report', html_content=content, files=files)
def email_alert(task_instance, error_info): try: subject, html_content, _ = task_instance.get_email_subject_content(error_info) email = sensor_work.execution_context.get('email') send_email(email, subject, html_content) except Exception: # pylint: disable=broad-except sensor_work.log.warning("Exception alerting email.", exc_info=True)
def sendEmail(): files = glob.glob("test/*.pdf") print(files) content = '<h1>Radar</h1><br>' + str(kw) + '<br>' + str(files) send_email(to=receiversList[0], bcc=receiversList[1:], subject='Radar Test ' + str(kw), html_content=content, files=files)
def execute(self, context): send_email(self.to, self.subject, self.html_content, files=self.files, cc=self.cc, bcc=self.bcc, mime_subtype=self.mime_subtype, mime_charset=self.mime_charset)
def callable_func(context): print(context) dag_id = context.get('task_instance').dag_id task_id = context.get('task_instance').task_id log_url = context.get('task_instance').log_url log_filepath = context.get('task_instance').log_filepath exec_date = context.get('execution_date') email_title = """ Airflow Alert-- Airflow Task {task_id} is Success """.format(task_id=task_id) email_body = """ <table class="reportWrapperTable" cellspacing="4" cellpadding="2" width="100%" rules="rows" style="border-collapse:collapse;color:#1f2240;background-color:#ffffff"> <caption style="background-color:#ffffff;color:#1f2240;margin-bottom:.5em;font-size:18pt;width:100%;border:0">Airflow Alert</caption> <thead style="100%;color:#ffffff;background-color:#1f2240;font-weight:bold"> <tr> <th scope="col" style="background-color:#1f2240">Name</th> <th scope="col" style="background-color:#1f2240">Status</th> </tr> </thead> <tbody> <tr> <td>The Task: </td> <td>{task_id}</td> </tr> <tr> <td>The Dag: </td> <td>{dag_id}</td> </tr> <tr> <td>Execution Time: </td> <td>{exec_date}</td> </tr> <tr> <td>Status: </td> <td> <span style="font-size: 20px; color: #008000;"> Success. </span> </td> </tr> <tr> <td>Log Url: </td> <td><a href="{log_url}">Link</a></td> </tr> </tbody></table> """.format(task_id=task_id, dag_id=dag_id, log_url=log_url, exec_date=exec_date) to = TO_MAIL send_email(to, email_title, email_body)
def notify_email(contextDict, **kwargs): title = "Airflow alert: file does not exist.".format(**contextDict) body = """ Hi Everyone, <br> <br> There's been an error in the data_exist job. No data available. <br> <br>Please, fix it. """.format(**contextDict) send_email('*****@*****.**', title, body)
def send_bdm_dim_file_email(ds, ds_nodash, **kwargs): cursor = get_hive_cursor() sql = """ select dt, area_name, --points, bdm_name, hbdm_name, take_time_avg, delivery_time_avg, score_peisong_avg, cancel_order_cnt, concat(cast(nvl(round(sys_cancel_order_cnt * 100 / cancel_order_cnt,1),0) as string),'%'), concat(cast(nvl(round(user_cancel_order_cnt * 100/cancel_order_cnt,1),0) as string),'%'), concat(cast(nvl(round(merchant_cancel_order_cnt * 100/cancel_order_cnt,1),0) as string),'%') from ofood_bi.ofood_bdm_area_metrics_report where dt = '{dt}' """.format(dt=ds, ds=ds_nodash) headers = [ 'day', 'area_name', #'points', 'bdm_name', 'hbdm_name', 'time_pick', 'time_peisong', 'score_peisong', 'total_cancle', 'total_auto_cancle', 'total_merchant_cancle', 'total_user_cancle' ] logging.info('Executing: %s', sql) cursor.execute(sql) rows = cursor.fetchall() file_name = '/tmp/ofood_bdm_dim_metrics_{dt}.csv'.format(dt=ds) with codecs.open(file_name, 'w', 'utf_8_sig') as f: f_csv = csv.writer(f) f_csv.writerow(headers) f_csv.writerows(rows) # send mail email_to = Variable.get("ofood_honour_metrics_receivers").split() # email_to = ['*****@*****.**'] email_subject = 'ofood-BDM履约每日数据_{dt}'.format(dt=ds) email_body = 'ofood-BDM履约每日数据' send_email(email_to, email_subject, email_body, [file_name], mime_charset='utf-8')
def send_email_notification(self, email_content): send_email(self.email_to, 'expectations in suite ' + self.expectation_suite_name + ' not met', email_content, files=None, cc=None, bcc=None, mime_subtype='mixed', mime_charset='us_ascii')
def alert_deaths(**context): # Fetch the cleaned DataFrame from the above XCOM df = context["ti"].xcom_pull(key="df") email = context["ti"].xcom_pull(key="email") if df.head(n=1).loc[df['positive'] > 200000, 'More than 7 million cases'] == True: send_email(email, "U.S. Cases Exceed 7 million", cases_body) else: pass
def alert_cases(**context): # Fetch the cleaned DataFrame from the above XCOM df = context["ti"].xcom_pull(key="df") # Send email if cases exceed 7 million cases = df['positive'] > 7000000 if cases.bool(): send_email(email, "U.S. Cases Exceed 7 million", cases_body) else: pass
def execute(self, context): message = "<h3> Dag Successfull </h3>" try: config = json.loads(Variable.get("config")) email = config['email'] except NameError as e: raise ConfigVariableNotFoundException() send_email(to=email, subject='Airflow Notification', html_content=message)
def send_alert_email(mark, context, raise_exception=False): title = "[%s] %s@%s: Airflow alert" % (mark, context["dag"].dag_id, context["ti"].hostname) body = ("Log: <a href='{ti.log_url}'>Link</a><br>" "Host: {ti.hostname}<br>" "Log file: {ti.log_filepath}<br>").format(**{"ti": context["ti"]}) try: send_email(context["task"].email, title, body) except Exception as e: logging.exception("send email failed") if raise_exception: raise e
def execute(self, context: Context): send_email( self.to, self.subject, self.html_content, files=self.files, cc=self.cc, bcc=self.bcc, mime_subtype=self.mime_subtype, mime_charset=self.mime_charset, conn_id=self.conn_id, custom_headers=self.custom_headers, )
def send_shop_list_file_email(ds, ds_nodash, **kwargs): cursor = get_hive_cursor() sql = """ select dt, shop_id, title, bd_name, bdm_name, hbdm_name, his_order_cnt, if(closed = 0,'Y','N'), if(is_new_user_act = 1,'Y','N'), if(is_promotion_act = 1,'Y','N'), yy_peitime, product_cnt, addr, account_number from ofood_bi.ofood_shop_list_metrics_report where dt = '{dt}' """.format(dt=ds, ds=ds_nodash) headers = [ 'day', 'shop_id', 'title', 'bd_name', 'bdm_name', 'hbdm_name', 'his_order_cnt', 'is_open(Y or N)', 'activity_of_new_user(Y or N)', 'activity_of_promotion(Y or N)', 'business_time', 'menu_item', 'location', 'opay_account' ] logging.info('Executing: %s', sql) cursor.execute(sql) rows = cursor.fetchall() file_name = '/tmp/ofood_shop_list_metrics_{dt}.csv'.format(dt=ds) with codecs.open(file_name, 'w', 'utf_8_sig') as f: f_csv = csv.writer(f) f_csv.writerow(headers) f_csv.writerows(rows) # send mail email_to = Variable.get("ofood_honour_metrics_receivers").split() # email_to = ['*****@*****.**'] email_subject = 'ofood-商家明细List每日数据_{dt}'.format(dt=ds) email_body = 'ofood-商家明细List每日数据' send_email(email_to, email_subject, email_body, [file_name], mime_charset='utf-8')
def manage_slas(self, dag, session=None): """ Finding all tasks that have SLAs defined, and sending alert emails where needed. New SLA misses are also recorded in the database. Where assuming that the scheduler runs often, so we only check for tasks that should have succeeded in the past hour. """ TI = models.TaskInstance sq = ( session .query( TI.task_id, func.max(TI.execution_date).label('max_ti')) .filter(TI.dag_id == dag.dag_id) .filter(TI.state == State.SUCCESS) .filter(TI.task_id.in_(dag.task_ids)) .group_by(TI.task_id).subquery('sq') ) max_tis = session.query(TI).filter( TI.dag_id == dag.dag_id, TI.task_id == sq.c.task_id, TI.execution_date == sq.c.max_ti, ).all() ts = datetime.now() SlaMiss = models.SlaMiss for ti in max_tis: task = dag.get_task(ti.task_id) dttm = ti.execution_date if task.sla: dttm = dag.following_schedule(dttm) while dttm < datetime.now(): following_schedule = dag.following_schedule(dttm) if following_schedule + task.sla < datetime.now(): session.merge(models.SlaMiss( task_id=ti.task_id, dag_id=ti.dag_id, execution_date=dttm, timestamp=ts)) dttm = dag.following_schedule(dttm) session.commit() slas = ( session .query(SlaMiss) .filter(SlaMiss.email_sent == False or SlaMiss.notification_sent == False) .filter(SlaMiss.dag_id == dag.dag_id) .all() ) if slas: sla_dates = [sla.execution_date for sla in slas] qry = ( session .query(TI) .filter(TI.state != State.SUCCESS) .filter(TI.execution_date.in_(sla_dates)) .filter(TI.dag_id == dag.dag_id) .all() ) blocking_tis = [] for ti in qry: if ti.task_id in dag.task_ids: ti.task = dag.get_task(ti.task_id) blocking_tis.append(ti) else: session.delete(ti) session.commit() blocking_tis = ([ti for ti in blocking_tis if ti.are_dependencies_met(session=session)]) task_list = "\n".join([ sla.task_id + ' on ' + sla.execution_date.isoformat() for sla in slas]) blocking_task_list = "\n".join([ ti.task_id + ' on ' + ti.execution_date.isoformat() for ti in blocking_tis]) # Track whether email or any alert notification sent # We consider email or the alert callback as notifications email_sent = False notification_sent = False if dag.sla_miss_callback: # Execute the alert callback self.logger.info(' --------------> ABOUT TO CALL SLA MISS CALL BACK ') dag.sla_miss_callback(dag, task_list, blocking_task_list, slas, blocking_tis) notification_sent = True email_content = """\ Here's a list of tasks thas missed their SLAs: <pre><code>{task_list}\n<code></pre> Blocking tasks: <pre><code>{blocking_task_list}\n{bug}<code></pre> """.format(bug=asciiart.bug, **locals()) emails = [] for t in dag.tasks: if t.email: if isinstance(t.email, basestring): l = [t.email] elif isinstance(t.email, (list, tuple)): l = t.email for email in l: if email not in emails: emails.append(email) if emails and len(slas): send_email( emails, "[airflow] SLA miss on DAG=" + dag.dag_id, email_content) email_sent = True notification_sent = True # If we sent any notification, update the sla_miss table if notification_sent: for sla in slas: if email_sent: sla.email_sent = True sla.notification_sent = True session.merge(sla) session.commit() session.close()
def execute(self, context): send_email(self.to, self.subject, self.html_content, files=self.files)