[Metrics] Fix box drawing on Windows, and refactor code.

The Unicode box drawing turn out malformed on Windows (even with
fix_encoding.py). This CL refactors the box drawing code, and makes
Windows box drawing use regular ASCII characters. Details:
* Add print_red_boxed_text() to programatically print box aroun given
  list og strings.
  * Unicode box drawing characters are escaped, removing the use of
    non-ASCII characters in source code.
* Convert message source to generators for greater flexibilty, and
  remove the need for manual horizontal padding.
* Add proper use colorama with Style.BRIGNT and Style.RESET_ALL.
* For Windows, revert to ASCII characters for box drawing.

Change-Id: I71a728db480709b4376d538aaecd701f9197e7ac
Reviewed-on: https://chromium-review.googlesource.com/c/1461249
Commit-Queue: Samuel Huang <huangs@chromium.org>
Reviewed-by: Edward Lesmes <ehmaldonado@chromium.org>
diff --git a/metrics_utils.py b/metrics_utils.py
index 854179e..dc53726 100644
--- a/metrics_utils.py
+++ b/metrics_utils.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-# -*- coding: utf-8 -*-
 # Copyright (c) 2018 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.
@@ -22,43 +21,35 @@
 
 APP_URL = 'https://cit-cli-metrics.appspot.com'
 
-EMPTY_LINE = (
-  '┃                                                   ┃'
-)
-NOTICE_COUNTDOWN_HEADER = (
-  '┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n'
-  '┃  METRICS COLLECTION WILL START IN %2d EXECUTIONS   ┃'
-)
-NOTICE_COLLECTION_HEADER = (
-  '┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n'
-  '┃      METRICS COLLECTION IS TAKING PLACE           ┃'
-)
-NOTICE_VERSION_CHANGE_HEADER = (
-  '┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n'
-  '┃       WE ARE COLLECTING ADDITIONAL METRICS        ┃\n'
-  '┃                                                   ┃\n'
-  '┃ Please review the changes and opt-in again.       ┃'
-)
-NOTICE_FOOTER = (
-  '┃ To suppress this message opt in or out using:     ┃\n'
-  '┃ $ gclient metrics [--opt-in] [--opt-out]          ┃\n'
-  '┃ For more information please see metrics.README.md ┃\n'
-  '┃ in your depot_tools checkout or visit             ┃\n'
-  '┃ https://goo.gl/yNpRDV.                            ┃\n'
-  '┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n'
-)
+def get_notice_countdown_header(countdown):
+  if countdown == 0:
+    yield '     METRICS COLLECTION IS TAKING PLACE'
+  else:
+    yield '  METRICS COLLECTION WILL START IN %d EXECUTIONS' % countdown
 
-CHANGE_NOTICE = {
-  # No changes for version 0
-  0: '',
-  1: ('┃ We want to collect the Git version.               ┃\n'
-      '┃ We want to collect information about the HTTP     ┃\n'
-      '┃ requests that depot_tools makes, and the git and  ┃\n'
-      '┃ cipd commands it executes.                        ┃\n'
-      '┃                                                   ┃\n'
-      '┃ We only collect known strings to make sure we     ┃\n'
-      '┃ don\'t record PII.                                 ┃')
-}
+def get_notice_version_change_header():
+  yield '       WE ARE COLLECTING ADDITIONAL METRICS'
+  yield ''
+  yield ' Please review the changes and opt-in again.'
+
+def get_notice_footer():
+  yield 'To suppress this message opt in or out using:'
+  yield '$ gclient metrics [--opt-in] [--opt-out]'
+  yield 'For more information please see metrics.README.md'
+  yield 'in your depot_tools checkout or visit'
+  yield 'https://goo.gl/yNpRDV.'
+
+def get_change_notice(version):
+  if version == 0:
+    pass # No changes for version 0
+  elif version == 1:
+    yield 'We want to collect the Git version.'
+    yield 'We want to collect information about the HTTP'
+    yield 'requests that depot_tools makes, and the git and'
+    yield 'cipd commands it executes.'
+    yield ''
+    yield 'We only collect known strings to make sure we'
+    yield 'don\'t record PII.'
 
 
 KNOWN_PROJECT_URLS = {
@@ -286,25 +277,31 @@
   # Get the age of the checkout in weeks.
   return seconds_to_weeks(stdout.strip())
 
+def print_red_boxed_text(out, min_width, lines):
+  colorama.init()
+  if sys.platform == 'win32':
+    [EW, NS, SE, SW, NE, NW] = list('=|++++')
+  else:
+    [EW, NS, SE, SW, NE, NW] = list(u'\u2501\u2503\u250F\u2513\u2517\u251B')
+  out(colorama.Fore.RED + colorama.Style.BRIGHT)
+  width = max(min_width, max(len(line) for line in lines))
+  out(SE + EW * (width + 2) + SW + '\n')
+  for line in lines:
+     out('%s %-*s %s\n' % (NS, width, line, NS))
+  out(NE + EW * (width + 2) + NW + '\n')
+  out(colorama.Style.RESET_ALL)
 
 def print_notice(countdown):
   """Print a notice to let the user know the status of metrics collection."""
-  colorama.init()
-  print(colorama.Fore.RED + '\033[1m', file=sys.stderr, end='')
-  if countdown:
-    print(NOTICE_COUNTDOWN_HEADER % countdown, file=sys.stderr)
-  else:
-    print(NOTICE_COLLECTION_HEADER, file=sys.stderr)
-  print(EMPTY_LINE, file=sys.stderr)
-  print(NOTICE_FOOTER + colorama.Style.RESET_ALL, file=sys.stderr)
-
+  lines = list(get_notice_countdown_header(countdown))
+  lines.append('')
+  lines += list(get_notice_footer())
+  print_red_boxed_text(sys.stderr.write, 49, lines)
 
 def print_version_change(config_version):
   """Print a notice to let the user know we are collecting more metrics."""
-  colorama.init()
-  print(colorama.Fore.RED + '\033[1m', file=sys.stderr, end='')
-  print(NOTICE_VERSION_CHANGE_HEADER, file=sys.stderr)
-  print(EMPTY_LINE, file=sys.stderr)
-  for version in range(config_version + 1, CURRENT_VERSION + 1):
-    print(CHANGE_NOTICE[version], file=sys.stderr)
-    print(EMPTY_LINE, file=sys.stderr)
+  lines = list(get_notice_version_change_header())
+  for version in xrange(config_version + 1, CURRENT_VERSION + 1):
+    lines.append('')
+    lines += list(get_change_notice(version))
+  print_red_boxed_text(sys.stderr.write, 49, lines)