text: """Basic views for ox_profile flask UI.
  """
  
  import urllib.parse
  import io
  import csv
  import collections
  import datetime
  import logging
  from functools import wraps
  
  from flask import (
      request, Markup, current_app, render_template, url_for, g, make_response)
  from flask_login import login_required
  from flask_login import current_user
  
  from ox_profile.ui.flask import OX_PROF_BP, ReqRecord
  
  RouteInfo = collections.namedtuple('RouteInfo', [
      'name', 'hits', 'avg_time'])
  
  def access_problem_p():
      """If user is allowed access to ox_profile, return None else return error.
  
      This is meant to be called when a user tries to access ox_profile routes
      so that non-privledged users cannot cause trouble. It looks in
      current_app.config['OX_PROF_USERS'] for a dictionary or set of allowed
      users.
      """
      allowed = current_app.config.get('OX_PROF_USERS', {})
      name_field = current_app.config.get(
          'OX_PROF_USERNAME_FIELD', 'name')
      effective_username = getattr(current_user, name_field)
      if effective_username in allowed:
          return None
  
      msg = 'Current user.%s = %s not in the %i allowed ox profile users.%s' % (
          effective_username, name_field, len(allowed),
          '\nConfigure app.config["OX_PROF_USERS"] if necessary.')
      return render_template('ox_prof_err.html', error_msg=msg)
  
  
  def restrict_access(my_func):
      """Simple decorator to call access_problem_p before execution.
      """
      @wraps(my_func)
      def wrapper(*args, **kwds):
          """Check access_problem_p and then execute wrapped function.
          """
  
          problem = access_problem_p()
          if problem is None:
              return my_func(*args, **kwds)
          return problem
  
      return wrapper
  
  
  @OX_PROF_BP.route('/')
  @OX_PROF_BP.route('/index')
  @login_required
  @restrict_access
  def index():
      """Top-level index for ox profile.
      """
      commands = [(n, url_for('%s.%s' % ('ox_profile', n))) for n in [
          'status', 'pause', 'unpause', 'show_req_times']]
  
      return render_template('ox_prof_intro.html', commands=commands)
  
  
  @OX_PROF_BP.route('/status')
  @login_required
  @restrict_access
  def status():
      """Show status of current profiling.
      """
      re_filter = request.args.get('re_filter', '.*')
      max_records = int(request.args.get('max_records', 50))
  
      query, total_records = OX_PROF_BP.launcher.sampler.my_db.query(
          re_filter=re_filter, max_records=max_records)
  
      return render_template(
          'ox_prof_status.html', launcher=OX_PROF_BP.launcher,
          max_records=max_records, total_records=total_records, query=query)
  
  
  @OX_PROF_BP.route('/pause')
  @login_required
  @restrict_access
  def pause():
      """Pause the profiler.
      """
      OX_PROF_BP.launcher.pause()
      return render_template('ox_prof_msg.html', message='paused')
  
  
  @OX_PROF_BP.route('/unpause')
  @login_required
  @restrict_access
  def unpause():
      """Unpause profiler or start it for the first time.
      """
      launcher = OX_PROF_BP.launcher
      msgs = []
      if not launcher.is_alive():
          msgs.append('Started thread for first time')
          launcher.start()
      launcher.unpause()
      msgs.append('unpaused')
      return render_template('ox_prof_msg.html', message=Markup(
          '\n<BR>\n'.join(msgs)))
  
  
  @OX_PROF_BP.route('/set_interval')
  @login_required
  @restrict_access
  def set_interval():
      "Set the sampling interval"
      interval = request.args.get('interval', None)
      problem = None
      try:
          fint = float(interval)
          if fint < 0:
              raise ValueError('Negative interval of %s illogical.')
          if fint > 10:
              raise ValueError('Interval > 10 is useless.')
      except ValueError as problem:
          logging.debug('Could not convert interval %s to float because %s',
                        interval, problem)
      if problem:
          return render_template('ox_prof_err.html', error_msg=problem)
      OX_PROF_BP.launcher.set_interval(fint)
  
      return render_template('ox_prof_msg.html', message=(
          'Changed sampling interval to %f' % fint))
  
  
  @OX_PROF_BP.before_app_request
  def monitor_routes():
      """Simple function to record start time of request.
  
      See monitor_route_completion for more info.
      """
      g.ox_prof_ts = datetime.datetime.utcnow()
  
  
  @OX_PROF_BP.teardown_app_request
  def monitor_route_completion(exception=None):
      """Monitor and record route completion.
  
      :param exception=None:    Provided by flask if exception happened.
  
      ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
  
      PURPOSE:   Record a few things in OX_PROF_BP about the route that
                 just completed for profiling information.
  
      """
      try:
          user = getattr(current_user, 'name', None)
          if user is None:
              user = getattr(current_user, 'username', 'unknown')
          OX_PROF_BP.record_req(user, request.endpoint, g.ox_prof_ts,
                                datetime.datetime.utcnow())
      except Exception as problem:  # pylint:disable=broad-except
          logging.error('Got problem %s in monitor_route_completion', str(
              problem))
  
  
  @OX_PROF_BP.route('/show_req_times')
  @login_required
  @restrict_access
  def show_req_times():
      """Show statistical summary of request times for each route.
      """
      reqs = OX_PROF_BP.get_reqs()
      if int(request.args.get('as_csv', 0)):
          return _make_csv_response('reqs.csv', reqs)
  
      user_times = {}
      func_times = {}
      for (user, endpoint), time_list in reqs.items():
          runtimes = [(e - s).total_seconds() for s, e in time_list]
          fdata = func_times.get(endpoint, [])
          if not fdata:
              func_times[endpoint] = fdata
          fdata.extend(runtimes)
          udata = user_times.get(user, [])
          if not udata:
              user_times[endpoint] = udata
          udata.extend(runtimes)
      rinfo = [RouteInfo(name, len(data), sum(data)/float(len(data))) for (
          name, data) in func_times.items()]
  
      result = render_template('ox_prof_rinfo.html', rinfo=reversed(sorted(
          rinfo, key=lambda r: r.avg_time)), csv_link=Markup(make_download_link(
              request, {'as_csv': 1})))
  
      return result
  
  
  def _make_csv_response(name, data):
      if isinstance(data, str):
          text = data
      elif isinstance(data, dict):
          csv_data = io.StringIO()
          writer = csv.writer(csv_data)
          writer.writerow(['user', 'endpoint'] + list(ReqRecord._fields))
          for (user, endpoint), req_data in data.items():
              writer.writerows([[user, endpoint] + list(i) for i in req_data])
          del writer
          csv_data.seek(0)
          text = csv_data.read()
      else:
          raise TypeError('Cannot convert type %s to CSV.' % type(data))
  
      response = make_response(text)
      response.headers["Content-Disposition"] = (
          "attachment; filename=%s" % name)
      return response
  
  
  def make_download_link(my_request, updates, text='Download CSV'):
      """Helper function to make a download link to give request.
  
      :arg my_request:        A request object we are linking to.
  
      :arg updates:           A dict of parameters to override in my_request.
  
      :arg text='DownloadCSV':   Text string of download link.
  
      ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
  
      :returns:   String representing HTML download link to request.
  
      ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
  
      PURPOSE:    Make a simple download link to a request.
  
      """
      url_params = my_request.args.to_dict()
      url_params.update(updates)
      return '<A HREF="%s/%s?%s">%s</A>' % (
          my_request.url_root.rstrip('/'), my_request.path.lstrip('/'),
          urllib.parse.urlencode(url_params), text)
  