text: # Copyright 2015 The Chromium Authors. All rights reserved.
  # Use of this source code is governed by a BSD-style license that can be
  # found in the LICENSE file.
  
  """A request handler to send alert summary emails to sheriffs on duty."""
  
  from google.appengine.api import mail
  from google.appengine.ext import ndb
  
  from dashboard import email_template
  from dashboard.common import datastore_hooks
  from dashboard.common import request_handler
  from dashboard.common import utils
  from dashboard.models import sheriff
  from dashboard.models import stoppage_alert
  
  _HTML_BODY_TEMPLATE = """
  <p>Tests that have not received data in some time:</p>
  <table>
    <tr><th>Last rev</th><th>Test</th><th>Stdio</th></tr>
    %(alert_rows)s
  </table>
  <p>It is possible that the test has been failing, or that the test was
  disabled on purpose and we should remove monitoring. It is also possible
  that the test has been renamed and the monitoring should be updated.
  <a href="%(bug_template_link)s">File a bug.</a></p>
  """
  _HTML_ALERT_ROW_TEMPLATE = """<tr>
      <td>%(rev)d</td>
      <td><a href="%(graph_link)s">%(test_path)s</a></td>
      <td><a href="%(buildbot_status_link)s"</a></td>
      <td><a href="%(stdio_link)s">stdio</a></td>
    </tr>
  """
  _TEXT_BODY_TEMPLATE = """
  Tests that have not received data in some time:
  %(alert_rows)s
  
  It is possible that the test has been failing, or that the test was
  disabled on purpose and we should remove monitoring. It is also possible
  that the test has been renamed and the monitoring should be updated.
  
  File a bug: %(bug_template_link)s
  """
  _TEXT_ALERT_ROW_TEMPLATE = """
    Last rev: %(rev)d
    Test: %(test_path)s
    Graph:%(graph_link)s
    Buildbot Status Page: %(buildbot_status_link)s
    Stdio: %(stdio_link)s
  """
  _BUG_TEMPLATE_URL = (
      'https://code.google.com/p/chromium/issues/entry'
      '?labels=Pri-1,Performance-Waterfall,'
      'Type-Bug-Regression,OS-?&comment=Tests affected:'
      '&summary=No+data+received+for+<tests>+since+<rev>'
      'cc=%s')
  
  
  class SendStoppageAlertEmailsHandler(request_handler.RequestHandler):
    """Sends emails to sheriffs about stoppage alerts in the past day.
  
    This request handler takes no parameters and is intended to be called by cron.
    """
  
    def get(self):
      """Emails sheriffs about new stoppage alerts."""
      datastore_hooks.SetPrivilegedRequest()
      sheriffs_to_email_query = sheriff.Sheriff.query(
          sheriff.Sheriff.stoppage_alert_delay > 0)
      for sheriff_entity in sheriffs_to_email_query:
        _SendStoppageAlertEmail(sheriff_entity)
  
  
  def _SendStoppageAlertEmail(sheriff_entity):
    """Sends a summary email for the given sheriff rotation.
  
    Args:
      sheriff_entity: A Sheriff key.
    """
    stoppage_alerts = _RecentStoppageAlerts(sheriff_entity)
    if not stoppage_alerts:
      return
    alert_dicts = [_AlertRowDict(a) for a in stoppage_alerts]
    mail.send_mail(
        sender='gasper-alerts@google.com',
        to=email_template.GetSheriffEmails(sheriff_entity),
        subject=_Subject(sheriff_entity, stoppage_alerts),
        body=_TextBody(alert_dicts),
        html=_HtmlBody(alert_dicts))
    for alert in stoppage_alerts:
      alert.mail_sent = True
    ndb.put_multi(stoppage_alerts)
  
  
  def _RecentStoppageAlerts(sheriff_entity):
    """Returns new StoppageAlert entities that have not had a mail sent yet."""
    return stoppage_alert.StoppageAlert.query(
        stoppage_alert.StoppageAlert.sheriff == sheriff_entity.key,
        stoppage_alert.StoppageAlert.mail_sent == False).fetch()
  
  
  def _Subject(sheriff_entity, stoppage_alerts):
    """Returns the subject line for an email about stoppage alerts.
  
    Args:
      sheriff_entity: The Sheriff who will receive the alerts.
      stoppage_alerts: A list of StoppageAlert entities.
  
    Returns:
      A string email subject line.
    """
    template = 'No data received in at least %d days for %d series.'
    return template % (sheriff_entity.stoppage_alert_delay, len(stoppage_alerts))
  
  
  def _HtmlBody(alert_dicts):
    """Returns the HTML body for an email about stoppage alerts."""
    html_alerts = '\n'.join(_HTML_ALERT_ROW_TEMPLATE % a for a in alert_dicts)
    return _HTML_BODY_TEMPLATE % {
        'alert_rows': html_alerts,
        'bug_template_link': _BUG_TEMPLATE_URL,
    }
  
  
  def _TextBody(alert_dicts):
    """Returns the text body for an email about stoppage alerts."""
    text_alerts = '\n'.join(_TEXT_ALERT_ROW_TEMPLATE % a for a in alert_dicts)
    return _TEXT_BODY_TEMPLATE % {
        'alert_rows': text_alerts,
        'bug_template_link': _BUG_TEMPLATE_URL,
    }
  
  
  def _AlertRowDict(alert):
    """Returns a dict with information to print about one stoppage alert."""
    test_path = utils.TestPath(alert.GetTestMetadataKey())
    return {
        'rev': alert.revision,
        'test_path': test_path,
        'graph_link': email_template.GetReportPageLink(test_path),
        'stdio_link': _StdioLink(alert),
        'buildbot_status_link': _BuildbotStatusLink(alert),
    }
  
  
  def _StdioLink(alert):
    stdio_uri = utils.GetStdioLinkFromRow(alert.row.get())
    if not stdio_uri:
      return None
    return utils.GetLogdogLogUriFromStdioLink(stdio_uri)
  
  
  def _BuildbotStatusLink(alert):
    stdio_uri = utils.GetStdioLinkFromRow(alert.row.get())
    if not stdio_uri:
      return None
    return utils.GetBuildbotStatusPageUriFromStdioLink(stdio_uri)
  