Write the html output to f.

def WriteOutput(self, feed_location, f, schedule, extension):
    """Write the html output to f."""
    if self.HasIssues():
      if self.ErrorCount() + self.WarningCount() == 1:
        summary = ('<span class="fail">Found this problem:</span>\n%s' %
                   self.CountTable())
      else:
        summary = ('<span class="fail">Found these problems:</span>\n%s' %
                   self.CountTable())
    else:
      summary = '<span class="pass">feed validated successfully</span>'

    if self.HasNotices():
      summary = ('<h3 class="issueHeader">Notices:</h3>' + 
          self.FormatType("Notice", self.ProblemListMap(TYPE_NOTICE).items()) +
          summary)

    basename = os.path.basename(feed_location)
    feed_path = (feed_location[:feed_location.rfind(basename)], basename)

    agencies = ', '.join(['<a href="%s">%s</a>' % (a.agency_url, a.agency_name)
                          for a in schedule.GetAgencyList()])
    if not agencies:
      agencies = '?'

    dates = "No valid service dates found"
    (start, end) = schedule.GetDateRange()
    if start and end:
      def FormatDate(yyyymmdd):
        src_format = "%Y%m%d"
        dst_format = "%B %d, %Y"
        try:
          return time.strftime(dst_format,
                               time.strptime(yyyymmdd, src_format))
        except ValueError:
          return yyyymmdd

      formatted_start = FormatDate(start)
      formatted_end = FormatDate(end)
      dates = "%s to %s" % (formatted_start, formatted_end)

    calendar_summary = CalendarSummary(schedule)
    if calendar_summary:
      calendar_summary_html = """<br>
During the upcoming service dates %(date_summary_range)s:
<table>
<tr><th class="header">Average trips per date:</th><td class="header">%(mean_trips)s</td></tr>
<tr><th class="header">Most trips on a date:</th><td class="header">%(max_trips)s, on %(max_trips_dates)s</td></tr>
<tr><th class="header">Least trips on a date:</th><td class="header">%(min_trips)s, on %(min_trips_dates)s</td></tr>
</table>""" % calendar_summary
    else:
      calendar_summary_html = ""

    output_prefix = """
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>FeedValidator: %(feed_file)s</title>
<style>
body {font-family: Georgia, serif; background-color: white}
.path {color: gray}
div.problem {max-width: 500px}
table.dump td,th {background-color: khaki; padding: 2px; font-family:monospace}
table.dump td.problem,th.problem {background-color: dc143c; color: white; padding: 2px; font-family:monospace}
table.count_outside td {vertical-align: top}
table.count_outside {border-spacing: 0px; }
table {border-spacing: 5px 0px; margin-top: 3px}
h3.issueHeader {padding-left: 0.5em}
h4.issueHeader {padding-left: 1em}
.pass {background-color: lightgreen}
.fail {background-color: yellow}
.notice {background-color: yellow}
.pass, .fail {font-size: 16pt}
.header {background-color: white; font-family: Georgia, serif; padding: 0px}
th.header {text-align: right; font-weight: normal; color: gray}
.footer {font-size: 10pt}
</style>
</head>
<body>
GTFS validation results for feed:<br>
<code><span class="path">%(feed_dir)s</span><b>%(feed_file)s</b></code><br>
FeedValidator extension used: %(extension)s
<br><br>
<table>
<tr><th class="header">Agencies:</th><td class="header">%(agencies)s</td></tr>
<tr><th class="header">Routes:</th><td class="header">%(routes)s</td></tr>
<tr><th class="header">Stops:</th><td class="header">%(stops)s</td></tr>
<tr><th class="header">Trips:</th><td class="header">%(trips)s</td></tr>
<tr><th class="header">Shapes:</th><td class="header">%(shapes)s</td></tr>
<tr><th class="header">Effective:</th><td class="header">%(dates)s</td></tr>
</table>
%(calendar_summary)s
<br>
%(problem_summary)s
<br><br>
""" % { "feed_file": feed_path[1],
        "feed_dir": feed_path[0],
        "agencies": agencies,
        "routes": len(schedule.GetRouteList()),
        "stops": len(schedule.GetStopList()),
        "trips": len(schedule.GetTripList()),
        "shapes": len(schedule.GetShapeList()),
        "dates": dates,
        "problem_summary": summary,
        "calendar_summary": calendar_summary_html,
        "extension": extension}

# In output_suffix string
# time.strftime() returns a regular local time string (not a Unicode one) with
# default system encoding. And decode() will then convert this time string back
# into a Unicode string. We use decode() here because we don't want the operating
# system to do any system encoding (which may cause some problem if the string
# contains some non-English characters) for the string. Therefore we decode it
# back to its original Unicode code print.

    time_unicode = (time.strftime('%B %d, %Y at %I:%M %p %Z').
                    decode(sys.getfilesystemencoding()))
    output_suffix = """
<div class="footer">
Generated by <a href="https://github.com/google/transitfeed/wiki/FeedValidator">
FeedValidator</a> version %s on %s.
</div>
</body>
</html>""" % (transitfeed.__version__, time_unicode)

    f.write(transitfeed.EncodeUnicode(output_prefix))
    if self.ProblemListMap(TYPE_ERROR):
      f.write('<h3 class="issueHeader">Errors:</h3>')
      f.write(self.FormatType("Error", self.ProblemListMap(TYPE_ERROR).items()))
    if self.ProblemListMap(TYPE_WARNING):
      f.write('<h3 class="issueHeader">Warnings:</h3>')
      f.write(self.FormatType("Warning", self.ProblemListMap(TYPE_WARNING).items()))
    f.write(transitfeed.EncodeUnicode(output_suffix))