# 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.

import logging

import endpoints
import random
import webapp2
from apiclient import discovery
from google.appengine.ext import ndb
from oauth2client.client import GoogleCredentials
from protorpc import messages
from protorpc import message_types
from protorpc import remote

from components import auth

CONFIG_DATASTORE_KEY = "CONFIG_DATASTORE_KEY"
API_NAME = 'consoleapp'
API_VERSION = 'v1'
DISCOVERY_URL = '%s/_ah/api/discovery/v1/apis/{api}/{apiVersion}/rest'


class FieldParamsModel(ndb.Model):
  field_key = ndb.StringProperty()
  values = ndb.StringProperty(repeated=True)


class MetricModel(ndb.Model):
  name = ndb.StringProperty(default="")
  minimum = ndb.FloatProperty(default=0)
  maximum = ndb.FloatProperty(default=100)


class ParamsModel(ndb.Model):
  time = ndb.FloatProperty(default=10)
  freq = ndb.FloatProperty(default=1)
  url = ndb.StringProperty()
  params = ndb.LocalStructuredProperty(FieldParamsModel, repeated=True)
  metrics = ndb.LocalStructuredProperty(MetricModel, repeated=True)


class Field(messages.Message):
  key = messages.StringField(1)
  value = messages.StringField(2)


class Point(messages.Message):
  time = messages.FloatField(1)
  value = messages.FloatField(2)


class FieldParams(messages.Message):
  field_key = messages.StringField(1)
  values = messages.StringField(2, repeated=True)


class Metric(messages.Message):
  name = messages.StringField(1)
  minimum = messages.FloatField(2)
  maximum = messages.FloatField(3)


class Params(messages.Message):
  time = messages.FloatField(1)
  freq = messages.FloatField(2)
  url = messages.StringField(3)
  params = messages.MessageField(FieldParams, 4, repeated=True)
  metrics = messages.MessageField(Metric, 5, repeated=True)


class TimeSeries(messages.Message):
  points = messages.MessageField(Point, 1, repeated=True)
  fields = messages.MessageField(Field, 2, repeated=True)
  metric = messages.StringField(3)


class DataPacket(messages.Message):
  timeseries = messages.MessageField(TimeSeries, 1, repeated=True)


@auth.endpoints_api(name='consoleapp', version='v1')
class LoadTestApi(remote.Service):
  """A testing endpoint that receives timeseries data."""

  @auth.endpoints_method(DataPacket, message_types.VoidMessage,
                         name='timeseries.update')
  @auth.require(lambda: auth.is_group_member('metric-generators'))
  def timeseries_update(self, request):
    logging.debug('Datapacket length is %d', len(request.timeseries))
    return message_types.VoidMessage()


@auth.endpoints_api(name='ui', version='v1')
class UIApi(remote.Service):
  """API for the loadtest configuration UI."""

  @auth.endpoints_method(message_types.VoidMessage, Params,
                         name='ui.get')
  @auth.require(lambda: auth.is_group_member('metric-generators'))
  def UI_get(self, _request):
    data = ParamsModel.get_or_insert(CONFIG_DATASTORE_KEY)
    params = [FieldParams(field_key=field.field_key, values=field.values)
              for field in data.params]
    metrics = [Metric(name=metric.name,
                      minimum=metric.minimum,
                      maximum=metric.maximum)
              for metric in data.metrics]
    return Params(time=data.time, freq=data.freq, url=data.url, params=params,
                  metrics=metrics)

  @auth.endpoints_method(Params, message_types.VoidMessage,
                         name='ui.set')
  @auth.require(lambda: auth.is_group_member('metric-generators'))
  def UI_set(self, request):
    logging.debug('Got %s', request)
    data = ParamsModel.get_or_insert(CONFIG_DATASTORE_KEY)
    data.time = request.time
    data.freq = request.freq
    data.url = request.url
    data.params = [FieldParamsModel(field_key=field.field_key, 
                                    values=field.values)
                   for field in request.params]
    data.metrics = [MetricModel(name=metric.name,
                                 minimum=metric.minimum,
                                 maximum=metric.maximum)
                   for metric in request.metrics]
    data.put()
    return message_types.VoidMessage()


def field_generator(dataparams, index, fields):
  if index == len(dataparams):
    return [fields]
  else:
    key = dataparams[index].field_key
    return sum((field_generator(
      dataparams, index+1, fields+[{'key': key, 'value': value}])
                for value in dataparams[index].values), [])


class CronHandler(webapp2.RequestHandler):

  def get(self):
    data = ParamsModel.get_or_insert(CONFIG_DATASTORE_KEY)
    metric_ranges = {}
    for metric in data.metrics:
      metric_ranges[metric.name] = (metric.minimum,metric.maximum)
    datapacket = {'timeseries': []}
    logging.debug('There are %d metrics', len(metric_ranges))
    fieldlist = field_generator(data.params, 0, [])
    for metric in metric_ranges:
      for fields in fieldlist:
        points = []
        for x in xrange(0, int(data.time), int(data.freq)):
          points.append({'time': x,
                         'value': random.uniform(*metric_ranges[metric])})
        timeseries = {'points': points,
                      'fields': fields,
                      'metric': metric}
        datapacket['timeseries'].append(timeseries)
    logging.info('Send data to %s', data.url)
    discovery_url = DISCOVERY_URL % data.url
    credentials = GoogleCredentials.get_application_default()
    service = discovery.build(API_NAME, API_VERSION,
                              discoveryServiceUrl=discovery_url, 
                              credentials=credentials)
    _response = service.timeseries().update(body=datapacket).execute()


backend_handlers = [
  ('/cron', CronHandler)
]

WEBAPP = webapp2.WSGIApplication(backend_handlers, debug=True)

APPLICATION = endpoints.api_server([LoadTestApi, UIApi])

[((83, 1, 83, 52), 'components.auth.endpoints_api', 'auth.endpoints_api', (), '', False, 'from components import auth\n'), ((95, 1, 95, 44), 'components.auth.endpoints_api', 'auth.endpoints_api', (), '', False, 'from components import auth\n'), ((176, 9, 176, 62), 'webapp2.WSGIApplication', 'webapp2.WSGIApplication', (), '', False, 'import webapp2\n'), ((178, 14, 178, 56), 'endpoints.api_server', 'endpoints.api_server', ({(178, 35, 178, 55): '[LoadTestApi, UIApi]'}, {}), '([LoadTestApi, UIApi])', False, 'import endpoints\n'), ((26, 14, 26, 34), 'google.appengine.ext.ndb.StringProperty', 'ndb.StringProperty', ({}, {}), '()', False, 'from google.appengine.ext import ndb\n'), ((27, 11, 27, 44), 'google.appengine.ext.ndb.StringProperty', 'ndb.StringProperty', (), '', False, 'from google.appengine.ext import ndb\n'), ((31, 9, 31, 39), 'google.appengine.ext.ndb.StringProperty', 'ndb.StringProperty', (), '', False, 'from google.appengine.ext import ndb\n'), ((32, 12, 32, 40), 'google.appengine.ext.ndb.FloatProperty', 'ndb.FloatProperty', (), '', False, 'from google.appengine.ext import ndb\n'), ((33, 12, 33, 42), 'google.appengine.ext.ndb.FloatProperty', 'ndb.FloatProperty', (), '', False, 'from google.appengine.ext import ndb\n'), ((37, 9, 37, 38), 'google.appengine.ext.ndb.FloatProperty', 'ndb.FloatProperty', (), '', False, 'from google.appengine.ext import ndb\n'), ((38, 9, 38, 37), 'google.appengine.ext.ndb.FloatProperty', 'ndb.FloatProperty', (), '', False, 'from google.appengine.ext import ndb\n'), ((39, 8, 39, 28), 'google.appengine.ext.ndb.StringProperty', 'ndb.StringProperty', ({}, {}), '()', False, 'from google.appengine.ext import ndb\n'), ((40, 11, 40, 71), 'google.appengine.ext.ndb.LocalStructuredProperty', 'ndb.LocalStructuredProperty', (), '', False, 'from google.appengine.ext import ndb\n'), ((41, 12, 41, 67), 'google.appengine.ext.ndb.LocalStructuredProperty', 'ndb.LocalStructuredProperty', (), '', False, 'from google.appengine.ext import ndb\n'), ((45, 8, 45, 31), 'protorpc.messages.StringField', 'messages.StringField', ({(45, 29, 45, 30): '1'}, {}), '(1)', False, 'from protorpc import messages\n'), ((46, 10, 46, 33), 'protorpc.messages.StringField', 'messages.StringField', ({(46, 31, 46, 32): '2'}, {}), '(2)', False, 'from protorpc import messages\n'), ((50, 9, 50, 31), 'protorpc.messages.FloatField', 'messages.FloatField', ({(50, 29, 50, 30): '1'}, {}), '(1)', False, 'from protorpc import messages\n'), ((51, 10, 51, 32), 'protorpc.messages.FloatField', 'messages.FloatField', ({(51, 30, 51, 31): '2'}, {}), '(2)', False, 'from protorpc import messages\n'), ((55, 14, 55, 37), 'protorpc.messages.StringField', 'messages.StringField', ({(55, 35, 55, 36): '1'}, {}), '(1)', False, 'from protorpc import messages\n'), ((56, 11, 56, 49), 'protorpc.messages.StringField', 'messages.StringField', (), '', False, 'from protorpc import messages\n'), ((60, 9, 60, 32), 'protorpc.messages.StringField', 'messages.StringField', ({(60, 30, 60, 31): '1'}, {}), '(1)', False, 'from protorpc import messages\n'), ((61, 12, 61, 34), 'protorpc.messages.FloatField', 'messages.FloatField', ({(61, 32, 61, 33): '2'}, {}), '(2)', False, 'from protorpc import messages\n'), ((62, 12, 62, 34), 'protorpc.messages.FloatField', 'messages.FloatField', ({(62, 32, 62, 33): '3'}, {}), '(3)', False, 'from protorpc import messages\n'), ((66, 9, 66, 31), 'protorpc.messages.FloatField', 'messages.FloatField', ({(66, 29, 66, 30): '1'}, {}), '(1)', False, 'from protorpc import messages\n'), ((67, 9, 67, 31), 'protorpc.messages.FloatField', 'messages.FloatField', ({(67, 29, 67, 30): '2'}, {}), '(2)', False, 'from protorpc import messages\n'), ((68, 8, 68, 31), 'protorpc.messages.StringField', 'messages.StringField', ({(68, 29, 68, 30): '3'}, {}), '(3)', False, 'from protorpc import messages\n'), ((69, 11, 69, 63), 'protorpc.messages.MessageField', 'messages.MessageField', (), '', False, 'from protorpc import messages\n'), ((70, 12, 70, 59), 'protorpc.messages.MessageField', 'messages.MessageField', (), '', False, 'from protorpc import messages\n'), ((74, 11, 74, 57), 'protorpc.messages.MessageField', 'messages.MessageField', (), '', False, 'from protorpc import messages\n'), ((75, 11, 75, 57), 'protorpc.messages.MessageField', 'messages.MessageField', (), '', False, 'from protorpc import messages\n'), ((76, 11, 76, 34), 'protorpc.messages.StringField', 'messages.StringField', ({(76, 32, 76, 33): '3'}, {}), '(3)', False, 'from protorpc import messages\n'), ((80, 15, 80, 66), 'protorpc.messages.MessageField', 'messages.MessageField', (), '', False, 'from protorpc import messages\n'), ((87, 3, 88, 50), 'components.auth.endpoints_method', 'auth.endpoints_method', (), '', False, 'from components import auth\n'), ((99, 3, 100, 39), 'components.auth.endpoints_method', 'auth.endpoints_method', (), '', False, 'from components import auth\n'), ((113, 3, 114, 39), 'components.auth.endpoints_method', 'auth.endpoints_method', (), '', False, 'from components import auth\n'), ((92, 11, 92, 38), 'protorpc.message_types.VoidMessage', 'message_types.VoidMessage', ({}, {}), '()', False, 'from protorpc import message_types\n'), ((117, 4, 117, 36), 'logging.debug', 'logging.debug', ({(117, 18, 117, 26): '"""Got %s"""', (117, 28, 117, 35): 'request'}, {}), "('Got %s', request)", False, 'import logging\n'), ((130, 11, 130, 38), 'protorpc.message_types.VoidMessage', 'message_types.VoidMessage', ({}, {}), '()', False, 'from protorpc import message_types\n'), ((163, 4, 163, 45), 'logging.info', 'logging.info', ({(163, 17, 163, 34): '"""Send data to %s"""', (163, 36, 163, 44): 'data.url'}, {}), "('Send data to %s', data.url)", False, 'import logging\n'), ((165, 18, 165, 61), 'oauth2client.client.GoogleCredentials.get_application_default', 'GoogleCredentials.get_application_default', ({}, {}), '()', False, 'from oauth2client.client import GoogleCredentials\n'), ((166, 14, 168, 54), 'apiclient.discovery.build', 'discovery.build', (), '', False, 'from apiclient import discovery\n'), ((89, 24, 89, 65), 'components.auth.is_group_member', 'auth.is_group_member', ({(89, 45, 89, 64): '"""metric-generators"""'}, {}), "('metric-generators')", False, 'from components import auth\n'), ((101, 24, 101, 65), 'components.auth.is_group_member', 'auth.is_group_member', ({(101, 45, 101, 64): '"""metric-generators"""'}, {}), "('metric-generators')", False, 'from components import auth\n'), ((115, 24, 115, 65), 'components.auth.is_group_member', 'auth.is_group_member', ({(115, 45, 115, 64): '"""metric-generators"""'}, {}), "('metric-generators')", False, 'from components import auth\n'), ((158, 34, 158, 72), 'random.uniform', 'random.uniform', ({(158, 49, 158, 71): '*metric_ranges[metric]'}, {}), '(*metric_ranges[metric])', False, 'import random\n')]