def report_duplicates(self): """ Send a report on duplicate name+language+dictionary mappings. """ if not self.dups: self.logger.error("no duplicates to report") return if self.recip: recips = [self.recip] else: recips = Job.get_group_email_addresses("GlossaryDupGroup") if not recips: raise Exception("no recipients found for glossary dup message") body = ["The following {:d} sets of ".format(len(self.dups)), "duplicate glossary mappings were found in the CDR ", "on {}. ".format(self.SERVER.upper()), "Mappings for any phrase + language + dictionary must ", "be unique. ", "Please correct the data so that this requirement is met. ", "You may need to look at the External Map Table for ", "Glossary Terms to find some of the mappings.\n"] template = "\n{} (language={!r} dictionary={!r})\n" for key in sorted(self.dups): name, language, dictionary = key args = name.upper(), language, dictionary body.append(template.format(*args)) for doc_id in self.dups[key]: body.append("\tCDR{:010d}\n".format(doc_id)) body = "".join(body) opts = dict(subject=self.SUBJECT, body=body) message = cdr.EmailMessage(self.SENDER, recips, **opts) message.send() self.logger.info("duplicate mapping notification sent to %r", recips)
def send_report(self, report): """ Email the report to the right recipient list. report Serialized HTML document for the report. """ if self.recip: recips = [self.recip] else: if self.test: group = "Test Publishing Notification" else: group = { "spanish": "GovDelivery ES Docs Notification", "english": "GovDelivery EN Docs Notification", "trials": "GovDelivery Trials Notification" }.get(self.key) recips = Job.get_group_email_addresses(group) if recips: subject = "[%s] %s" % (self.TIER.name, self.title) opts = dict(subject=subject, body=report, subtype="html") message = cdr.EmailMessage(self.SENDER, recips, **opts) message.send() self.logger.info("sent %s", subject) self.logger.info("recips: %s", ", ".join(recips)) else: self.logger.error("no email recipients for %s", group)
def send_report(self, jobs): """ Send weekly report of new translation jobs to lead translator Pass: Sequence of tuples of values """ report = self.create_report(jobs) self.logger.debug("report\n%s", report) if self.recip: recips = [self.recip] else: group = "Spanish Translation Leads" if self.test: group = "Test Translation Queue Recips" recips = Job.get_group_email_addresses(group) if recips: subject = "[%s] %s" % (cdr.Tier().name, self.title) opts = dict(subject=subject, body=report, subtype="html") message = cdr.EmailMessage(self.SENDER, recips, **opts) message.send() self.logger.info("sent %s", subject) self.logger.info("recips: %s", ", ".join(recips)) else: self.logger.error("no email recipients for %s", group)
def check(self): problems = [] for tier in self.TIERS: try: server = self.Server(tier) except Exception as e: self.logger.exception("failure checking %s", tier) problem = f"failure checking {tier}: {e}" problems.append(problem) continue for drive in sorted(server.free): free = server.free[drive] if free.bytes < self.thresholds[drive] * self.GB: amount = f"{free.bytes} bytes {free.human}" args = drive, server.name, amount problem = "{}: drive on {} server down to {}".format(*args) self.logger.warning(problem) problems.append(problem) if problems: subject = "*** WARNING: CDR DISK SPACE CHECK ***" opts = dict(subject=subject, body="\n".join(problems)) message = cdr.EmailMessage(self.FROM, self.recips, **opts) message.send() self.logger.info("sent alert to %s", ", ".join(self.recips)) else: self.logger.info("disk space OK")
def send(self, recips, subject, message): """ Send an email message, trying multiple times before giving up. We pause between attempts in order to avoid flooding the NIH mail server with too many requests at the same time. Note that the `recips` argument will always be a sequence of addresses when we are sending internal messages reporting processing to the operators, and will always be a single string when sending the notification to an individual data partner contact. This is why the checks which prevent sending out email messages to outside data partners from non-production tiers works correctly. Pass: recips - string (for partner contact) or list of strings (for ops) subject - string for message's subject header message - string for message body Raise: exception propagated by sendmail if too many attempts fail """ if isinstance(recips, str): if not cdr.isProdHost() or self.test: extra = "In live prod mode, would have gone to %r" % recips if Notify.test_recips is None: if self.recip: Notify.test_recips = [self.recip] else: group = "Test Publishing Notification" test_recips = self.get_group_email_addresses(group) Notify.test_recips = test_recips recips = Notify.test_recips self.logger.info("using recips: %r", recips) message = "<h3>%s</h3>\n%s" % (html_escape(extra), message) else: recips = [recips] tries, delay = 0, self.DELAY while True: self.logger.debug("top of send(%r)", recips) try: opts = dict(subject=subject, body=message, subtype="html") msg = cdr.EmailMessage(self.SENDER, recips, **opts) msg.send() msg = str(msg) if self.log_messages: self.logger.debug(msg) return msg except Exception as e: self.logger.exception("failure sending to %r", recips) tries += 1 if tries >= self.MAX_TRIES: self.logger.error("bailing after %d tries", self.MAX_TRIES) raise self.logger.debug("pausing %s seconds", delay) time.sleep(delay) delay += self.DELAY
def run(self): self.logger.info("started") try: self.check() except Exception as e: self.logger.exception("failure") opts = dict(subject="DISK CHECK FAILURE", body=str(e)) message = cdr.EmailMessage(self.FROM, self.recips, **opts) message.send()
def sendFailureMessage(header="*** Error ***", body=""): emailDL = cdr.getEmailList('Test Publishing Notification') subject = header if not body: body = """ The publishing job failed. Please check the log files. """ opts = dict(subject=subject, body=body) cdr.EmailMessage(cdr.OPERATOR, emailDL, **opts).send() return
def fatalError(msg): """ Log and display error message. Then exit. Pass: Error message. """ global recips, TIER # Add message to log file msg = "FATAL error: %s\n" %msg FS_LOGGER.error(msg) # Send mail to recipients from command line or registered group sender = '*****@*****.**' if not recips: try: group = "FileSweeper Error Notification" recips = CDRTask.get_group_email_addresses(group) except Exception as e: FS_LOGGER.exception("Getting email recipients from the CDR") # Message subject subject = "FileSweeper failed on %s tier" % TIER # Body errorBody = """ The CDR FileSweeper failed on %s at %s. Error message was: %s """ % (TIER, time.ctime(), msg) # Send it mailSent = False if recips: try: opts = dict(subject=subject, body=errorBody) cdr.EmailMessage(sender, recips, **opts).send() mailSent = True except Exception as e: FS_LOGGER.exception("Attempting to send mail for fatal error") if mailSent: FS_LOGGER.info("Mail sent to: %s", recips) else: FS_LOGGER.info("No mail sent") raise Exception(errorBody)
def send_report(self, control): report = self.create_report(control) control.logger.debug("report\n%s", report) if self.recip: recips = [self.recip] elif control.test: group = "Test Translation Queue Recips" recips = Job.get_group_email_addresses(group) else: recips = [self.email] if recips: subject = "[%s] %s" % (cdr.Tier().name, control.title) opts = dict(subject=subject, body=report, subtype="html") message = cdr.EmailMessage(self.SENDER, recips, **opts) message.send() control.logger.info("sent %s", subject) control.logger.info("recips: %s", ", ".join(recips)) else: control.logger.error("no email recipients for %s", group)
def send_report(self, report): """ Email the report to the right recipient list. report Serialized HTML document for the report. """ if self.recip: recips = [self.recip] else: if self.test: group = "Test Publishing Notification" else: group = "Licensee Report Notification" recips = Job.get_group_email_addresses(group) title = "PDQ Distribution Partner List" subject = "[%s] %s" % (cdr.Tier().name, title) opts = dict(subject=subject, body=report, subtype="html") message = cdr.EmailMessage(self.SENDER, recips, **opts) message.send() self.logger.info("sent %s", subject) self.logger.info("recips: %s", ", ".join(recips))
def send(self): """ Send the glossary information to registered Drupal CMS servers """ failures = [] success = "Sent glossary to server %r at %s" failure = "Failure sending glossary to server %r at %s: %s" for alias, base in self.servers.items(): url = "{}/pdq/api/glossifier/refresh".format(base) try: response = requests.post(url, json=self.data, auth=self.auth) if response.ok: self.logger.info(success, alias, base) else: args = alias, base, response.reason self.logger.error(failure, *args) failures.append(args) except Exception as e: args = alias, base, e self.logger.exception(failure, *args) failures.append(args) if failures: group = "Developers Notification" if self.recip: recips = [self.recip] else: recips = Job.get_group_email_addresses(group) if not recips: raise Exception("no recips found for glossary failure message") tier = self.tier.name subject = "[{}] Failure sending glossary information".format(tier) lines = [] for args in failures: lines.append("Server {!r} at {}: {}".format(*args)) body = "\n".join(lines) opts = dict(subject=subject, body=body) message = cdr.EmailMessage(self.SENDER, recips, **opts) self.logger.error("send failure notice sent to %r", recips)
def __sendMail(self): try: if self.__email: self.log("Sending mail to %s" % self.__email) sender = MailerJob.__CDR_EMAIL subject = "[%s] CDR Mailer Job Status" % self.__TIER.name # Specify the hostname based on the environment we're in # ------------------------------------------------------ args = cdr.APPC, "PubStatus.py", self.__id url = "https://{}/cgi-bin/cdr/{}?id={:d}".format(*args) message = """\ Job %d has completed. You can view a status report for this job at: %s %s Please do not reply to this message. """ % (self.__id, url, self.__letterLink) opts = dict(subject=subject, body=message) cdr.EmailMessage(sender, [self.__email], **opts).send() except: self.log("failure sending email to %s: %s" % (self.__email, cdr.exceptionInfo()))
def send_report(self): """ Send the report as an attachment to an email message. """ label = str(self.month) book = cdr.EmailAttachment(filepath=self.report_path) subject = self.SUBJECT % label body = ( "Attached is the monthly PDQ Distribution Partner report listing " "all documents downloaded from the SFTP server for %s.\n" % label, "The report is based on the log file provided at", " %s\n" % self.log_path, "Please save the attached report to the network directory", " L:\\OCPL\\_CROSS\\CDR\\Reports\\FTP Stats", "so the Clinical Trials team can access the information as needed.", "", "For questions or comments please reply to this email message.") body = "\n".join(body) recips = self.recips opts = dict(subject=subject, body=body, attachments=[book]) message = cdr.EmailMessage(self.SENDER, recips, **opts) message.send() self.logger.info("sent report to %s", ", ".join(recips))
# ---------------------------------------------- emailDL = sorted(cdr.getEmailList('VOL Notification')) emailDevs = sorted(cdr.getEmailList("Developers Notification")) if not len(emailDL) or testMode: recips = emailDevs else: recips = emailDL allChanges = newMediaChanges + updMediaChanges + delMediaChanges email_opts = dict(subject=subject, body=html, subtype="html") if allChanges and recips: LOGGER.info("Sending Email to DL") LOGGER.info(" DL: %s", recips) LOGGER.info("\nEmail body:") LOGGER.info("-----------------------------------------------") LOGGER.info("%s", html) LOGGER.info("-----------------------------------------------\n") cdr.EmailMessage(sender, recips, **email_opts).send() LOGGER.info("Email send successfully!") else: # Else statement included to monitor the program LOGGER.info("Email NOT submitted to DL") cdr.EmailMessage(sender, emailDevs, **email_opts).send() # All done, going home now # ------------------------ cpu = time.process_time() LOGGER.info('CPU time: %6.2f seconds', cpu) LOGGER.info('Notify_VOL - Finished') sys.exit(0)
def republish(self, addNewLinkedDocuments, docList=None, jobList=None, docType=None, docTypeAll=False, failedOnly=True, email=''): """ Requests that a set of documents be sent to Cancer.gov, avoiding the optimization which blocks sending the same version of a document twice in succession. Pass: addNewLinkedDocuments - True if the method should recursively look for and add to the job any new documents linked by any other document in the set to be re-published; otherwise False (required parameter) docList - sequence of integers each identifying with its unique document identifier a CDR document to be republished (optional parameter); can be None (the default) or an empty sequence if documents will be identified by job or document type jobList - sequence of integers each identifying a publishing job for which each of the documents were successfully exported are to be included in the new republishing job (optional parameter); can be None (the default) or an empty sequence if documents to be republished will be identified by document ID or document type docType - string identifying the document type for which all publishable (or published -- see docTypeAll parameter below) documents are to be re-published (optional parameter); can be None (the default) or an empty string if documents to be republished will be identified by document ID and/or job ID; the document type 'Protocol' is mapped to 'InScopeProtocol' docTypeAll - True if all publishable documents of the type specified by the docType parameter should be included in the re-publishing job; False or None if the job should only send documents which are currently in the pub_proc_cg table as having been sent to Cancer.gov already (optional parameter, defaulting to False); ignored if the docType parameter is not specified failedOnly - True if only documents with failure set to 'Y' in the pub_proc_doc table are to be included when collecting documents for specified publishing jobs; otherwise all documents are included for the publishing jobs specified (optional parameter, defaulting to True); ignored if no job IDs are specified email - optional string containing the address to which an email message is to be sent when the publishing job completes; also used for reporting failures if this method hits an exception Returns: integer representing the unique ID of the newly created export job An exception is raised in the event of a failure to create the new job. """ # Record the request. self.__logger.info("republish(): %d doc IDs, %d job IDs, docType: %s", docList and len(docList) or 0, jobList and len(jobList) or 0, docType or "None") # Gather the documents from the list of individual document IDs self.__docs = {} if docList: for docId in docList: # Users might accidentally try to publish individual # modules. Need to ensure we're dealing with a true # summary document # -------------------------------------------------- self.__cursor.execute( """\ SELECT 'x' FROM query_term WHERE path = '/Summary/@ModuleOnly' AND doc_id = ?""", docId) row = self.__cursor.fetchone() if row: self.__logger.error("republish(): *** Invalid document") self.__logger.error(" *** Skipping module %s", docId) continue self.__addDocumentToSet(docId) # Add to the list documents identified by previous publishing job if jobList: for jobId in jobList: self.__cursor.execute( """\ SELECT doc_id, failure FROM pub_proc_doc WHERE pub_proc = ? AND (removed IS NULL or removed = 'N')""", jobId) rows = self.__cursor.fetchall() for docId, failure in rows: if not failedOnly or failure == 'Y': self.__addDocumentToSet(docId) # Collect all documents of a specified document type if requested. if docType: # InScopeProtocol documents are know to Cancer.gov as 'Protocol'. if docType == 'Protocol': docType == 'InScopeProtocol' # Get all publishable documents of the specified document type ... if docTypeAll: self.__cursor.execute( """\ SELECT DISTINCT v.id FROM doc_version v JOIN doc_type t ON t.id = v.doc_type JOIN document d ON d.id = v.id LEFT OUTER JOIN query_term_pub q ON v.id = q.doc_id AND q.path = '/Summary/@ModuleOnly' WHERE v.publishable = 'Y' AND v.val_status = 'V' AND d.active_status = 'A' AND t.name = ? AND q.value is null ORDER BY v.id""", docType) # ... or just those already sent to Cancer.gov, as requested. else: # If selecting summaries we need to prevent summary modules # from being picked up for publishing. These documents # should not exist in pub_proc_cg but might end up being # pushed if accidentally published via a hot-fix. # --------------------------------------------------------- self.__cursor.execute( """\ SELECT a.id FROM active_doc a JOIN pub_proc_cg c ON c.id = a.id LEFT OUTER JOIN query_term_pub q ON c.id = q.doc_id AND q.path = '/Summary/@ModuleOnly' JOIN doc_type t ON t.id = a.doc_type WHERE t.name = ? AND q.value IS NULL ORDER BY a.id""", docType) rows = self.__cursor.fetchall() for row in rows: self.__addDocumentToSet(row[0]) # Sanity check. if not self.__docs: raise Exception("republish(): no documents to publish") # Record the number of documents collected directly. self.__logger.info("republish(): %d documents collected", len(self.__docs)) # If requested, include new docs linked to by the ones we will publish. if addNewLinkedDocuments: numOriginalDocs = len(self.__docs) self.__addNewLinkedDocuments() self.__logger.info( "republish(): %d new linked documents added " "to set", len(self.__docs) - numOriginalDocs) try: # Make sure we don't optimize away the push of any of these docs. self.__adjustPubProcCgTable() self.__logger.info("republish(): pub_proc_cg table adjusted") # Use the publishing job type appropriate for republishing. pubSystem = 'Primary' pubSubset = 'Republish-Export' # Create a sequence of strings in the form doc-id/version-number. docs = [str(self.__docs[docId]) for docId in self.__docs] # Create the export job, which in turn creates the follow-on push # job. parms = [] opts = dict(parms=parms, docList=docs, email=email) opts["tier"] = self.__tier resp = cdr.publish(self.__credentials, pubSystem, pubSubset, **opts) # Make sure the job creation succeeded. jobId, errors = resp if jobId: jobId = int(jobId) message = "republish(): new publishing job %d created" self.__logger.info(message, jobId) return jobId else: self.__cleanupPubProcCgTable() raise Exception("republish(): %s" % errors) # Clean up in the event of failure, including resetting the # force_push and cg_new columns back to 'N'. If we have an # email address, use it to notify the requestor of the bad news. except Exception as e: try: self.__logger.exception("republish failure") except: pass if email: try: sender = "cdr@%s" % cdrcgi.WEBSERVER subject = "Republication failure on %s" % self.__tier body = "Failure republishing CDR documents:\n%s\n" % e opts = dict(subject=subject, body=body) message = cdr.EmailMessage(sender, [email], **opts) message.send() message = "republish(): sent failure notification to %s" self.__logger.info(message, email) except: pass try: self.__cleanupPubProcCgTable() self.__logger.info("republish(): pub_proc_cg table cleaned up") except: pass raise
LOGGER.warning('*** Warning: No Email DL found') message = """\ Status and Error reports for the latest %s publishing/push jobs: Publishing Job Report: %s/cgi-bin/cdr/PubStatus.py?id=%s Push Job Report: %s/cgi-bin/cdr/PubStatus.py?id=%s """ % (addSubj.lower(), url, submit[0], url, pushId) opts = dict(subject=subject, body=message) cdr.EmailMessage(cdr.OPERATOR, emailDL, **opts).send() LOGGER.info("Submitting Email: OK") except: LOGGER.exception("*** Error sending email ***") raise except Exception as arg: LOGGER.exception("*** Standard Failure") subject = '[%s] *** SubmitPubJob.py - Standard Failure' % TIER msgBody = "The publishing job failed: %s" % arg sendFailureMessage(subject, msgBody) except: LOGGER.exception("*** Error - Program stopped with failure ***") raise sys.exit(0)
# Retrieve the Email addresses from the specified group # ----------------------------------------------------- emailDL = sorted(cdr.getEmailList('Operator Publishing Notification')) emailDev = sorted(cdr.getEmailList("Developers Notification")) # Set the variables and send the message # -------------------------------------- sender = "*****@*****.**" subject = "[%s] %s" % (cdr.Tier().name, sys.argv[1]) message = """\ Automated Publishing Email Notification: %s""" % sys.argv[2] try: # Somebody needs to get the message if the group is empty if not len(emailDL): emailDL = emailDev subject = '*** DL Missing *** %s' % subject opts = dict(subject=subject, body=message) cdr.EmailMessage(sender, emailDL, **opts).send() except: LOGGER.exception('*** Failure sending email message') raise # All done, we can go home now # ---------------------------- LOGGER.info('PubEmail Notification - Finished') sys.exit(0)