<commit_before>"""Computes the least-squares optimal Keplerian elements for a sequence of
   cartesian position observations.
"""

import math
#import numpy as np
#Thinly-wrapped numpy
import autograd.numpy as np
import matplotlib.pyplot as plt
from autograd import grad
from autograd import jacobian
from autograd import elementwise_grad as egrad

# convention:
# a: semi-major axis
# e: eccentricity
# eps: mean longitude at epoch
# taup: time of pericenter passage
# Euler angles:
# I: inclination
# Omega: longitude of ascending node
# omega: argument of pericenter

#rotation about the z-axis about an angle `ang`
def rotz(ang):
    cos_ang = np.cos(ang)
    sin_ang = np.sin(ang)
    return np.array(((cos_ang,-sin_ang,0.0), (sin_ang, cos_ang,0.0), (0.0,0.0,1.0)))

#rotation about the x-axis about an angle `ang`
def rotx(ang):
    cos_ang = np.cos(ang)
    sin_ang = np.sin(ang)
    return np.array(((1.0,0.0,0.0), (0.0,cos_ang,-sin_ang), (0.0,sin_ang,cos_ang)))

#rotation from the orbital plane to the inertial frame
#it is composed of the following rotations, in that order:
#1) rotation about the z axis about an angle `omega` (argument of pericenter)
#2) rotation about the x axis about an angle `I` (inclination)
#3) rotation about the z axis about an angle `Omega` (longitude of ascending node)
def orbplane2frame_(omega,I,Omega):
    P2_mul_P3 = np.matmul(rotx(I),rotz(omega))
    return np.matmul(rotz(Omega),P2_mul_P3)

def orbplane2frame(x):
    return orbplane2frame_(x[0],x[1],x[2])

# get Keplerian range
def kep_r_(a, e, f):
    return a*(1.0-e**2)/(1.0+e*np.cos(f))

def kep_r(x):
    return kep_r_(x[0],x[1],x[2])

# get cartesian positions wrt orbital plane
def xyz_orbplane_(a, e, f):
    r = kep_r_(a, e, f)
    return np.array((r*np.cos(f),r*np.sin(f),0.0))

def xyz_orbplane(x):
    return xyz_orbplane_(x[0],x[1],x[2])

# get cartesian positions wrt inertial frame from orbital elements
def xyz_frame_(a,e,f,omega,I,Omega):
    return np.matmul( orbplane2frame_(omega,I,Omega) , xyz_orbplane_(a, e, f) )

def xyz_frame(x):
    return np.matmul( orbplane2frame(x[3:6]) , xyz_orbplane(x[0:3]) )

# get mean motion from mass parameter (mu) and semimajor axis (a)
def meanmotion(mu,a):
    return np.sqrt(mu/(a**3))

# get mean anomaly from mean motion (n), time (t) and time of pericenter passage (taup)
def meananomaly(n, t, taup):
    return n*(t-taup)

# compute eccentric anomaly (E) from eccentricity (e) and mean anomaly (M)
def eccentricanomaly(e,M):
    M0 = np.mod(M,2*np.pi)
    E0 = M0 + np.sign(np.sin(M0))*0.85*e #Murray-Dermotts' initial estimate
    # successive approximations via Newtons' method
    for i in range(0,4):
        #TODO: implement modified Newton's method for Kepler's equation (Murray-Dermott)
        Eans = E0 - (E0-e*np.sin(E0)-M0)/(1.0-e*np.cos(E0))
        E0 = Eans
    return E0

#compute true anomaly (f) from eccentricity (e) and eccentric anomaly (E)
def trueanomaly(e,E):
    Enew = np.mod(E,2*np.pi)
    return 2*np.arctan(  np.sqrt((1.0+e)/(1.0-e))*np.tan(Enew/2)  )

# # TODO:
# # write function to compute range as a function of orbital elements: DONE
# # write function to compute true anomaly as a function of time-of-fly
# # the following transformation is needed: from time t, to mean anomaly M,
# # to eccentric anomaly E, to true anomaly f
# # t -> M=n*(t-taup) -> M=E-e*sin(E) (invert) ->
# # -> f = 2*atan(  sqrt((1+e)/(1-e))*tan(E/2)  )
# # write function which takes observed values and computes the difference wrt expected-to-be-observed values as a function of unknown orbital elements (to be fitted)
# # compute Q as a function of unknown orbital elements (to be fitted)
# # optimize Q -> return fitted orbital elements (requires an ansatz: take input from minimalistic Gibb's?)

# NOTES:
# matrix multiplication of numpy's 2-D arrays is done through `np.matmul`

omega = np.radians(31.124)
I = np.radians(75.0)
Omega = np.radians(60.0)

# rotation matrix from orbital plane to inertial frame
# two ways to compute it; result should be the same
P_1 = rotz(omega) #rotation about z axis by an angle `omega`
P_2 = rotx(I) #rotation about x axis by an angle `I`
P_3 = rotz(Omega) #rotation about z axis by an angle `Omega`

Rot1 = np.matmul(P_3,np.matmul(P_2,P_1))
Rot2 = orbplane2frame_(omega,I,Omega)

v = np.array((3.0,-2.0,1.0))

print(I)
print(omega)
print(Omega)

print(Rot1)

print(np.matmul(Rot1,v))

print(Rot2)

# #import autograd.numpy as np  # Thinly-wrapped numpy
# #from autograd import grad    # The only autograd function you may ever need
# def tanh(x):                 # Define a function
#     y = np.exp(-2.0 * x)
#     return (1.0 - y) / (1.0 + y)

# grad_tanh = grad(tanh)       # Obtain its gradient function
# print(grad_tanh(1.0))

# def mycos(x):
#     return np.cos(x)

# drotz = grad(mycos)

# print(drotz(0.1))

drotz = jacobian(orbplane2frame_)
drotz2 = jacobian(orbplane2frame)

print('op2f=',orbplane2frame_(np.radians(10.0),np.radians(-31.124),np.radians(200.001)))

# print('drotz=',drotz(np.radians(10.0),np.radians(-31.124),np.radians(200.001)))

bbb= np.array( (np.radians(10.0),np.radians(-31.124),np.radians(200.001)) )
#bbb= np.array( (np.radians(0.0),np.radians(0.0),np.radians(0.0)) )
print('drotz2(bbb)=',drotz2( bbb ))

#print(dkep_r)
#print(kep_r_(1.0,0.1,np.radians(-29.99)))
#print(dkep_r(1.0,0.1,np.radians(-29.99)))

aaa = np.array((1.0,0.45,np.radians(1.0)))
# def printz(x):
#     return print(str(x)+' = ',x)
# printz(aaa)

print('aaa = ', aaa)
print('kep_r(aaa) = ',kep_r(aaa))

dkep_r = grad(kep_r)

print('dkep_r(aaa) = ',dkep_r(aaa))

print('xyz_orbplane(aaa) = ',xyz_orbplane(aaa))

dxyz_orbplane = jacobian(xyz_orbplane)

print('dxyz_orbplane(aaa) = ',dxyz_orbplane(aaa))

dxyz_orbplane_ = jacobian(xyz_orbplane_)

print('dxyz_orbplane_(aaa) = ',dxyz_orbplane_(aaa[0],aaa[1],aaa[2]))

ccc = np.array((aaa[0],aaa[1],aaa[2],bbb[0],bbb[1],bbb[2]))

print('xyz_frame(ccc) = ',xyz_frame(ccc))

print('xyz_frame_((aaa[0],aaa[1],aaa[2],bbb[0],bbb[1],bbb[2])) = ',xyz_frame((aaa[0],aaa[1],aaa[2],bbb[0],bbb[1],bbb[2])))

dxyz_frame = jacobian(xyz_frame)

print('dxyz_frame(ccc) = ',dxyz_frame(ccc))

print('meanmotion(1.0,1.1) = ', meanmotion(1.0,1.1))

mye = 0.3134123
myM = -21.98*np.pi
myEans = eccentricanomaly(mye,myM)
print('myEans-mye*np.sin(myEans)-np.mod(myM,2*np.pi) = ', myEans-mye*np.sin(myEans)-np.mod(myM,2*np.pi))

print('eccentricanomaly(mye,myM) = ', eccentricanomaly(mye,myM))

print('trueanomaly(0.1, 0.16*np.pi) = ', trueanomaly(0.1, 0.16*np.pi)/np.pi, '*pi')
print('trueanomaly(0.1, 0.56*np.pi) = ', trueanomaly(0.1, 0.56*np.pi)/np.pi, '*pi')
print('trueanomaly(0.1, 1.16*np.pi) = ', trueanomaly(0.1, 1.16*np.pi)/np.pi, '*pi')
print('trueanomaly(0.1, 1.56*np.pi) = ', trueanomaly(0.1, 1.56*np.pi)/np.pi, '*pi')
print('trueanomaly(0.1, 1.99*np.pi) = ', trueanomaly(0.1, 1.99*np.pi)/np.pi, '*pi')

<commit_msg>Add conversion method from cartesian positions at time t to orbital elements<commit_after>"""Computes the least-squares optimal Keplerian elements for a sequence of
   cartesian position observations.
"""

import math
#import numpy as np
#Thinly-wrapped numpy
import autograd.numpy as np
import matplotlib.pyplot as plt
from autograd import grad
from autograd import jacobian
from autograd import elementwise_grad as egrad

# convention:
# a: semi-major axis
# e: eccentricity
# eps: mean longitude at epoch
# taup: time of pericenter passage
# Euler angles:
# I: inclination
# Omega: longitude of ascending node
# omega: argument of pericenter

#rotation about the z-axis about an angle `ang`
def rotz(ang):
    cos_ang = np.cos(ang)
    sin_ang = np.sin(ang)
    return np.array(((cos_ang,-sin_ang,0.0), (sin_ang, cos_ang,0.0), (0.0,0.0,1.0)))

#rotation about the x-axis about an angle `ang`
def rotx(ang):
    cos_ang = np.cos(ang)
    sin_ang = np.sin(ang)
    return np.array(((1.0,0.0,0.0), (0.0,cos_ang,-sin_ang), (0.0,sin_ang,cos_ang)))

#rotation from the orbital plane to the inertial frame
#it is composed of the following rotations, in that order:
#1) rotation about the z axis about an angle `omega` (argument of pericenter)
#2) rotation about the x axis about an angle `I` (inclination)
#3) rotation about the z axis about an angle `Omega` (longitude of ascending node)
def orbplane2frame_(omega,I,Omega):
    P2_mul_P3 = np.matmul(rotx(I),rotz(omega))
    return np.matmul(rotz(Omega),P2_mul_P3)

def orbplane2frame(x):
    return orbplane2frame_(x[0],x[1],x[2])

# get Keplerian range
def kep_r_(a, e, f):
    return a*(1.0-e**2)/(1.0+e*np.cos(f))

def kep_r(x):
    return kep_r_(x[0],x[1],x[2])

# get cartesian positions wrt orbital plane
def xyz_orbplane_(a, e, f):
    r = kep_r_(a, e, f)
    return np.array((r*np.cos(f),r*np.sin(f),0.0))

def xyz_orbplane(x):
    return xyz_orbplane_(x[0],x[1],x[2])

# get cartesian positions wrt inertial frame from orbital elements
def xyz_frame_(a,e,f,omega,I,Omega):
    return np.matmul( orbplane2frame_(omega,I,Omega) , xyz_orbplane_(a, e, f) )

def xyz_frame(x):
    return np.matmul( orbplane2frame(x[3:6]) , xyz_orbplane(x[0:3]) )

# get mean motion from mass parameter (mu) and semimajor axis (a)
def meanmotion(mu,a):
    return np.sqrt(mu/(a**3))

# get mean anomaly from mean motion (n), time (t) and time of pericenter passage (taup)
def meananomaly(n, t, taup):
    return n*(t-taup)

# compute eccentric anomaly (E) from eccentricity (e) and mean anomaly (M)
def eccentricanomaly(e,M):
    M0 = np.mod(M,2*np.pi)
    E0 = M0 + np.sign(np.sin(M0))*0.85*e #Murray-Dermotts' initial estimate
    # successive approximations via Newtons' method
    for i in range(0,4):
        #TODO: implement modified Newton's method for Kepler's equation (Murray-Dermott)
        Eans = E0 - (E0-e*np.sin(E0)-M0)/(1.0-e*np.cos(E0))
        E0 = Eans
    return E0

# compute true anomaly (f) from eccentricity (e) and eccentric anomaly (E)
def trueanomaly(e,E):
    Enew = np.mod(E,2*np.pi)
    return 2*np.arctan(  np.sqrt((1.0+e)/(1.0-e))*np.tan(Enew/2)  )

# compute true anomaly from eccentricity and mean anomaly
def meanan2truean(e,M):
    return trueanomaly(e, eccentricanomaly(e, M))

# compute true anomaly from time, a, e, mu and taup
def time2truean(a, e, mu, t, taup):
    return meanan2truean(e, meananomaly(meanmotion(mu, a), t, taup))

# compute cartesian positions (x,y,z) at time t
# for mass parameter mu from orbital elements a, e, taup, I, omega, Omega
def orbel2xyz(t, mu, a, e, taup, omega, I, Omega):

    # compute true anomaly at time t
    f = time2truean(a, e, mu, t, taup)
    # get cartesian positions wrt inertial frame from orbital elements
    return xyz_frame_(a, e, f, omega, I, Omega)

# # TODO:
# # write function to compute range as a function of orbital elements: DONE
# # write function to compute true anomaly as a function of time-of-fly
# # the following transformation is needed: from time t, to mean anomaly M,
# # to eccentric anomaly E, to true anomaly f
# # t -> M=n*(t-taup) -> M=E-e*sin(E) (invert) ->
# # -> f = 2*atan(  sqrt((1+e)/(1-e))*tan(E/2)  )
# # write function which takes observed values and computes the difference wrt expected-to-be-observed values as a function of unknown orbital elements (to be fitted)
# # compute Q as a function of unknown orbital elements (to be fitted)
# # optimize Q -> return fitted orbital elements (requires an ansatz: take input from minimalistic Gibb's?)

# NOTES:
# matrix multiplication of numpy's 2-D arrays is done through `np.matmul`

omega = np.radians(31.124)
I = np.radians(75.0)
Omega = np.radians(60.0)

# rotation matrix from orbital plane to inertial frame
# two ways to compute it; result should be the same
P_1 = rotz(omega) #rotation about z axis by an angle `omega`
P_2 = rotx(I) #rotation about x axis by an angle `I`
P_3 = rotz(Omega) #rotation about z axis by an angle `Omega`

Rot1 = np.matmul(P_3,np.matmul(P_2,P_1))
Rot2 = orbplane2frame_(omega,I,Omega)

v = np.array((3.0,-2.0,1.0))

print(I)
print(omega)
print(Omega)

print(Rot1)

print(np.matmul(Rot1,v))

print(Rot2)

# #import autograd.numpy as np  # Thinly-wrapped numpy
# #from autograd import grad    # The only autograd function you may ever need
# def tanh(x):                 # Define a function
#     y = np.exp(-2.0 * x)
#     return (1.0 - y) / (1.0 + y)

# grad_tanh = grad(tanh)       # Obtain its gradient function
# print(grad_tanh(1.0))

# def mycos(x):
#     return np.cos(x)

# drotz = grad(mycos)

# print(drotz(0.1))

drotz = jacobian(orbplane2frame_)
drotz2 = jacobian(orbplane2frame)

print('op2f=',orbplane2frame_(np.radians(10.0),np.radians(-31.124),np.radians(200.001)))

# print('drotz=',drotz(np.radians(10.0),np.radians(-31.124),np.radians(200.001)))

bbb = np.array( (np.radians(10.0),np.radians(-31.124),np.radians(200.001)) )
#bbb= np.array( (np.radians(0.0),np.radians(0.0),np.radians(0.0)) )
print('drotz2(bbb)=',drotz2( bbb ))

#print(dkep_r)
#print(kep_r_(1.0,0.1,np.radians(-29.99)))
#print(dkep_r(1.0,0.1,np.radians(-29.99)))

aaa = np.array((1.0,0.45,np.radians(1.0)))
# def printz(x):
#     return print(str(x)+' = ',x)
# printz(aaa)

print('aaa = ', aaa)
print('kep_r(aaa) = ',kep_r(aaa))

dkep_r = grad(kep_r)

print('dkep_r(aaa) = ',dkep_r(aaa))

print('xyz_orbplane(aaa) = ',xyz_orbplane(aaa))

dxyz_orbplane = jacobian(xyz_orbplane)

print('dxyz_orbplane(aaa) = ',dxyz_orbplane(aaa))

dxyz_orbplane_ = jacobian(xyz_orbplane_)

print('dxyz_orbplane_(aaa) = ',dxyz_orbplane_(aaa[0],aaa[1],aaa[2]))

ccc = np.array((aaa[0],aaa[1],aaa[2],bbb[0],bbb[1],bbb[2]))

print('xyz_frame(ccc) = ',xyz_frame(ccc))

print('xyz_frame_((aaa[0],aaa[1],aaa[2],bbb[0],bbb[1],bbb[2])) = ',xyz_frame((aaa[0],aaa[1],aaa[2],bbb[0],bbb[1],bbb[2])))

dxyz_frame = jacobian(xyz_frame)

print('dxyz_frame(ccc) = ',dxyz_frame(ccc))

print('meanmotion(1.0,1.1) = ', meanmotion(1.0,1.1))

mye = 0.3134123
myM = -21.98*np.pi
myEans = eccentricanomaly(mye,myM)
print('myEans-mye*np.sin(myEans)-np.mod(myM,2*np.pi) = ', myEans-mye*np.sin(myEans)-np.mod(myM,2*np.pi))

print('eccentricanomaly(mye,myM) = ', eccentricanomaly(mye,myM))

print('trueanomaly(0.1, 0.16*np.pi) = ', trueanomaly(0.1, 0.16*np.pi)/np.pi, '*pi')
print('trueanomaly(0.1, 0.56*np.pi) = ', trueanomaly(0.1, 0.56*np.pi)/np.pi, '*pi')
print('trueanomaly(0.1, 1.16*np.pi) = ', trueanomaly(0.1, 1.16*np.pi)/np.pi, '*pi')
print('trueanomaly(0.1, 1.56*np.pi) = ', trueanomaly(0.1, 1.56*np.pi)/np.pi, '*pi')
print('trueanomaly(0.1, 1.99*np.pi) = ', trueanomaly(0.1, 1.99*np.pi)/np.pi, '*pi')

print('trueanomaly(0.1, -1.99*np.pi) = ', trueanomaly(0.1, -1.99*np.pi)/np.pi, '*pi')

print('time2truean(1.0, 0.5, 1.0, 1.0+np.pi, 1.0) = ', time2truean(1.0, 0.7, 1.0, 1.0-1.9*np.pi, 1.0)/np.pi, '*pi')

print('orbel2xyz(t, mu, a, e, taup, omega, I, Omega) = ', orbel2xyz(0.0*np.pi, 1.0, 1.0, 0.1, 0.0, np.deg2rad(137.2345) , np.deg2rad(10.0), np.deg2rad(100.1)))
<|endoftext|>


<commit_before>#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Utility methods for testing

For internal use only; no backwards-compatibility guarantees.
"""

import hashlib
import imp
import logging
import os
import shutil
import tempfile
import time

from mock import Mock
from mock import patch

from apache_beam.io.filesystems import FileSystems
from apache_beam.utils import retry

DEFAULT_HASHING_ALG = 'sha1'

class TempDir(object):
  """Context Manager to create and clean-up a temporary directory."""

  def __init__(self):
    self._tempdir = tempfile.mkdtemp()

  def __enter__(self):
    return self

  def __exit__(self, *args):
    if os.path.exists(self._tempdir):
      shutil.rmtree(self._tempdir)

  def get_path(self):
    """Returns the path to the temporary directory."""
    return self._tempdir

  def create_temp_file(self, suffix='', lines=None):
    """Creates a temporary file in the temporary directory.

    Args:
      suffix (str): The filename suffix of the temporary file (e.g. '.txt')
      lines (List[str]): A list of lines that will be written to the temporary
        file.
    Returns:
      The name of the temporary file created.
    """
    with tempfile.NamedTemporaryFile(
        delete=False, dir=self._tempdir, suffix=suffix) as f:
      if lines:
        for line in lines:
          f.write(line)

      return f.name

def compute_hash(content, hashing_alg=DEFAULT_HASHING_ALG):
  """Compute a hash value from a list of string."""
  content.sort()
  m = hashlib.new(hashing_alg)
  for elem in content:
    m.update(str(elem))
  return m.hexdigest()

def patch_retry(testcase, module):
  """A function to patch retry module to use mock clock and logger.

  Clock and logger that defined in retry decorator will be replaced in test
  in order to skip sleep phase when retry happens.

  Args:
    testcase: An instance of unittest.TestCase that calls this function to
      patch retry module.
    module: The module that uses retry and need to be replaced with mock
      clock and logger in test.
  """
  real_retry_with_exponential_backoff = retry.with_exponential_backoff

  def patched_retry_with_exponential_backoff(num_retries, retry_filter):
    """A patch for retry decorator to use a mock dummy clock and logger."""
    return real_retry_with_exponential_backoff(
        num_retries=num_retries, retry_filter=retry_filter, logger=Mock(),
        clock=Mock())

  patch.object(retry, 'with_exponential_backoff',
               side_effect=patched_retry_with_exponential_backoff).start()

  # Reload module after patching.
  imp.reload(module)

  def remove_patches():
    patch.stopall()
    # Reload module again after removing patch.
    imp.reload(module)

  testcase.addCleanup(remove_patches)

@retry.with_exponential_backoff(
    num_retries=3,
    retry_filter=retry.retry_on_beam_io_error_filter)
def delete_files(file_paths):
  """A function to clean up files or directories using ``FileSystems``.

  Glob is supported in file path and directories will be deleted recursively.

  Args:
    file_paths: A list of strings contains file paths or directories.
  """
  if len(file_paths) == 0:
    raise RuntimeError('Clean up failed. Invalid file path: %s.' %
                       file_paths)
  FileSystems.delete(file_paths)

def wait_for_subscriptions_created(subs, timeout=60):
  """Wait for all PubSub subscriptions are created."""
  return _wait_until_all_exist(subs, timeout)

def wait_for_topics_created(topics, timeout=60):
  """Wait for all PubSub topics are created."""
  return _wait_until_all_exist(topics, timeout)

def _wait_until_all_exist(components, timeout):
  needs_wait = set(components)
  start_time = time.time()
  while time.time() - start_time <= timeout:
    for c in components:
      if c in needs_wait and c.exists():
        needs_wait.remove(c)
    if len(needs_wait) == 0:
      return True
    time.sleep(2)

  raise RuntimeError(
      'Timeout after %d seconds. %d of %d topics/subscriptions not exist. '
      'They are %s.' %
      (timeout, len(needs_wait), len(components), list(needs_wait)))

def cleanup_subscriptions(subs):
  """Cleanup PubSub subscriptions if exist."""
  _cleanup_pubsub(subs)

def cleanup_topics(topics):
  """Cleanup PubSub topics if exist."""
  _cleanup_pubsub(topics)

def _cleanup_pubsub(components):
  for c in components:
    if c.exists():
      c.delete()
    else:
      logging.debug('Cannot delete topic/subscription. %s does not exist.',
                    c.full_name)
<commit_msg>Improve test util for PubSub exist check (#4922)<commit_after>#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Utility methods for testing

For internal use only; no backwards-compatibility guarantees.
"""

import hashlib
import imp
import logging
import os
import shutil
import tempfile
import time

from mock import Mock
from mock import patch

from apache_beam.io.filesystems import FileSystems
from apache_beam.utils import retry

DEFAULT_HASHING_ALG = 'sha1'

class TempDir(object):
  """Context Manager to create and clean-up a temporary directory."""

  def __init__(self):
    self._tempdir = tempfile.mkdtemp()

  def __enter__(self):
    return self

  def __exit__(self, *args):
    if os.path.exists(self._tempdir):
      shutil.rmtree(self._tempdir)

  def get_path(self):
    """Returns the path to the temporary directory."""
    return self._tempdir

  def create_temp_file(self, suffix='', lines=None):
    """Creates a temporary file in the temporary directory.

    Args:
      suffix (str): The filename suffix of the temporary file (e.g. '.txt')
      lines (List[str]): A list of lines that will be written to the temporary
        file.
    Returns:
      The name of the temporary file created.
    """
    with tempfile.NamedTemporaryFile(
        delete=False, dir=self._tempdir, suffix=suffix) as f:
      if lines:
        for line in lines:
          f.write(line)

      return f.name

def compute_hash(content, hashing_alg=DEFAULT_HASHING_ALG):
  """Compute a hash value from a list of string."""
  content.sort()
  m = hashlib.new(hashing_alg)
  for elem in content:
    m.update(str(elem))
  return m.hexdigest()

def patch_retry(testcase, module):
  """A function to patch retry module to use mock clock and logger.

  Clock and logger that defined in retry decorator will be replaced in test
  in order to skip sleep phase when retry happens.

  Args:
    testcase: An instance of unittest.TestCase that calls this function to
      patch retry module.
    module: The module that uses retry and need to be replaced with mock
      clock and logger in test.
  """
  real_retry_with_exponential_backoff = retry.with_exponential_backoff

  def patched_retry_with_exponential_backoff(num_retries, retry_filter):
    """A patch for retry decorator to use a mock dummy clock and logger."""
    return real_retry_with_exponential_backoff(
        num_retries=num_retries, retry_filter=retry_filter, logger=Mock(),
        clock=Mock())

  patch.object(retry, 'with_exponential_backoff',
               side_effect=patched_retry_with_exponential_backoff).start()

  # Reload module after patching.
  imp.reload(module)

  def remove_patches():
    patch.stopall()
    # Reload module again after removing patch.
    imp.reload(module)

  testcase.addCleanup(remove_patches)

@retry.with_exponential_backoff(
    num_retries=3,
    retry_filter=retry.retry_on_beam_io_error_filter)
def delete_files(file_paths):
  """A function to clean up files or directories using ``FileSystems``.

  Glob is supported in file path and directories will be deleted recursively.

  Args:
    file_paths: A list of strings contains file paths or directories.
  """
  if len(file_paths) == 0:
    raise RuntimeError('Clean up failed. Invalid file path: %s.' %
                       file_paths)
  FileSystems.delete(file_paths)

def wait_for_subscriptions_created(subs, timeout=60):
  """Wait for all PubSub subscriptions are created."""
  return _wait_until_all_exist(subs, timeout)

def wait_for_topics_created(topics, timeout=60):
  """Wait for all PubSub topics are created."""
  return _wait_until_all_exist(topics, timeout)

def _wait_until_all_exist(components, timeout):
  unchecked_components = set(components)
  start_time = time.time()
  while time.time() - start_time <= timeout:
    unchecked_components = set(
        [c for c in unchecked_components if not c.exists()])
    if len(unchecked_components) == 0:
      return True
    time.sleep(2)

  raise RuntimeError(
      'Timeout after %d seconds. %d of %d topics/subscriptions not exist. '
      'They are %s.' % (timeout, len(unchecked_components),
                        len(components), list(unchecked_components)))

def cleanup_subscriptions(subs):
  """Cleanup PubSub subscriptions if exist."""
  _cleanup_pubsub(subs)

def cleanup_topics(topics):
  """Cleanup PubSub topics if exist."""
  _cleanup_pubsub(topics)

def _cleanup_pubsub(components):
  for c in components:
    if c.exists():
      c.delete()
    else:
      logging.debug('Cannot delete topic/subscription. %s does not exist.',
                    c.full_name)
<|endoftext|>


<commit_before>from django import forms
from django.utils.safestring import mark_safe
from django.contrib.staticfiles.storage import staticfiles_storage
from django.template.loader import render_to_string

import bleach

PAGEDOWN_EDITOR_TEMPLATE = u'''

{textarea}

'''

class MarkdownWidget(forms.Textarea):
    class Media:
        css = {
            'all': ('widgy/js/components/markdown/lib/pagedown.css',),
        }

    def render(self, *args, **kwargs):
        textarea = super(MarkdownWidget, self).render(*args, **kwargs)

        return mark_safe(PAGEDOWN_EDITOR_TEMPLATE.format(textarea=textarea))

class MarkdownField(forms.CharField):
    widget = MarkdownWidget

class CKEditorWidget(forms.Textarea):
    CONFIG = {
        'toolbar': [
            {'name': 'clipboard', 'groups': ['clipboard', 'undo'], 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
            {'name': 'editing', 'groups': ['find', 'selection', 'spellchecker'], 'items': ['Scayt']},
            {'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
            {'name': 'insert', 'items': ['HorizontalRule', 'SpecialChar']},
            {'name': 'tools', 'items': ['Maximize']},
            {'name': 'document', 'groups': ['mode', 'document', 'doctools'], 'items': ['Source']},
            {'name': 'others', 'items': ['-']},
            '/',
            {'name': 'basicstyles', 'groups': ['basicstyles', 'cleanup'], 'items': ['Bold', 'Italic', 'Strike', '-', 'RemoveFormat']},
            {'name': 'paragraph', 'groups': ['list', 'indent', 'blocks', 'align'], 'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote']},
            {'name': 'styles', 'items': ['Styles', 'Format']},
            {'name': 'about', 'items': ['About']}
        ],
        'stylesSet': [
            {'name': 'Big', 'element': 'big'},
            {'name': 'Small', 'element': 'small'},
            {'name': 'Typewriter', 'element': 'tt'},

            {'name': 'Computer Code', 'element': 'code'},
            {'name': 'Keyboard Phrase', 'element': 'kbd'},
            {'name': 'Sample Text', 'element': 'samp'},
            {'name': 'Variable', 'element': 'var'},

            {'name': 'Deleted Text', 'element': 'del'},
            {'name': 'Inserted Text', 'element': 'ins'},

            {'name': 'Cited Work', 'element': 'cite'},
            {'name': 'Inline Quotation', 'element': 'q'},

            {'name': 'Language: RTL', 'element': 'span', 'attributes': {'dir': 'rtl'}},
            {'name': 'Language: LTR', 'element': 'span', 'attributes': {'dir': 'ltr'}},
        ]
    }

    def __init__(self, *args, **kwargs):
        super(CKEditorWidget, self).__init__(*args, **kwargs)
        self.attrs['class'] = 'widgy_ckeditor'

    def render(self, name, value, attrs=None):
        textarea = super(CKEditorWidget, self).render(name, value, attrs)
        return render_to_string('page_builder/ckeditor_widget.html', {
            'html_id': attrs['id'],
            'textarea': textarea,
            'ckeditor_path': staticfiles_storage.url('widgy/js/lib/ckeditor/'),
            'config': self.CONFIG,
        })

class CKEditorField(forms.CharField):
    widget = CKEditorWidget

    ALLOWED_ATTRIBUTES = {
        '*': ['class', 'dir', 'title'],
        'a': ['href', 'target', 'rel', 'name'],
        'time': ['datetime', 'pubdate'],
        'img': ['src'],

        'table': ['border'],
        'colgroup': ['span'],
        'col': ['span'],
        'td': ['colspan', 'rowspan', 'headers'],
        'th': ['colspan', 'rowspan', 'headers', 'scope'],
    }
    ALLOWED_TAGS = [
        'a', 'abbr', 'acronym', 'address', 'b', 'big', 'br', 'cite', 'code',
        'del', 'dfn', 'div', 'em', 'hr', 'i', 'ins', 'kbd', 'mark', 'p', 'pre',
        'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u',
        'var', 'wbr', 's',

        'ul', 'ol', 'li',

        'dl', 'dt', 'dd',

        'blockquote', 'details', 'summary',

        'hgroup', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',

        'figure', 'figcaption', 'img',

        'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th',
        'thead', 'tr',
    ]

    def clean(self, value):
        value = super(CKEditorField, self).clean(value)
        return bleach.clean(value,
                            tags=self.ALLOWED_TAGS,
                            attributes=self.ALLOWED_ATTRIBUTES)
<commit_msg>added the <tt> element to the bleach whitelist<commit_after>from django import forms
from django.utils.safestring import mark_safe
from django.contrib.staticfiles.storage import staticfiles_storage
from django.template.loader import render_to_string

import bleach

PAGEDOWN_EDITOR_TEMPLATE = u'''

{textarea}

'''

class MarkdownWidget(forms.Textarea):
    class Media:
        css = {
            'all': ('widgy/js/components/markdown/lib/pagedown.css',),
        }

    def render(self, *args, **kwargs):
        textarea = super(MarkdownWidget, self).render(*args, **kwargs)

        return mark_safe(PAGEDOWN_EDITOR_TEMPLATE.format(textarea=textarea))

class MarkdownField(forms.CharField):
    widget = MarkdownWidget

class CKEditorWidget(forms.Textarea):
    CONFIG = {
        'toolbar': [
            {'name': 'clipboard', 'groups': ['clipboard', 'undo'], 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
            {'name': 'editing', 'groups': ['find', 'selection', 'spellchecker'], 'items': ['Scayt']},
            {'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
            {'name': 'insert', 'items': ['HorizontalRule', 'SpecialChar']},
            {'name': 'tools', 'items': ['Maximize']},
            {'name': 'document', 'groups': ['mode', 'document', 'doctools'], 'items': ['Source']},
            {'name': 'others', 'items': ['-']},
            '/',
            {'name': 'basicstyles', 'groups': ['basicstyles', 'cleanup'], 'items': ['Bold', 'Italic', 'Strike', '-', 'RemoveFormat']},
            {'name': 'paragraph', 'groups': ['list', 'indent', 'blocks', 'align'], 'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote']},
            {'name': 'styles', 'items': ['Styles', 'Format']},
            {'name': 'about', 'items': ['About']}
        ],
        'stylesSet': [
            {'name': 'Big', 'element': 'big'},
            {'name': 'Small', 'element': 'small'},
            {'name': 'Typewriter', 'element': 'tt'},

            {'name': 'Computer Code', 'element': 'code'},
            {'name': 'Keyboard Phrase', 'element': 'kbd'},
            {'name': 'Sample Text', 'element': 'samp'},
            {'name': 'Variable', 'element': 'var'},

            {'name': 'Deleted Text', 'element': 'del'},
            {'name': 'Inserted Text', 'element': 'ins'},

            {'name': 'Cited Work', 'element': 'cite'},
            {'name': 'Inline Quotation', 'element': 'q'},

            {'name': 'Language: RTL', 'element': 'span', 'attributes': {'dir': 'rtl'}},
            {'name': 'Language: LTR', 'element': 'span', 'attributes': {'dir': 'ltr'}},
        ]
    }

    def __init__(self, *args, **kwargs):
        super(CKEditorWidget, self).__init__(*args, **kwargs)
        self.attrs['class'] = 'widgy_ckeditor'

    def render(self, name, value, attrs=None):
        textarea = super(CKEditorWidget, self).render(name, value, attrs)
        return render_to_string('page_builder/ckeditor_widget.html', {
            'html_id': attrs['id'],
            'textarea': textarea,
            'ckeditor_path': staticfiles_storage.url('widgy/js/lib/ckeditor/'),
            'config': self.CONFIG,
        })

class CKEditorField(forms.CharField):
    widget = CKEditorWidget

    ALLOWED_ATTRIBUTES = {
        '*': ['class', 'dir', 'title'],
        'a': ['href', 'target', 'rel', 'name'],
        'time': ['datetime', 'pubdate'],
        'img': ['src'],

        'table': ['border'],
        'colgroup': ['span'],
        'col': ['span'],
        'td': ['colspan', 'rowspan', 'headers'],
        'th': ['colspan', 'rowspan', 'headers', 'scope'],
    }
    ALLOWED_TAGS = [
        'a', 'abbr', 'acronym', 'address', 'b', 'big', 'br', 'cite', 'code',
        'del', 'dfn', 'div', 'em', 'hr', 'i', 'ins', 'kbd', 'mark', 'p', 'pre',
        'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u',
        'var', 'wbr', 's', 'tt',

        'ul', 'ol', 'li',

        'dl', 'dt', 'dd',

        'blockquote', 'details', 'summary',

        'hgroup', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',

        'figure', 'figcaption', 'img',

        'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th',
        'thead', 'tr',
    ]

    def clean(self, value):
        value = super(CKEditorField, self).clean(value)
        return bleach.clean(value,
                            tags=self.ALLOWED_TAGS,
                            attributes=self.ALLOWED_ATTRIBUTES)
<|endoftext|>


<commit_before>###############################################################################
# Universal Analytics for Python
# Copyright (c) 2013, Analytics Pros
#
# This project is free software, distributed under the BSD license.
# Analytics Pros offers consulting and integration services if your firm needs
# assistance in strategy, implementation, or auditing existing work.
###############################################################################

from urllib2 import urlopen, build_opener, install_opener
from urllib2 import Request, HTTPSHandler
from urllib import urlencode

import datetime
import time

class Time(object):
    """ Wrappers and convenience methods for processing various time representations """

    @classmethod
    def from_unix(cls, seconds, milliseconds = 0):
        """ Produce a full |datetime.datetime| object from a Unix timestamp """
        base = list(time.gmtime(seconds))[0:6]
        base.append(milliseconds * 1000) # microseconds
        return datetime.datetime(* base)

    @classmethod
    def to_unix(cls, timestamp):
        """ Wrapper over time module to produce Unix epoch time as a float """
        if not isinstance(timestamp, datetime.datetime):
            raise TypeError, 'Time.milliseconds expects a datetime object'
        base = time.mktime(timestamp.timetuple())
        return base

    @classmethod
    def milliseconds_offset(cls, timestamp, now = None):
        """ Offset time (in milliseconds) from a |datetime.datetime| object to now """
        if isinstance(timestamp, (int, float)):
            base = timestamp
        else:
            base = cls.to_unix(timestamp)
            base = base + (timestamp.microsecond / 1000000)
        if now is None:
            now = time.time()
        return (now - base) * 1000

class HTTPPost(object):
    """ URL Construction and request handling abstraction.
        This is not intended to be used outside this module.

        Automates mapping of persistent state (i.e. query parameters)
        onto transcient datasets for each query.
    """

    endpoint = 'https://www.google-analytics.com/collect'
    attribs = {}
    base_attribs = {}

    @staticmethod
    def debug():
        """ Activate debugging on urllib2 """
        handler = HTTPSHandler(debuglevel = 1)
        opener = build_opener(handler)
        install_opener(opener)

    # Store properties for all requests
    def __init__(self, *args, **opts):
        self.attribs = {}
        self.base_attribs = opts
        self.attribs.update(opts)

    # Clear transcient properties; restore only the props given at instantiation (__init__)
    def reset(self):
        self.attribs = {}
        self.attribs.update(self.base_attribs)

    # Store transcient properties for subsequent requests
    def update(self, **opts):
        self.attribs.update(opts)

    @classmethod
    def fixUTF8(cls, data):
        """ Convert all strings to UTF-8 """
        for key in data:
            if isinstance(data[ key ], basestring):
                data[ key ] = data[ key ].encode('utf-8')

    # Apply stored properties to the given dataset & POST to the configured endpoint
    def send(self, **data):
        data.update(self.base_attribs)
        data.update(self.attribs)
        self.fixUTF8(data) # Ensure proper encoding for UA's servers...
        request = Request(
                self.endpoint,
                data = urlencode(data),
                headers = {
                    'User-Agent': 'Analytics Pros - Universal Analytics (Python)'
                }
            )
        urlopen(request)

class Tracker(object):
    """ Primary tracking interface for Universal Analytics """
    trackers = {}
    state = {}
    data_mapping = {  }
    valid_hittypes = ('pageview', 'event', 'social', 'appview', 'transaction', 'item', 'exception', 'timing')

    @classmethod
    def params(cls, base, *names):
        for i in names:
            cls.data_mapping[ i ] = base

    @classmethod
    def hittime(cls, timestamp = None, age = None, milliseconds = None):
        """ Returns an integer represeting the milliseconds offset for a given hit (relative to now) """
        if isinstance(timestamp, (int, float)):
            return int(Time.miliseconds_offset(Time.from_unix(timestamp, milliseconds = milliseconds)))
        if isinstance(timestamp, datetime.datetime):
            return int(Time.milliseconds_offset(timestamp))
        if isinstance(age, (int, float)):
            return int(age * 1000) + (milliseconds or 0)

    def __init__(self, account, name = None, client_id = None):
        self.account = account
        self.state = {}
        if name:
            Tracker.trackers[ name ] = self

        self.http = HTTPPost(v = 1, tid = account, cid = client_id)

    # Issue HTTP requests on the measurement protocol
    def send(self, hittype, *args, **opts):
        data = {}
        data.update(self.state)

        hittime = opts.get('hittime', None)
        hitage = opts.get('hitage', None)

        if hittime is not None: # an absolute timestamp
            data['qt'] = self.hittime(timestamp = hittime)

        if hitage is not None: # a relative age (in seconds)
            data['qt'] = self.hittime(age = hitage)

        if hittype not in self.valid_hittypes:
            raise KeyError('Unsupported Universal Analytics Hit Type: {0}'.format(repr(hittype)))

        if hittype == 'pageview' and len(args) and isinstance(args[0], basestring):
            data['dp'] = args[0] # page path

        if hittype == 'event' and len(args) > 1:
            data['ec'] = args[0] # event category
            data['ea'] = args[1] # event action
            if len(args) > 2 and isinstance(args[2], basestring):
                data['el'] = args[2] # event label
            if len(args) > 3 and isinstance(args[3], int):
                data['ev'] = args[3] # event value

        if hittype == 'social' and len(args) > 1:
            data['sn'] = args[0] # social network
            data['sa'] = args[1] # social action
            if len(args) > 2 and isinstance(args[2], basestring):
                data['st'] = args[2] # social target

        if hittype == 'timing' and len(args) > 1:
            data['utc'] = args[0] # timing category
            data['utv'] = args[1] # timing variable
            data['utt'] = args[2] # timing time (value)
            if len(args) > 3 and isinstance(args[3], basestring):
                data['utl'] = args[3] # timing label

        for item in args: # process dictionary-object arguments of transcient data
            if isinstance(item, dict):
                for key, val in item.items():
                    if key in self.data_mapping:
                        data[ self.data_mapping[ key ] ] = val
                    else:
                        data[ key ] = val

        for key, val in opts.items(): # process attributes given as named arguments
            if key in self.data_mapping:
                data[ self.data_mapping[ key ] ] = val

        # Transmit the hit to Google...
        self.http.send(t = hittype, **data)

    # Setting persistent attibutes of the session/hit/etc (inc. custom dimensions/metrics)
    def set(self, name, value):
        if name in self.data_mapping:
            self.state[ self.data_mapping[ name ] ] = value
        else:
            pass

# Declaring name mappings for Measurement Protocol parameters
Tracker.params('cid', 'client-id', 'clientId', 'clientid')
Tracker.params('dp', 'page', 'path')
Tracker.params('dt', 'title', 'pagetitle', 'pageTitle' 'page-title')
Tracker.params('dl', 'location')
Tracker.params('dh', 'hostname')
Tracker.params('sc', 'sessioncontrol', 'session-control', 'sessionControl')
Tracker.params('dr', 'referrer', 'referer')
Tracker.params('qt', 'queueTime', 'queue-time')

# Campaign attribution
Tracker.params('cn', 'campaign', 'campaignName', 'campaign-name')
Tracker.params('cs', 'source', 'campaignSource', 'campaign-source')
Tracker.params('cm', 'medium', 'campaignMedium', 'campaign-medium')
Tracker.params('ck', 'keyword', 'campaignKeyword', 'campaign-keyword')
Tracker.params('cc', 'content', 'campaignContent', 'campaign-content')
Tracker.params('ci', 'campaignId', 'campaignID', 'campaign-id')

# Technical specs
Tracker.params('sr', 'screenResolution', 'screen-resolution', 'resolution')
Tracker.params('vp', 'viewport', 'viewportSize', 'viewport-size')
Tracker.params('de', 'encoding', 'documentEncoding', 'document-encoding')
Tracker.params('sd', 'colors', 'screenColors', 'screen-colors')
Tracker.params('ul', 'language', 'user-language', 'userLanguage')

# Mobile app
Tracker.params('an', 'appName', 'app-name', 'app')
Tracker.params('cd', 'contentDescription', 'screenName', 'screen-name', 'content-description')
Tracker.params('av', 'appVersion', 'app-version', 'version')

# Ecommerce
Tracker.params('ta', 'affiliation', 'transactionAffiliation', 'transaction-affiliation')
Tracker.params('ti', 'transaction', 'transactionId', 'transaction-id')
Tracker.params('tr', 'revenue', 'transactionRevenue', 'transaction-revenue')
Tracker.params('ts', 'shipping', 'transactionShipping', 'transaction-shipping')
Tracker.params('tt', 'tax', 'transactionTax', 'transaction-tax')
Tracker.params('cu', 'currency', 'transactionCurrency', 'transaction-currency') # Currency code, e.g. USD, EUR
Tracker.params('in', 'item-name', 'itemName')
Tracker.params('ip', 'item-price', 'itemPrice')
Tracker.params('iq', 'item-quantity', 'itemQuantity')
Tracker.params('ic', 'item-code', 'sku', 'itemCode')
Tracker.params('iv', 'item-variation', 'item-category', 'itemCategory', 'itemVariation')

# Events
Tracker.params('ec', 'event-category', 'eventCategory', 'category')
Tracker.params('ea', 'event-action', 'eventAction', 'action')
Tracker.params('el', 'event-label', 'eventLabel', 'label')
Tracker.params('ev', 'event-value', 'eventValue', 'value')
Tracker.params('ni', 'noninteractive', 'nonInteractive', 'noninteraction', 'nonInteraction')

# Social
Tracker.params('sa', 'social-action', 'socialAction')
Tracker.params('sn', 'social-network', 'socialNetwork')
Tracker.params('st', 'social-target', 'socialTarget')

# Exceptions
Tracker.params('exd', 'exception-description', 'exceptionDescription')
Tracker.params('exf', 'exception-fatal', 'exceptionFatal')

# User Timing
Tracker.params('utc', 'timingCategory', 'timing-category')
Tracker.params('utv', 'timingVariable', 'timing-variable')
Tracker.params('utt', 'time', 'timingTime', 'timing-time')
Tracker.params('utl', 'timingLabel', 'timing-label')
Tracker.params('dns', 'timingDNS', 'timing-dns')
Tracker.params('pdt', 'timingPageLoad', 'timing-page-load')
Tracker.params('rrt', 'timingRedirect', 'timing-redirect')
Tracker.params('tcp', 'timingTCPConnect', 'timing-tcp-connect')
Tracker.params('srt', 'timingServerResponse', 'timing-server-response')

# Custom dimensions and metrics
for i in range(0,200):
    Tracker.params('cd{0}'.format(i), 'dimension{0}'.format(i))
    Tracker.params('cm{0}'.format(i), 'metric{0}'.format(i))

# Shortcut for creating trackers
def create(account, *args, **kwargs):
    return Tracker(account, *args, **kwargs)

# vim: set nowrap tabstop=4 shiftwidth=4 softtabstop=0 expandtab textwidth=0 filetype=python foldmethod=indent foldcolumn=4
<commit_msg>Add user-id support<commit_after>###############################################################################
# Universal Analytics for Python
# Copyright (c) 2013, Analytics Pros
#
# This project is free software, distributed under the BSD license.
# Analytics Pros offers consulting and integration services if your firm needs
# assistance in strategy, implementation, or auditing existing work.
###############################################################################

from urllib2 import urlopen, build_opener, install_opener
from urllib2 import Request, HTTPSHandler
from urllib import urlencode

import random
import datetime
import time

class Time(object):
    """ Wrappers and convenience methods for processing various time representations """

    @classmethod
    def from_unix(cls, seconds, milliseconds = 0):
        """ Produce a full |datetime.datetime| object from a Unix timestamp """
        base = list(time.gmtime(seconds))[0:6]
        base.append(milliseconds * 1000) # microseconds
        return datetime.datetime(* base)

    @classmethod
    def to_unix(cls, timestamp):
        """ Wrapper over time module to produce Unix epoch time as a float """
        if not isinstance(timestamp, datetime.datetime):
            raise TypeError, 'Time.milliseconds expects a datetime object'
        base = time.mktime(timestamp.timetuple())
        return base

    @classmethod
    def milliseconds_offset(cls, timestamp, now = None):
        """ Offset time (in milliseconds) from a |datetime.datetime| object to now """
        if isinstance(timestamp, (int, float)):
            base = timestamp
        else:
            base = cls.to_unix(timestamp)
            base = base + (timestamp.microsecond / 1000000)
        if now is None:
            now = time.time()
        return (now - base) * 1000

class HTTPPost(object):
    """ URL Construction and request handling abstraction.
        This is not intended to be used outside this module.

        Automates mapping of persistent state (i.e. query parameters)
        onto transcient datasets for each query.
    """

    endpoint = 'https://www.google-analytics.com/collect'
    attribs = {}
    base_attribs = {}

    @staticmethod
    def debug():
        """ Activate debugging on urllib2 """
        handler = HTTPSHandler(debuglevel = 1)
        opener = build_opener(handler)
        install_opener(opener)

    # Store properties for all requests
    def __init__(self, *args, **opts):
        self.attribs = {}
        self.base_attribs = opts
        self.attribs.update(opts)

    # Clear transcient properties; restore only the props given at instantiation (__init__)
    def reset(self):
        self.attribs = {}
        self.attribs.update(self.base_attribs)

    # Store transcient properties for subsequent requests
    def update(self, **opts):
        self.attribs.update(opts)

    @classmethod
    def fixUTF8(cls, data):
        """ Convert all strings to UTF-8 """
        for key in data:
            if isinstance(data[ key ], basestring):
                data[ key ] = data[ key ].encode('utf-8')

    # Apply stored properties to the given dataset & POST to the configured endpoint
    def send(self, **data):
        data.update(self.base_attribs)
        data.update(self.attribs)
        self.fixUTF8(data) # Ensure proper encoding for UA's servers...
        request = Request(
                self.endpoint,
                data = urlencode(data),
                headers = {
                    'User-Agent': 'Analytics Pros - Universal Analytics (Python)'
                }
            )
        urlopen(request)

class Tracker(object):
    """ Primary tracking interface for Universal Analytics """
    trackers = {}
    state = {}
    data_mapping = {  }
    valid_hittypes = ('pageview', 'event', 'social', 'appview', 'transaction', 'item', 'exception', 'timing')

    @classmethod
    def params(cls, base, *names):
        for i in names:
            cls.data_mapping[ i ] = base

    @classmethod
    def hittime(cls, timestamp = None, age = None, milliseconds = None):
        """ Returns an integer represeting the milliseconds offset for a given hit (relative to now) """
        if isinstance(timestamp, (int, float)):
            return int(Time.miliseconds_offset(Time.from_unix(timestamp, milliseconds = milliseconds)))
        if isinstance(timestamp, datetime.datetime):
            return int(Time.milliseconds_offset(timestamp))
        if isinstance(age, (int, float)):
            return int(age * 1000) + (milliseconds or 0)

    def __init__(self, account, name = None, client_id = None, user_id = None):
        self.account = account
        self.state = {}
        if name:
            Tracker.trackers[ name ] = self

        if user_id:
            self.state[ 'uid' ] = user_id

        self.http = HTTPPost(v = 1, tid = account, cid = client_id)

    # Issue HTTP requests on the measurement protocol
    def send(self, hittype, *args, **opts):
        data = {}
        data.update(self.state)

        hittime = opts.get('hittime', None)
        hitage = opts.get('hitage', None)

        if hittime is not None: # an absolute timestamp
            data['qt'] = self.hittime(timestamp = hittime)

        if hitage is not None: # a relative age (in seconds)
            data['qt'] = self.hittime(age = hitage)

        if hittype not in self.valid_hittypes:
            raise KeyError('Unsupported Universal Analytics Hit Type: {0}'.format(repr(hittype)))

        if hittype == 'pageview' and len(args) and isinstance(args[0], basestring):
            data['dp'] = args[0] # page path

        if hittype == 'event' and len(args) > 1:
            data['ec'] = args[0] # event category
            data['ea'] = args[1] # event action
            if len(args) > 2 and isinstance(args[2], basestring):
                data['el'] = args[2] # event label
            if len(args) > 3 and isinstance(args[3], int):
                data['ev'] = args[3] # event value

        if hittype == 'social' and len(args) > 1:
            data['sn'] = args[0] # social network
            data['sa'] = args[1] # social action
            if len(args) > 2 and isinstance(args[2], basestring):
                data['st'] = args[2] # social target

        if hittype == 'timing' and len(args) > 1:
            data['utc'] = args[0] # timing category
            data['utv'] = args[1] # timing variable
            data['utt'] = args[2] # timing time (value)
            if len(args) > 3 and isinstance(args[3], basestring):
                data['utl'] = args[3] # timing label

        for item in args: # process dictionary-object arguments of transcient data
            if isinstance(item, dict):
                for key, val in item.items():
                    if key in self.data_mapping:
                        data[ self.data_mapping[ key ] ] = val
                    else:
                        data[ key ] = val

        for key, val in opts.items(): # process attributes given as named arguments
            if key in self.data_mapping:
                data[ self.data_mapping[ key ] ] = val

        # Transmit the hit to Google...
        self.http.send(t = hittype, **data)

    # Setting persistent attibutes of the session/hit/etc (inc. custom dimensions/metrics)
    def set(self, name, value):
        if name in self.data_mapping:
            self.state[ self.data_mapping[ name ] ] = value
        else:
            pass

# Declaring name mappings for Measurement Protocol parameters
Tracker.params('cid', 'client-id', 'clientId', 'clientid')
Tracker.params('uid', 'user-id', 'userId', 'userid')
Tracker.params('dp', 'page', 'path')
Tracker.params('dt', 'title', 'pagetitle', 'pageTitle' 'page-title')
Tracker.params('dl', 'location')
Tracker.params('dh', 'hostname')
Tracker.params('sc', 'sessioncontrol', 'session-control', 'sessionControl')
Tracker.params('dr', 'referrer', 'referer')
Tracker.params('qt', 'queueTime', 'queue-time')

# Campaign attribution
Tracker.params('cn', 'campaign', 'campaignName', 'campaign-name')
Tracker.params('cs', 'source', 'campaignSource', 'campaign-source')
Tracker.params('cm', 'medium', 'campaignMedium', 'campaign-medium')
Tracker.params('ck', 'keyword', 'campaignKeyword', 'campaign-keyword')
Tracker.params('cc', 'content', 'campaignContent', 'campaign-content')
Tracker.params('ci', 'campaignId', 'campaignID', 'campaign-id')

# Technical specs
Tracker.params('sr', 'screenResolution', 'screen-resolution', 'resolution')
Tracker.params('vp', 'viewport', 'viewportSize', 'viewport-size')
Tracker.params('de', 'encoding', 'documentEncoding', 'document-encoding')
Tracker.params('sd', 'colors', 'screenColors', 'screen-colors')
Tracker.params('ul', 'language', 'user-language', 'userLanguage')

# Mobile app
Tracker.params('an', 'appName', 'app-name', 'app')
Tracker.params('cd', 'contentDescription', 'screenName', 'screen-name', 'content-description')
Tracker.params('av', 'appVersion', 'app-version', 'version')

# Ecommerce
Tracker.params('ta', 'affiliation', 'transactionAffiliation', 'transaction-affiliation')
Tracker.params('ti', 'transaction', 'transactionId', 'transaction-id')
Tracker.params('tr', 'revenue', 'transactionRevenue', 'transaction-revenue')
Tracker.params('ts', 'shipping', 'transactionShipping', 'transaction-shipping')
Tracker.params('tt', 'tax', 'transactionTax', 'transaction-tax')
Tracker.params('cu', 'currency', 'transactionCurrency', 'transaction-currency') # Currency code, e.g. USD, EUR
Tracker.params('in', 'item-name', 'itemName')
Tracker.params('ip', 'item-price', 'itemPrice')
Tracker.params('iq', 'item-quantity', 'itemQuantity')
Tracker.params('ic', 'item-code', 'sku', 'itemCode')
Tracker.params('iv', 'item-variation', 'item-category', 'itemCategory', 'itemVariation')

# Events
Tracker.params('ec', 'event-category', 'eventCategory', 'category')
Tracker.params('ea', 'event-action', 'eventAction', 'action')
Tracker.params('el', 'event-label', 'eventLabel', 'label')
Tracker.params('ev', 'event-value', 'eventValue', 'value')
Tracker.params('ni', 'noninteractive', 'nonInteractive', 'noninteraction', 'nonInteraction')

# Social
Tracker.params('sa', 'social-action', 'socialAction')
Tracker.params('sn', 'social-network', 'socialNetwork')
Tracker.params('st', 'social-target', 'socialTarget')

# Exceptions
Tracker.params('exd', 'exception-description', 'exceptionDescription')
Tracker.params('exf', 'exception-fatal', 'exceptionFatal')

# User Timing
Tracker.params('utc', 'timingCategory', 'timing-category')
Tracker.params('utv', 'timingVariable', 'timing-variable')
Tracker.params('utt', 'time', 'timingTime', 'timing-time')
Tracker.params('utl', 'timingLabel', 'timing-label')
Tracker.params('dns', 'timingDNS', 'timing-dns')
Tracker.params('pdt', 'timingPageLoad', 'timing-page-load')
Tracker.params('rrt', 'timingRedirect', 'timing-redirect')
Tracker.params('tcp', 'timingTCPConnect', 'timing-tcp-connect')
Tracker.params('srt', 'timingServerResponse', 'timing-server-response')

# Custom dimensions and metrics
for i in range(0,200):
    Tracker.params('cd{0}'.format(i), 'dimension{0}'.format(i))
    Tracker.params('cm{0}'.format(i), 'metric{0}'.format(i))

# Shortcut for creating trackers
def create(account, *args, **kwargs):
    return Tracker(account, *args, **kwargs)

# vim: set nowrap tabstop=4 shiftwidth=4 softtabstop=0 expandtab textwidth=0 filetype=python foldmethod=indent foldcolumn=4
<|endoftext|>


<commit_before>from keras.models import Sequential, model_from_json
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import *
from keras.optimizers import SGD, RMSprop
from tetris_game import *
from collections import deque
import os
import glob
import numpy as np
import queue
import threading
import time
import random
import math
import socket
import sys
import os

N_REPLAYS_PER_ROUND = 2000

# SARS - [State_0, Action, Reward, State_1]
STATE0_INDEX = 0
ACTION_INDEX = 1
REWARD_INDEX = 2
STATE1_INDEX = 3

BUFFER_SIZE = 25000
DISCOUNT = 0.85
BROADCAST_PORT = 50005
DESIRED_GAME_QUEUE_SIZE = 50

DEBUG=False

def tbug(msg):
    if DEBUG:
        print(msg)

class StatePrinter:
    def __init__(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.sock.bind(('', 0))

    def send_to_websocket(self, state):
        data = bytes("|".join([",".join(map(str, x)) for x in state[0]]) + "\n", 'ascii')
        self.sock.sendto(data, ("<broadcast>", BROADCAST_PORT))

class Agent():
    def __init__(self):
        self.init_model()
        self.save_requests = 0
        # Treat as a ring buffer
        self.max_pos = 0
        self.current_pos = 0
        self.n_plays = 0
        self.states_t0 = np.zeros((BUFFER_SIZE,1,BOARD_HEIGHT,BOARD_WIDTH), dtype=np.int8)
        self.actions = np.zeros([BUFFER_SIZE], dtype=np.int8)
        self.states_t1 = np.zeros((BUFFER_SIZE,1,BOARD_HEIGHT,BOARD_WIDTH), dtype=np.int8)
        self.rewards = np.zeros([BUFFER_SIZE], dtype=np.float32)
        self.interesting_games = deque([], 2*DESIRED_GAME_QUEUE_SIZE)
        self.current_episode_length = 0
        self.current_game_length = 0
        self.training_runs = 0
        self.recent_q_values = deque([], 10*N_REPLAYS_PER_ROUND)
        self.recent_accuracies = deque([], 5*N_ROLLING_AVG)
        self.recent_losses = deque([], 5*N_ROLLING_AVG)
        self.last_avg_rewards = 0
        self.n_games = 0
        self.state_printer = StatePrinter()
        self.avg_score = 0
        self.n_warmups = 0
        self.exploiting_turn = bool(random.getrandbits(1))
        self.rolled_over_buffer = False

    def exploit(self):
        return random.random() < self.epsilon()

    def warming_up(self):
        return False

    def epsilon(self):
        return min((0.0 + 0.9 * (self.n_games)/10000), 0.9)

    def __log_choice__(self, state, vals):
        if random.random() < 0.0005:
            tbug('Some predicted values for a board. Trained against {} examples so far.'.format(self.training_runs))
            tbug(np.array(state, ndmin=4))
            tbug('[MOVE_RIGHT, MOVE_LEFT, MOVE_DOWN, DO_NOTHING]')
            tbug(vals)
            max_arg = np.argmax(vals)
            tbug('POSSIBLE_MOVES[{}] aka {} aka '.format(max_arg, POSSIBLE_MOVES[max_arg], POSSIBLE_MOVE_NAMES[max_arg]))

    def choose_action(self, state):
        if self.exploiting_turn:
            if not self.exploit():
                choice = random.choice(POSSIBLE_MOVES)
                while (choice == MOVE_DOWN or choice == DO_NOTHING) and random.random() < 0.9:
                    choice = random.choice(POSSIBLE_MOVES)

            else:
                vals = self.model.predict(np.array(state, ndmin=4), verbose=0)
                max_choice = np.argmax(vals)
                choice = POSSIBLE_MOVES[max_choice]
                self.recent_q_values.append(vals[0][max_choice])
        else:
            choice = random.choice(POSSIBLE_MOVES)
            while (choice == MOVE_DOWN or choice == DO_NOTHING) and random.random() < 0.8:
                choice = random.choice(POSSIBLE_MOVES)
        return choice

    def last_n_indexes(self, n):
        if n == 0:
            indexes = [(self.current_pos - 1) % BUFFER_SIZE]
        else:
            indexes = [x % BUFFER_SIZE for x in range(self.current_pos - 1, self.current_pos - 1 - n, -1)]
        return indexes

    def tick_forward(self):
        self.current_pos = (self.current_pos + 1) % BUFFER_SIZE
        self.n_plays += 1
        self.current_game_length += 1
        self.max_pos = min(self.max_pos + 1, BUFFER_SIZE)
        self.current_episode_length += 1
        if self.current_pos == 0: # Rolled over on indexes
            self.rolled_over_buffer = True
            self.last_avg_rewards = sum(self.rewards) / len(self.rewards)

    def keep_game(self, total_reward, indexes):
        n_games = len(self.interesting_games)
        if n_games <= self.interesting_games.maxlen and total_reward > 30:
            states, y = self.training_data_for_indexes(indexes)
            game_info = (total_reward, states, y)
            self.interesting_games.append(game_info)
        else:
            for g in self.interesting_games:
                if g[0] < total_reward:
                    self.interesting_games.remove(g)
                    states, y = self.training_data_for_indexes(indexes)
                    game_info = (total_reward, states, y)
                    self.interesting_games.append(game_info)

    def game_over(self, total_reward):
        self.state_printer.send_to_websocket(self.states_t1[self.current_pos - 1])
        indexes = self.last_n_indexes(self.current_game_length)
        debug_s1 = ""
        debug_s2 = ""
        total_reward_factor = 1.0 + (float(total_reward) * 0.01)
        for i, index in enumerate(indexes):
            debug_s1 += str(self.rewards[index]) + ","
            self.rewards[index] = self.rewards[index] * total_reward_factor
            debug_s2 += str(self.rewards[index]) + ","
        tbug("Game:")
        tbug(debug_s1)
        tbug("---")
        tbug(debug_s2)
        tbug("")
        self.experience_replay()
        self.save()
        self.current_game_length = 0

    def backup_episode(self, reward):
        indexes = self.last_n_indexes(self.current_episode_length)
        debug_indexes = ""
        debug_s1 = ""
        debug_s2 = ""
        for i, index in enumerate(indexes):
            debug_indexes += str(index) + ","
            debug_s1 += str(self.rewards[index]) + ","
            self.rewards[index] += float(reward) * (DISCOUNT**i)
            debug_s2 += str(self.rewards[index]) + ","
        tbug(debug_indexes)
        tbug(debug_s1)
        tbug(debug_s2)
        tbug("")
        if self.current_episode_length > 1:
            tbug(self.states_t0[self.current_pos - 2])
            tbug(str(self.actions[self.current_pos - 2]) + " - " + POSSIBLE_MOVE_NAMES[self.actions[self.current_pos - 2]])
            tbug(self.states_t1[self.current_pos - 2])
            tbug('----->')
        tbug(self.states_t0[self.current_pos - 1])
        tbug(str(self.actions[self.current_pos - 1]) + " - " + POSSIBLE_MOVE_NAMES[self.actions[self.current_pos - 1]])
        tbug(self.states_t1[self.current_pos - 1])
        self.current_episode_length = 0

    def on_episode_end(self, reward):
        self.backup_episode(reward)
        self.exploiting_turn = self.exploit()

    def handle(self, state0, action, reward, state1):
        self.states_t0[self.current_pos] = state0
        self.actions[self.current_pos] = action
        self.rewards[self.current_pos] = 0
        self.states_t1[self.current_pos] = state1
        self.tick_forward()
        sys.stdout.flush()

    def save(self):
        self.save_requests += 1
        if self.save_requests == 100:
            self.save_requests = 0
            name = time.strftime("%m-%dT%H%M%S%Z")
            model_file = 'output/model_{}.json'.format(name)
            weights_file = 'output/weights_{}.h5'.format(name)
            open(model_file, 'w').write(self.model.to_json())
            self.model.save_weights(weights_file, overwrite=True)
            tbug('Saved: {} and {}'.format(model_file, weights_file))

    def experience_replay(self):
        if self.warming_up():
            return
        if self.rolled_over_buffer:
            print('TRAIN')
            self.rolled_over_buffer = False
            keep_training = True
            random_idxs = np.random.randint(0, min(self.n_plays, BUFFER_SIZE) - 1, min(self.n_plays, BUFFER_SIZE))
            states, y = self.training_data_for_indexes(random_idxs)
            loss_i1, accuracy = self.model.train_on_batch(states, y, accuracy=True)
            improvements = deque([], 3)
            while keep_training:
                random_idxs = np.random.randint(0, min(self.n_plays, BUFFER_SIZE) - 1, min(self.n_plays, BUFFER_SIZE))
                states, y = self.training_data_for_indexes(random_idxs)
                loss_i2, accuracy = self.model.train_on_batch(states, y, accuracy=True)
                improvements.append(abs((loss_i2 - loss_i1)) / float(loss_i1))
                print('{} - {} / {}  =  {}'.format(loss_i2, loss_i1, loss_i1, sum(improvements)/float(len(improvements))))
                keep_training = sum(improvements)/float(len(improvements)) > 0.001 and len(improvements) > 1
                sys.stdout.flush()
                loss_i1 = loss_i2

            self.training_runs += y.shape[0]
            self.recent_losses.append(loss_i2)
            self.recent_accuracies.append(accuracy)

    def training_data_for_indexes(self, indexes):
        y = self.model.predict(self.states_t0[indexes], verbose=0)
        future_rewards = DISCOUNT*(np.amax(self.model.predict(self.states_t1[indexes], verbose=0), axis=1))
        for i, a in enumerate(self.actions[indexes]):
            a_index = np.where(POSSIBLE_MOVES == a)[0]
            y[i][a_index] = self.rewards[indexes][i] + future_rewards[i]
        states = self.states_t0[indexes]
        return (states, y)

    def init_model(self):
    #     self.model = model_from_json(open(max(glob.iglob('output/model_*.json'), key=os.path.getctime)).read())
    #     self.model.load_weights(max(glob.iglob('output/weights_*.h5'), key=os.path.getctime))
        self.model = Sequential()
       #  self.model.add(Convolution2D(64, 5, 5,
       #                                        activation='tanh',
       #                                        subsample=(2,2),
       #                                        init='he_uniform',
       #                                        input_shape=(1,BOARD_HEIGHT,BOARD_WIDTH)))
       #  self.model.add(Flatten())
       #  self.model.add(Dropout(0.5))

        self.model.add(Flatten(input_shape=(1,BOARD_HEIGHT,BOARD_WIDTH)))
        self.model.add(Dense(1024, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(128, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(128, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(128, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(128, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(len(POSSIBLE_MOVES), activation='linear', init='he_uniform'))
        optim = SGD(lr=0.05, decay=0.0, momentum=0.5, nesterov=True)
        self.model.compile(loss='mae', optimizer=optim)

class GreedyAgent(Agent):
    def __init__(self):
        threading.Thread.__init__(self)
        self.init_model()
        self.state_printer = StatePrinter()

    def init_model(self):
        self.model = model_from_json(open(max(glob.iglob('output/model_*.json'), key=os.path.getctime)).read())
        self.model.load_weights(max(glob.iglob('output/weights_*.h5'), key=os.path.getctime))

    def game_over(self, total_reward):
        self.state_printer.send_to_websocket(self.states_t1[self.current_pos - 1])
        time.sleep(2)
        if self.n_games % 5 == 0:
            self.init_model()

    def on_episode_end(self, reward):
        pass

    def choose_action(self, state):
        time.sleep(1)
        self.state_printer.send_to_websocket(state)
        vals = self.model.predict(np.array(state, ndmin=4), verbose=0)
        print(state)
        print(POSSIBLE_MOVE_NAMES)
        print(vals)
        max_choice = np.argmax(vals)
        choice = POSSIBLE_MOVES[max_choice]
        self.recent_q_values.append(vals[0][max_choice])
        print(str(choice) + " -> " + POSSIBLE_MOVE_NAMES[choice])
        return choice

    def handle(self, s0, a, r, s1):
        pass
<commit_msg>More greedy agent updates<commit_after>from keras.models import Sequential, model_from_json
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import *
from keras.optimizers import SGD, RMSprop
from tetris_game import *
from collections import deque
import os
import glob
import numpy as np
import queue
import threading
import time
import random
import math
import socket
import sys
import os

N_REPLAYS_PER_ROUND = 2000

# SARS - [State_0, Action, Reward, State_1]
STATE0_INDEX = 0
ACTION_INDEX = 1
REWARD_INDEX = 2
STATE1_INDEX = 3

BUFFER_SIZE = 25000
DISCOUNT = 0.85
BROADCAST_PORT = 50005
DESIRED_GAME_QUEUE_SIZE = 50

DEBUG=False

def tbug(msg):
    if DEBUG:
        print(msg)

class StatePrinter:
    def __init__(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.sock.bind(('', 0))

    def send_to_websocket(self, state):
        data = bytes("|".join([",".join(map(str, x)) for x in state[0]]) + "\n", 'ascii')
        self.sock.sendto(data, ("<broadcast>", BROADCAST_PORT))

class Agent():
    def __init__(self):
        self.init_model()
        self.save_requests = 0
        # Treat as a ring buffer
        self.max_pos = 0
        self.current_pos = 0
        self.n_plays = 0
        self.states_t0 = np.zeros((BUFFER_SIZE,1,BOARD_HEIGHT,BOARD_WIDTH), dtype=np.int8)
        self.actions = np.zeros([BUFFER_SIZE], dtype=np.int8)
        self.states_t1 = np.zeros((BUFFER_SIZE,1,BOARD_HEIGHT,BOARD_WIDTH), dtype=np.int8)
        self.rewards = np.zeros([BUFFER_SIZE], dtype=np.float32)
        self.interesting_games = deque([], 2*DESIRED_GAME_QUEUE_SIZE)
        self.current_episode_length = 0
        self.current_game_length = 0
        self.training_runs = 0
        self.recent_q_values = deque([], 10*N_REPLAYS_PER_ROUND)
        self.recent_accuracies = deque([], 5*N_ROLLING_AVG)
        self.recent_losses = deque([], 5*N_ROLLING_AVG)
        self.last_avg_rewards = 0
        self.n_games = 0
        self.state_printer = StatePrinter()
        self.avg_score = 0
        self.n_warmups = 0
        self.exploiting_turn = bool(random.getrandbits(1))
        self.rolled_over_buffer = False

    def exploit(self):
        return random.random() < self.epsilon()

    def warming_up(self):
        return False

    def epsilon(self):
        return min((0.0 + 0.9 * (self.n_games)/10000), 0.9)

    def __log_choice__(self, state, vals):
        if random.random() < 0.0005:
            tbug('Some predicted values for a board. Trained against {} examples so far.'.format(self.training_runs))
            tbug(np.array(state, ndmin=4))
            tbug('[MOVE_RIGHT, MOVE_LEFT, MOVE_DOWN, DO_NOTHING]')
            tbug(vals)
            max_arg = np.argmax(vals)
            tbug('POSSIBLE_MOVES[{}] aka {} aka '.format(max_arg, POSSIBLE_MOVES[max_arg], POSSIBLE_MOVE_NAMES[max_arg]))

    def choose_action(self, state):
        if self.exploiting_turn:
            if not self.exploit():
                choice = random.choice(POSSIBLE_MOVES)
                while (choice == MOVE_DOWN or choice == DO_NOTHING) and random.random() < 0.9:
                    choice = random.choice(POSSIBLE_MOVES)

            else:
                vals = self.model.predict(np.array(state, ndmin=4), verbose=0)
                max_choice = np.argmax(vals)
                choice = POSSIBLE_MOVES[max_choice]
                self.recent_q_values.append(vals[0][max_choice])
        else:
            choice = random.choice(POSSIBLE_MOVES)
            while (choice == MOVE_DOWN or choice == DO_NOTHING) and random.random() < 0.8:
                choice = random.choice(POSSIBLE_MOVES)
        return choice

    def last_n_indexes(self, n):
        if n == 0:
            indexes = [(self.current_pos - 1) % BUFFER_SIZE]
        else:
            indexes = [x % BUFFER_SIZE for x in range(self.current_pos - 1, self.current_pos - 1 - n, -1)]
        return indexes

    def tick_forward(self):
        self.current_pos = (self.current_pos + 1) % BUFFER_SIZE
        self.n_plays += 1
        self.current_game_length += 1
        self.max_pos = min(self.max_pos + 1, BUFFER_SIZE)
        self.current_episode_length += 1
        if self.current_pos == 0: # Rolled over on indexes
            self.rolled_over_buffer = True
            self.last_avg_rewards = sum(self.rewards) / len(self.rewards)

    def keep_game(self, total_reward, indexes):
        n_games = len(self.interesting_games)
        if n_games <= self.interesting_games.maxlen and total_reward > 30:
            states, y = self.training_data_for_indexes(indexes)
            game_info = (total_reward, states, y)
            self.interesting_games.append(game_info)
        else:
            for g in self.interesting_games:
                if g[0] < total_reward:
                    self.interesting_games.remove(g)
                    states, y = self.training_data_for_indexes(indexes)
                    game_info = (total_reward, states, y)
                    self.interesting_games.append(game_info)

    def game_over(self, total_reward):
        self.state_printer.send_to_websocket(self.states_t1[self.current_pos - 1])
        indexes = self.last_n_indexes(self.current_game_length)
        debug_s1 = ""
        debug_s2 = ""
        total_reward_factor = 1.0 + (float(total_reward) * 0.01)
        for i, index in enumerate(indexes):
            debug_s1 += str(self.rewards[index]) + ","
            self.rewards[index] = self.rewards[index] * total_reward_factor
            debug_s2 += str(self.rewards[index]) + ","
        tbug("Game:")
        tbug(debug_s1)
        tbug("---")
        tbug(debug_s2)
        tbug("")
        self.experience_replay()
        self.save()
        self.current_game_length = 0

    def backup_episode(self, reward):
        indexes = self.last_n_indexes(self.current_episode_length)
        debug_indexes = ""
        debug_s1 = ""
        debug_s2 = ""
        for i, index in enumerate(indexes):
            debug_indexes += str(index) + ","
            debug_s1 += str(self.rewards[index]) + ","
            self.rewards[index] += float(reward) * (DISCOUNT**i)
            debug_s2 += str(self.rewards[index]) + ","
        tbug(debug_indexes)
        tbug(debug_s1)
        tbug(debug_s2)
        tbug("")
        if self.current_episode_length > 1:
            tbug(self.states_t0[self.current_pos - 2])
            tbug(str(self.actions[self.current_pos - 2]) + " - " + POSSIBLE_MOVE_NAMES[self.actions[self.current_pos - 2]])
            tbug(self.states_t1[self.current_pos - 2])
            tbug('----->')
        tbug(self.states_t0[self.current_pos - 1])
        tbug(str(self.actions[self.current_pos - 1]) + " - " + POSSIBLE_MOVE_NAMES[self.actions[self.current_pos - 1]])
        tbug(self.states_t1[self.current_pos - 1])
        self.current_episode_length = 0

    def on_episode_end(self, reward):
        self.backup_episode(reward)
        self.exploiting_turn = self.exploit()

    def handle(self, state0, action, reward, state1):
        self.states_t0[self.current_pos] = state0
        self.actions[self.current_pos] = action
        self.rewards[self.current_pos] = 0
        self.states_t1[self.current_pos] = state1
        self.tick_forward()
        sys.stdout.flush()

    def save(self):
        self.save_requests += 1
        if self.save_requests == 100:
            self.save_requests = 0
            name = time.strftime("%m-%dT%H%M%S%Z")
            model_file = 'output/model_{}.json'.format(name)
            weights_file = 'output/weights_{}.h5'.format(name)
            open(model_file, 'w').write(self.model.to_json())
            self.model.save_weights(weights_file, overwrite=True)
            tbug('Saved: {} and {}'.format(model_file, weights_file))

    def experience_replay(self):
        if self.warming_up():
            return
        if self.rolled_over_buffer:
            print('TRAIN')
            self.rolled_over_buffer = False
            keep_training = True
            random_idxs = np.random.randint(0, min(self.n_plays, BUFFER_SIZE) - 1, min(self.n_plays, BUFFER_SIZE))
            states, y = self.training_data_for_indexes(random_idxs)
            loss_i1, accuracy = self.model.train_on_batch(states, y, accuracy=True)
            improvements = deque([], 3)
            while keep_training:
                random_idxs = np.random.randint(0, min(self.n_plays, BUFFER_SIZE) - 1, min(self.n_plays, BUFFER_SIZE))
                states, y = self.training_data_for_indexes(random_idxs)
                loss_i2, accuracy = self.model.train_on_batch(states, y, accuracy=True)
                improvements.append(abs((loss_i2 - loss_i1)) / float(loss_i1))
                print('{} - {} / {}  =  {}'.format(loss_i2, loss_i1, loss_i1, sum(improvements)/float(len(improvements))))
                keep_training = sum(improvements)/float(len(improvements)) > 0.001 and len(improvements) > 1
                sys.stdout.flush()
                loss_i1 = loss_i2

            self.training_runs += y.shape[0]
            self.recent_losses.append(loss_i2)
            self.recent_accuracies.append(accuracy)

    def training_data_for_indexes(self, indexes):
        y = self.model.predict(self.states_t0[indexes], verbose=0)
        future_rewards = DISCOUNT*(np.amax(self.model.predict(self.states_t1[indexes], verbose=0), axis=1))
        for i, a in enumerate(self.actions[indexes]):
            a_index = np.where(POSSIBLE_MOVES == a)[0]
            y[i][a_index] = self.rewards[indexes][i] + future_rewards[i]
        states = self.states_t0[indexes]
        return (states, y)

    def init_model(self):
    #     self.model = model_from_json(open(max(glob.iglob('output/model_*.json'), key=os.path.getctime)).read())
    #     self.model.load_weights(max(glob.iglob('output/weights_*.h5'), key=os.path.getctime))
        self.model = Sequential()
       #  self.model.add(Convolution2D(64, 5, 5,
       #                                        activation='tanh',
       #                                        subsample=(2,2),
       #                                        init='he_uniform',
       #                                        input_shape=(1,BOARD_HEIGHT,BOARD_WIDTH)))
       #  self.model.add(Flatten())
       #  self.model.add(Dropout(0.5))

        self.model.add(Flatten(input_shape=(1,BOARD_HEIGHT,BOARD_WIDTH)))
        self.model.add(Dense(1024, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(128, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(128, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(128, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(128, activation='tanh', init='he_uniform'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(len(POSSIBLE_MOVES), activation='linear', init='he_uniform'))
        optim = SGD(lr=0.05, decay=0.0, momentum=0.5, nesterov=True)
        self.model.compile(loss='mae', optimizer=optim)

class GreedyAgent(Agent):
    def __init__(self):
        super().__init__()
        self.init_model()

    def init_model(self):
        self.model = model_from_json(open(max(glob.iglob('/home/ubuntu/src/reinforcement_learning/output/model_*.json'), key=os.path.getctime)).read())
        self.model.load_weights(max(glob.iglob('/home/ubuntu/src/reinforcement_learning/output/weights_*.h5'), key=os.path.getctime))

    def game_over(self, total_reward):
        time.sleep(1)
        if self.n_games % 5 == 0:
            self.init_model()

    def on_episode_end(self, reward):
        pass

    def choose_action(self, state):
        time.sleep(1)
        self.state_printer.send_to_websocket(state)
        vals = self.model.predict(np.array(state, ndmin=4), verbose=0)
        print(state)
        print(POSSIBLE_MOVE_NAMES)
        print(vals)
        max_choice = np.argmax(vals)
        choice = POSSIBLE_MOVES[max_choice]
        self.recent_q_values.append(vals[0][max_choice])
        print(str(choice) + " -> " + POSSIBLE_MOVE_NAMES[choice])
        return choice

    def handle(self, s0, a, r, s1):
        pass
<|endoftext|>


<commit_before>import datetime
from corehq.util.dates import iso_string_to_date
from dimagi.utils.dates import DateSpan
import json
from django.utils.translation import ugettext as _, ugettext_noop
from corehq.apps.reports.dont_use.fields import ReportField

class DateRangeField(ReportField):
    name = ugettext_noop("Date Range")
    slug = "datespan"
    template = "m4change/fields/daterange.html"
    inclusive = True
    default_days = 30

    def update_context(self):
        self.context["datespan_name"] = self.name

        range = self.request.GET.get('range', None)
        if range is not None:
            dates = str(range).split(_(' to '))
            self.request.datespan.startdate = datetime.datetime.combine(
                iso_string_to_date(dates[0]), datetime.time())
            self.request.datespan.enddate = datetime.datetime.combine(
                iso_string_to_date(dates[1]), datetime.time())

        self.datespan = DateSpan.since(self.default_days, timezone=self.timezone, inclusive=self.inclusive)
        if self.request.datespan.is_valid():
            self.datespan.startdate = self.request.datespan.startdate
            self.datespan.enddate = self.request.datespan.enddate
        self.context['timezone'] = self.timezone.zone
        self.context['datespan'] = self.datespan

        report_labels = json.dumps({
            'year_to_date': _('Year to Date'), 'last_month': _('Last Month'),
            'last_quarter': _('Last Quarter'), 'last_two_quarters': _('Last Two Quarters'),
            'last_three_quarters': _('Last Three Quarters'), 'last_year': _('Last Year'),
            'last_two_years': _('Last Two Years'), 'last_three_years': _('Last Three Years'),
            'last_four_years': _('Last Four Years')
        })

        self.context['report_labels'] = report_labels
        self.context['separator'] = _(' to ')

class CaseSearchField(ReportField):
    name = ugettext_noop("Case Search")
    slug = "case_search"
    template = "reports/filters/search.html"

    def update_context(self):
        self.search_query = self.request.GET.get("case_search", "")
        self.context["search_query"] = self.search_query
        self.context["label"] = _("Case Search")
<commit_msg>skip when range is an empty string<commit_after>import datetime
from corehq.util.dates import iso_string_to_date
from dimagi.utils.dates import DateSpan
import json
from django.utils.translation import ugettext as _, ugettext_noop
from corehq.apps.reports.dont_use.fields import ReportField

class DateRangeField(ReportField):
    name = ugettext_noop("Date Range")
    slug = "datespan"
    template = "m4change/fields/daterange.html"
    inclusive = True
    default_days = 30

    def update_context(self):
        self.context["datespan_name"] = self.name

        range = self.request.GET.get('range', None)
        if range:
            dates = str(range).split(_(' to '))
            self.request.datespan.startdate = datetime.datetime.combine(
                iso_string_to_date(dates[0]), datetime.time())
            self.request.datespan.enddate = datetime.datetime.combine(
                iso_string_to_date(dates[1]), datetime.time())

        self.datespan = DateSpan.since(self.default_days, timezone=self.timezone, inclusive=self.inclusive)
        if self.request.datespan.is_valid():
            self.datespan.startdate = self.request.datespan.startdate
            self.datespan.enddate = self.request.datespan.enddate
        self.context['timezone'] = self.timezone.zone
        self.context['datespan'] = self.datespan

        report_labels = json.dumps({
            'year_to_date': _('Year to Date'), 'last_month': _('Last Month'),
            'last_quarter': _('Last Quarter'), 'last_two_quarters': _('Last Two Quarters'),
            'last_three_quarters': _('Last Three Quarters'), 'last_year': _('Last Year'),
            'last_two_years': _('Last Two Years'), 'last_three_years': _('Last Three Years'),
            'last_four_years': _('Last Four Years')
        })

        self.context['report_labels'] = report_labels
        self.context['separator'] = _(' to ')

class CaseSearchField(ReportField):
    name = ugettext_noop("Case Search")
    slug = "case_search"
    template = "reports/filters/search.html"

    def update_context(self):
        self.search_query = self.request.GET.get("case_search", "")
        self.context["search_query"] = self.search_query
        self.context["label"] = _("Case Search")
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import argparse
import hashlib
import json
import os
import re
import time

os.environ['PYWIKIBOT_DIR'] = os.path.dirname(os.path.realpath(__file__))
import pywikibot
from pywikibot.data.api import Request
from config import config_page_name  # pylint: disable=E0611,W0614

parser = argparse.ArgumentParser()
parser.add_argument('--confirm', action='store_true')
parser.set_defaults(confirm=False)
args = parser.parse_args()
print(args)

os.environ['TZ'] = 'UTC'

site = pywikibot.Site()
site.login()

config_page = pywikibot.Page(site, config_page_name)
cfg = config_page.text
cfg = json.loads(cfg)
print(json.dumps(cfg, indent=4, ensure_ascii=False))

if not cfg["enable"]:
    exit("disabled\n")

cvpage = pywikibot.Page(site, cfg["page_name"])
text = cvpage.text

rndstr = hashlib.md5(str(time.time()).encode()).hexdigest()
text = re.sub(r'^(===.*=== *)$', rndstr + r'\1', text, flags=re.M)
sections = text.split(rndstr)

totalcnt = 0
istimeout = False
for secid, section in enumerate(sections[1:], 1):
    title = section[:section.index('\n')]
    print(title, end="\t")
    text = section[section.index('\n') + 1:]

    rndstr = hashlib.md5(str(time.time()).encode()).hexdigest()
    text = re.sub(r"^(.*{{CopyvioEntry\|.+)$", rndstr + r"\1", text, flags=re.M)

    text = text.split(rndstr)

    entrycnt = len(text[1:])
    print(entrycnt)

    newtext = title + '\n' + text[0]
    cnt = 0
    for entry in text[1:]:
        m = re.match(r"{{CopyvioEntry\|1=([^|]+)\|time=(\d+)\|", entry)
        if m:
            pagename = m.group(1)
            entrytime = int(m.group(2))
            print("\t", pagename, entrytime, end="\t")
            if entrytime > time.time():
                istimeout = True
                print("timeout", end="\t")
            page = pywikibot.Page(site, pagename)
            remove = False
            if page.exists():
                for reversion in page.revisions():
                    if reversion.timestamp.astimezone().timestamp() < entrytime - 86400 * 7:
                        break
                    if re.search(r"替换为未侵权版本|清理\[\[Category:已完成侵權驗證的頁面]]", reversion.comment):
                        print(reversion.comment)
                        remove = True
                        break
            else:
                data = Request(site=site, parameters={
                    'action': 'query',
                    'letitle': pagename,
                    "list": "logevents",
                    "leprop": "comment",
                    "letype": "delete",
                    "lelimit": "1"
                }).submit()
                print(data['query']['logevents'][0]['comment'])
                remove = True

            if remove:
                cnt += 1
            else:
                print("not remove")
                newtext += entry
        else:
            print("not match")
            newtext += entry

    if cnt == entrycnt:
        print("\t*** remove {} entry and section".format(cnt))
        sections[secid] = ''
    elif cnt > 0:
        print("\t*** remove {} entry".format(cnt))
        sections[secid] = newtext

    totalcnt += cnt

    if istimeout:
        break

text = ''.join(sections)

if cvpage.text == text:
    exit("nothing changed")

pywikibot.showDiff(cvpage.text, text)
cvpage.text = text
summary = cfg["page_summary"].format(totalcnt)
print(summary)
if args.confirm:
    save = input('Save?').lower()
else:
    save = 'yes'
if save in ['yes', 'y', '']:
    cvpage.save(summary=summary, minor=False)
<commit_msg>Show remain entry count in summary<commit_after># -*- coding: utf-8 -*-
import argparse
import hashlib
import json
import os
import re
import time

os.environ['PYWIKIBOT_DIR'] = os.path.dirname(os.path.realpath(__file__))
import pywikibot
from pywikibot.data.api import Request
from config import config_page_name  # pylint: disable=E0611,W0614

parser = argparse.ArgumentParser()
parser.add_argument('--confirm', action='store_true')
parser.set_defaults(confirm=False)
args = parser.parse_args()
print(args)

os.environ['TZ'] = 'UTC'

site = pywikibot.Site()
site.login()

config_page = pywikibot.Page(site, config_page_name)
cfg = config_page.text
cfg = json.loads(cfg)
print(json.dumps(cfg, indent=4, ensure_ascii=False))

if not cfg["enable"]:
    exit("disabled\n")

cvpage = pywikibot.Page(site, cfg["page_name"])
text = cvpage.text

rndstr = hashlib.md5(str(time.time()).encode()).hexdigest()
text = re.sub(r'^(===.*=== *)$', rndstr + r'\1', text, flags=re.M)
sections = text.split(rndstr)

removedcnt = 0
remaincnt = 0
istimeout = False
for secid, section in enumerate(sections[1:], 1):
    title = section[:section.index('\n')]
    print(title, end="\t")
    text = section[section.index('\n') + 1:]

    rndstr = hashlib.md5(str(time.time()).encode()).hexdigest()
    text = re.sub(r"^(.*{{CopyvioEntry\|.+)$", rndstr + r"\1", text, flags=re.M)

    text = text.split(rndstr)

    entrycnt = len(text[1:])
    print(entrycnt)

    newtext = title + '\n' + text[0]
    cnt = 0
    for entry in text[1:]:
        m = re.match(r"{{CopyvioEntry\|1=([^|]+)\|time=(\d+)\|", entry)
        if m:
            pagename = m.group(1)
            entrytime = int(m.group(2))
            print("\t", pagename, entrytime, end="\t")
            if entrytime > time.time():
                istimeout = True
                print("timeout", end="\t")
            page = pywikibot.Page(site, pagename)
            remove = False
            if page.exists():
                for reversion in page.revisions():
                    if reversion.timestamp.astimezone().timestamp() < entrytime - 86400 * 7:
                        break
                    if re.search(r"替换为未侵权版本|清理\[\[Category:已完成侵權驗證的頁面]]", reversion.comment):
                        print(reversion.comment)
                        remove = True
                        break
            else:
                data = Request(site=site, parameters={
                    'action': 'query',
                    'letitle': pagename,
                    "list": "logevents",
                    "leprop": "comment",
                    "letype": "delete",
                    "lelimit": "1"
                }).submit()
                print(data['query']['logevents'][0]['comment'])
                remove = True

            if remove:
                cnt += 1
            else:
                if not istimeout:
                    remaincnt += 1
                print("not remove")
                newtext += entry
        else:
            print("not match")
            newtext += entry

    if cnt == entrycnt:
        print("\t*** remove {} entry and section".format(cnt))
        sections[secid] = ''
    elif cnt > 0:
        print("\t*** remove {} entry".format(cnt))
        sections[secid] = newtext

    removedcnt += cnt

    if istimeout:
        break

text = ''.join(sections)

if cvpage.text == text:
    exit("nothing changed")

pywikibot.showDiff(cvpage.text, text)
cvpage.text = text
summary = cfg["page_summary"].format(removedcnt, remaincnt)
print(summary)
if args.confirm:
    save = input('Save?').lower()
else:
    save = 'yes'
if save in ['yes', 'y', '']:
    cvpage.save(summary=summary, minor=False)
<|endoftext|>


<commit_before>"""Codewars: Multiples of 3 or 5
6 kyu

URL: https://www.codewars.com/kata/514b92a657cdc65150000006/

If we list all the natural numbers below 10 that are multiples of 3 or 5,
we get 3, 5, 6 and 9. The sum of these multiples is 23.

Finish the solution so that it returns the sum of all the multiples of 3 or 5
below the number passed in.

Note: If the number is a multiple of both 3 and 5, only count it once.
"""

def solution(number):
    pass

def main():
    pass

if __name__ == '__main__':
    main()
<commit_msg>Complete cw_multiples_of_3_or_5.py<commit_after>"""Codewars: Multiples of 3 or 5
6 kyu

URL: https://www.codewars.com/kata/514b92a657cdc65150000006/

If we list all the natural numbers below 10 that are multiples of 3 or 5,
we get 3, 5, 6 and 9. The sum of these multiples is 23.

Finish the solution so that it returns the sum of all the multiples of 3 or 5
below the number passed in.

Note: If the number is a multiple of both 3 and 5, only count it once.
"""

def solution(number):
    """
    Time complexity: O(n), where n is number.
    Space complexity: O(1).
    """
    result = 0
    for n in range(1, number):
        if n % 3 == 0 or n % 5 == 0:
            result += n
    return result

def main():
    assert solution(10) == 23
    assert solution(16) == 60

if __name__ == '__main__':
    main()
<|endoftext|>