<commit_before>"""Cosmological Model simulations by Ian Huston
    $Id: cosmomodels.py,v 1.10 2008/04/23 19:03:54 ith Exp $

    Provides generic class CosmologicalModel that can be used as a base for explicit models."""

from __future__ import division # Get rid of integer division problems, i.e. 1/2=0
import numpy as N
import pylab as P
import rk4
import sys
import os.path
import datetime
import pickle

class ModelError(StandardError):
    """Generic error for model simulating. Attributes include current results stack."""

    def __init__(self, expression, tresult=None, yresult=None):
        self.expression = expression
        self.tresult = tresult
        self.yresult = yresult

class CosmologicalModel:
    """Generic class for cosmological model simulations.
    Has no derivs function to pass to ode solver, but does have
    plotting function and initial conditions check."""

    solverlist = ["odeint", "rkdriver_dumb"]

    def __init__(self, ystart, tstart, tend, tstep_wanted, tstep_min, eps=1.0e-6, dxsav=0.0, solver="odeint"):
        """Initialize model variables, some with default values. Default solver is odeint."""

        self.ystart = ystart

        if tstart < tend:
            self.tstart, self.tend = tstart, tend
        elif tstart==tend:
            raise ValueError, "End time is the same as start time!"
        else:
            raise ValueError, "Ending time is before starting time!"

        if tstep_wanted >= tstep_min:
            self.tstep_wanted, self.tstep_min = tstep_wanted, tstep_min
        else:
            raise ValueError, "Desired time step is smaller than specified minimum!"

        if eps < 1:
            self.eps = eps
        else:
            raise ValueError, "Not enough accuracy! Change eps < 1."

        if dxsav >=0.0:
            self.dxsav = dxsav
        else:
            raise ValueError, "Data saving step must be 0 or positive!"

        if solver in self.solverlist:
            self.solver = solver
        else:
            raise ValueError, "Solver not recognized!"

        self.tresult = None #Will hold last time result
        self.yresult = None #Will hold array of last y results
        self.runcount = 0 #How many times has the model been run?
        self.resultlist = [] #List of all completed results.

        self.plottitle = "A generic Cosmological Model"
        self.tname = "Time"
        self.ynames = ["First dependent variable"]

    def derivs(self, t, yarray):
        """Return an array of derivatives of the dependent variables yarray at timestep t"""
        pass

    def run(self):
        """Execute a simulation run using the parameters already provided."""

        if self.solver not in self.solverlist:
            raise ModelError("Unknown solver!")

        if self.solver == "odeint":
            try:
                self.tresult, self.yresult, self.nok, self.nbad = rk4.odeint(self.ystart, self.tstart,
                    self.tend, self.tstep_wanted, self.tstep_min, self.derivs, self.eps, self.dxsav)
                #Commented out next line to work with array of k values
                #self.yresult = N.hsplit(self.yresult, self.yresult.shape[1])
            except rk4.SimRunError:
                raise
            except StandardError, e:
                #raise ModelError("Error running odeint", self.tresult, self.yresult)
                raise

        if self.solver == "rkdriver_dumb":
            #Loosely estimate number of steps based on requested step size
            nstep = N.ceil((self.tend - self.tstart)/self.tstep_wanted)
            try:
                self.tresult, self.yresult = rk4.rkdriver_dumb(self.ystart, self.tstart, self.tend, nstep, self.derivs)
            except StandardError, e:
                #raise ModelError("Error running rkdriver_dumb", self.tresult, self.yresult)
                raise

        #Aggregrate results and calling parameters into results list
        callingparams = (self.ystart, self.tstart, self.tend, self.tstep_wanted, self.tstep_min,
                            self.eps, self.dxsav, self.solver, datetime.datetime.now() )

        self.resultlist.append([callingparams, self.tresult, self.yresult])

        self.runcount += 1

        return

    def argstring(self):
        a = r"; Arguments: ystart="+ str(self.ystart) + r", tstart=" + str(self.tstart)
        a += r", tend=" + str(self.tend) + r", mass=" + str(self.mass)
        return a

    def plotresults(self, saveplot = False):
        """Plot results of simulation run on a graph."""

        if self.runcount == 0:
            raise ModelError("Model has not been run yet, cannot plot results!", self.tresult, self.yresult)

        P.plot(self.tresult, self.yresult)
        P.xlabel(self.tname)
        P.ylabel(self.ynames[0])
        P.title(self.plottitle + self.argstring())
        P.show()
        return

    def saveallresults(self, filename=None):
        """Tries to save file as a pickled object in directory results."""

        now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        if not filename:
            filename = "./results/run" + now

        if os.path.isdir(os.path.dirname(filename)):
            if os.path.isfile(filename):
               raise IOError("File already exists!")
        else:
            raise IOError("Directory 'results' does not exist")

        try:
            resultsfile = open(filename, "w")
            try:
                pickle.dump(self.resultlist, resultsfile)
            finally:
                resultsfile.close()
        except IOError:
            raise

    def loadresults(self, filename):
        """Loads results from a file and appends them to current results list."""

        if not os.path.isfile(filename):
            raise IOError("File does not exist!")

        try:
            resultsfile = open(filename, "r")
            try:
                newresults = pickle.load(resultsfile)
                self.resultlist.extend(newresults)
            finally:
                resultsfile.close()
        except IOError:
                raise

class TestModel(CosmologicalModel):
    """Test class defining a very simple function"""
    def __init__(self, ystart=1.0, tstart=0.0, tend=1.0, tstep_wanted=0.01, tstep_min=0.001):
        CosmologicalModel.__init__(self, ystart, tstart, tend, tstep_wanted, tstep_min)

        self.plottitle = r"TestModel: $\frac{dy}{dt} = t$"
        self.tname = "Time"
        self.ynames = ["Simple y"]

    def derivs(self, t, y):
        """Very simple set of ODEs"""
        return N.cos(t)

class BasicBgModel(CosmologicalModel):
    """Basic model with background equations
        Array of dependent variables y is given by:

       y[0] - \phi_0 : Background inflaton
       y[1] - d\phi_0/d\eta : First deriv of \phi
       y[2] - a : Scale Factor
    """

    def __init__(self, ystart=N.array([0.1,0.1,0.1]), tstart=0.0, tend=120.0, tstep_wanted=0.02, tstep_min=0.0001):
        CosmologicalModel.__init__(self, ystart, tstart, tend, tstep_wanted, tstep_min)

        #Mass of inflaton in Planck masses
        self.mass = 1.0

        self.plottitle = "Basic Cosmological Model"
        self.tname = "Conformal time"
        self.ynames = [r"Inflaton $\phi$", "", r"Scale factor $a$"]

    def derivs(self, t, y):
        """Basic background equations of motion.
            dydx[0] = dy[0]/d\eta etc"""

        #Use inflaton mass
        mass2 = self.mass**2

        #potential U = 1/2 m^2 \phi^2
        U = 0.5*(mass2)*(y[0]**2)
        #deriv of potential wrt \phi
        dUdphi =  (mass2)*y[0]

        #factor in eom [1/3 a^2 U_0]^{1/2}
        Ufactor = N.sqrt((1.0/3.0)*(y[2]**2)*U)

        #Set derivatives
        dydx = N.zeros(3)

        #d\phi_0/d\eta = y_1
        dydx[0] = y[1]

        #dy_1/d\eta = -2
        dydx[1] = -2*Ufactor*y[1] - (y[2]**2)*dUdphi

        #da/d\eta = [1/3 a^2 U_0]^{1/2}*a
        dydx[2] = Ufactor*y[2]

        return dydx

    def plotresults(self, saveplot = False):
        """Plot results of simulation run on a graph."""

        if self.runcount == 0:
            raise ModelError("Model has not been run yet, cannot plot results!", self.tresult, self.yresult)

        P.plot(self.tresult, self.yresult[0], self.tresult, self.yresult[2])
        P.xlabel(self.tname)
        P.ylabel("")
        P.legend((self.ynames[0], self.ynames[2]))
        P.title(self.plottitle + self.argstring())
        P.show()
        return

class FirstOrderModel(CosmologicalModel):
    """First order model with background equations
        Array of dependent variables y is given by:

       y[0] - \phi_0 : Background inflaton
       y[1] - d\phi_0/d\eta : First deriv of \phi
       y[2] - a : Scale Factor
    """

    def __init__(self, ystart=(N.array([[0.1],[0.1],[0.1],[0.1],[0.1]])*N.ones((5,10))), tstart=0.0, tend=120.0, tstep_wanted=0.02, tstep_min=0.0001):
        CosmologicalModel.__init__(self, ystart, tstart, tend, tstep_wanted, tstep_min)

        #Mass of inflaton in Planck masses
        self.mass = 1.0

        self.plottitle = "First Order Model"
        self.tname = "Conformal time"
        self.ynames = [r"Inflaton $\phi$", "", r"Scale factor $a$"]

    def derivs(self, t, y):
        """First order equations of motion.
            dydx[0] = dy[0]/d\eta etc"""

        #Let k roam for a start
        k = 10**(N.arange(10.0)-8)

        #Use inflaton mass
        mass2 = self.mass**2

        #potential U = 1/2 m^2 \phi^2
        U = 0.5*(mass2)*(y[0]**2)
        #deriv of potential wrt \phi
        dUdphi =  (mass2)*y[0]
        #2nd deriv
        d2Udphi2 = mass2

        #Things we only want to calculate once
        #a^2
        asq = y[4]**2

        #factor in eom \mathcal{H} = [1/3 a^2 U_0]^{1/2}
        H = N.sqrt((1.0/3.0)*(asq)*U + 0.5*(y[1]**2))

        #Set derivatives
        dydx = N.zeros((5,10))

        #d\phi_0/d\eta = y_1
        dydx[0] = y[1]

        #d^2phi/d\eta^2 = -2Hphi^prime -a^2U_,phi
        dydx[1] = -2*H*y[1] - asq*dUdphi

        #dy_2/d\eta = \delta\phi_1^prime
        dydx[2] = y[3]

        #delta\phi^prime^prime
        dydx[3] = -2*H*y[3] - (k**2)*y[2] - asq*d2Udphi2*y[2]

        #da/d\eta = [1/3 a^2 U_0]^{1/2}*a
        dydx[4] = H*y[2]

        return dydx

    def plotresults(self, saveplot = False):
        """Plot results of simulation run on a graph."""

        if self.runcount == 0:
            raise ModelError("Model has not been run yet, cannot plot results!", self.tresult, self.yresult)

        P.plot(self.tresult, self.yresult[0], self.tresult, self.yresult[2])
        P.xlabel(self.tname)
        P.ylabel("")
        P.legend((self.ynames[0], self.ynames[2]))
        P.title(self.plottitle + self.argstring())
        P.show()
       <commit_msg>Added 3d plot, and changed k to be a data attribute.<commit_after>"""Cosmological Model simulations by Ian Huston
    $Id: cosmomodels.py,v 1.11 2008/04/24 16:50:45 ith Exp $

    Provides generic class CosmologicalModel that can be used as a base for explicit models."""

from __future__ import division # Get rid of integer division problems, i.e. 1/2=0
import numpy as N
import pylab as P
from matplotlib import axes3d
import rk4
import sys
import os.path
import datetime
import pickle

class ModelError(StandardError):
    """Generic error for model simulating. Attributes include current results stack."""

    def __init__(self, expression, tresult=None, yresult=None):
        self.expression = expression
        self.tresult = tresult
        self.yresult = yresult

class CosmologicalModel:
    """Generic class for cosmological model simulations.
    Has no derivs function to pass to ode solver, but does have
    plotting function and initial conditions check.

    Results can be saved in a pickled file as a list of tuples of the following
    structure:
       resultset = (callingparams, tresult, yresult)

       callingparams is formatted as in the function callingparams(self) below
    """

    solverlist = ["odeint", "rkdriver_dumb"]

    def __init__(self, ystart, tstart, tend, tstep_wanted, tstep_min, eps=1.0e-6, dxsav=0.0, solver="odeint"):
        """Initialize model variables, some with default values. Default solver is odeint."""

        self.ystart = ystart

        if tstart < tend:
            self.tstart, self.tend = tstart, tend
        elif tstart==tend:
            raise ValueError, "End time is the same as start time!"
        else:
            raise ValueError, "Ending time is before starting time!"

        if tstep_wanted >= tstep_min:
            self.tstep_wanted, self.tstep_min = tstep_wanted, tstep_min
        else:
            raise ValueError, "Desired time step is smaller than specified minimum!"

        if eps < 1:
            self.eps = eps
        else:
            raise ValueError, "Not enough accuracy! Change eps < 1."

        if dxsav >=0.0:
            self.dxsav = dxsav
        else:
            raise ValueError, "Data saving step must be 0 or positive!"

        if solver in self.solverlist:
            self.solver = solver
        else:
            raise ValueError, "Solver not recognized!"

        self.tresult = None #Will hold last time result
        self.yresult = None #Will hold array of last y results
        self.runcount = 0 #How many times has the model been run?
        self.resultlist = [] #List of all completed results.

        self.plottitle = "A generic Cosmological Model"
        self.tname = "Time"
        self.ynames = ["First dependent variable"]

    def derivs(self, t, yarray):
        """Return an array of derivatives of the dependent variables yarray at timestep t"""
        pass

    def run(self):
        """Execute a simulation run using the parameters already provided."""

        if self.solver not in self.solverlist:
            raise ModelError("Unknown solver!")

        if self.solver == "odeint":
            try:
                self.tresult, self.yresult, self.nok, self.nbad = rk4.odeint(self.ystart, self.tstart,
                    self.tend, self.tstep_wanted, self.tstep_min, self.derivs, self.eps, self.dxsav)
                #Commented out next line to work with array of k values
                #self.yresult = N.hsplit(self.yresult, self.yresult.shape[1])
            except rk4.SimRunError:
                raise
            except StandardError, e:
                #raise ModelError("Error running odeint", self.tresult, self.yresult)
                raise

        if self.solver == "rkdriver_dumb":
            #Loosely estimate number of steps based on requested step size
            nstep = N.ceil((self.tend - self.tstart)/self.tstep_wanted)
            try:
                self.tresult, self.yresult = rk4.rkdriver_dumb(self.ystart, self.tstart, self.tend, nstep, self.derivs)
            except StandardError, e:
                #raise ModelError("Error running rkdriver_dumb", self.tresult, self.yresult)
                raise

        #Aggregrate results and calling parameters into results list
        callingparams = self.callingparams()

        self.resultlist.append([callingparams, self.tresult, self.yresult])

        self.runcount += 1

        return

    def callingparams(self):
        """Returns list of parameters to save with results."""
        return (self.ystart, self.tstart, self.tend, self.tstep_wanted, self.tstep_min,
                            self.eps, self.dxsav, self.solver, datetime.datetime.now() )

    def argstring(self):
        a = r"; Arguments: ystart="+ str(self.ystart) + r", tstart=" + str(self.tstart)
        a += r", tend=" + str(self.tend) + r", mass=" + str(self.mass)
        return a

    def plotresults(self, saveplot = False):
        """Plot results of simulation run on a graph."""

        if self.runcount == 0:
            raise ModelError("Model has not been run yet, cannot plot results!", self.tresult, self.yresult)

        P.plot(self.tresult, self.yresult)
        P.xlabel(self.tname)
        P.ylabel(self.ynames[0])
        P.title(self.plottitle + self.argstring())
        P.show()
        return

    def saveallresults(self, filename=None):
        """Tries to save file as a pickled object in directory 'results'."""

        now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        if not filename:
            filename = "./results/run" + now

        if os.path.isdir(os.path.dirname(filename)):
            if os.path.isfile(filename):
               raise IOError("File already exists!")
        else:
            raise IOError("Directory 'results' does not exist")

        try:
            resultsfile = open(filename, "w")
            try:
                pickle.dump(self.resultlist, resultsfile)
            finally:
                resultsfile.close()
        except IOError:
            raise

    def loadresults(self, filename):
        """Loads results from a file and appends them to current results list."""

        if not os.path.isfile(filename):
            raise IOError("File does not exist!")

        try:
            resultsfile = open(filename, "r")
            try:
                newresults = pickle.load(resultsfile)
                #The following doesn't check to see if type of object is right!
                self.resultlist.extend(newresults)
                self.runcount = len(self.resultlist)
            finally:
                resultsfile.close()
        except IOError:
                raise

class TestModel(CosmologicalModel):
    """Test class defining a very simple function"""
    def __init__(self, ystart=1.0, tstart=0.0, tend=1.0, tstep_wanted=0.01, tstep_min=0.001):
        CosmologicalModel.__init__(self, ystart, tstart, tend, tstep_wanted, tstep_min)

        self.plottitle = r"TestModel: $\frac{dy}{dt} = t$"
        self.tname = "Time"
        self.ynames = ["Simple y"]

    def derivs(self, t, y):
        """Very simple set of ODEs"""
        return N.cos(t)

class BasicBgModel(CosmologicalModel):
    """Basic model with background equations
        Array of dependent variables y is given by:

       y[0] - \phi_0 : Background inflaton
       y[1] - d\phi_0/d\eta : First deriv of \phi
       y[2] - a : Scale Factor
    """

    def __init__(self, ystart=N.array([0.1,0.1,0.1]), tstart=0.0, tend=120.0, tstep_wanted=0.02, tstep_min=0.0001):
        CosmologicalModel.__init__(self, ystart, tstart, tend, tstep_wanted, tstep_min)

        #Mass of inflaton in Planck masses
        self.mass = 1.0

        self.plottitle = "Basic Cosmological Model"
        self.tname = "Conformal time"
        self.ynames = [r"Inflaton $\phi$", "", r"Scale factor $a$"]

    def derivs(self, t, y):
        """Basic background equations of motion.
            dydx[0] = dy[0]/d\eta etc"""

        #Use inflaton mass
        mass2 = self.mass**2

        #potential U = 1/2 m^2 \phi^2
        U = 0.5*(mass2)*(y[0]**2)
        #deriv of potential wrt \phi
        dUdphi =  (mass2)*y[0]

        #factor in eom [1/3 a^2 U_0]^{1/2}
        Ufactor = N.sqrt((1.0/3.0)*(y[2]**2)*U)

        #Set derivatives
        dydx = N.zeros(3)

        #d\phi_0/d\eta = y_1
        dydx[0] = y[1]

        #dy_1/d\eta = -2
        dydx[1] = -2*Ufactor*y[1] - (y[2]**2)*dUdphi

        #da/d\eta = [1/3 a^2 U_0]^{1/2}*a
        dydx[2] = Ufactor*y[2]

        return dydx

    def plotresults(self, saveplot = False):
        """Plot results of simulation run on a graph."""

        if self.runcount == 0:
            raise ModelError("Model has not been run yet, cannot plot results!", self.tresult, self.yresult)

        P.plot(self.tresult, self.yresult[0], self.tresult, self.yresult[2])
        P.xlabel(self.tname)
        P.ylabel("")
        P.legend((self.ynames[0], self.ynames[2]))
        P.title(self.plottitle + self.argstring())
        P.show()
        return

class FirstOrderModel(CosmologicalModel):
    """First order model with background equations
        Array of dependent variables y is given by:

       y[0] - \phi_0 : Background inflaton
       y[1] - d\phi_0/d\eta : First deriv of \phi
       y[2] - \delta\varphi_1 : First order perturbation
       y[3] - \delta\varphi_1^\prime : Derivative of first order perturbation
       y[4] - a : Scale Factor

       Results can be saved in a pickled file as a list of tuples of the following
       structure:
       resultset = (callingparams, tresult, yresult)

       callingparams is formatted as in the function callingparams(self) below

    """

    def __init__(self, ystart=None, tstart=0.0, tend=120.0, tstep_wanted=0.02, tstep_min=0.0001, k=None):
        """Initialize all variables and call ancestor's __init__ method."""

        #Let k roam for a start
        if k is None:
            self.k = 10**(N.arange(10.0)-8)
        else:
            self.k = k

        #Initial conditions for each of the variables.
        if ystart is None:
            ystart = (N.array([[0.1],[0.1],[0.1],[0.1],[0.1]])*N.ones((5,len(self.k))))

        CosmologicalModel.__init__(self, ystart, tstart, tend, tstep_wanted, tstep_min)

        #Mass of inflaton in Planck masses
        self.mass = 1.0

        #Text for graphs
        self.plottitle = "First Order Model"
        self.tname = r"$\eta$"
        self.ynames = [r"$\varphi_0$",
                        r"$\varphi_0^\prime$",
                        r"$\delta\varphi_1$",
                        r"$\delta\varphi_1^\prime$",
                        r"$a$"]

    def callingparams(self):
        """Returns list of parameters to save with results."""
        return (self.ystart, self.tstart, self.tend, self.tstep_wanted, self.tstep_min,
                self.k, self.eps, self.dxsav, self.solver, datetime.datetime.now() )

    def derivs(self, t, y):
        """First order equations of motion.
            dydx[0] = dy[0]/d\eta etc"""

        #Use inflaton mass
        mass2 = self.mass**2

        #potential U = 1/2 m^2 \phi^2
        U = 0.5*(mass2)*(y[0]**2)
        #deriv of potential wrt \phi
        dUdphi =  (mass2)*y[0]
        #2nd deriv
        d2Udphi2 = mass2

        #Things we only want to calculate once
        #a^2
        asq = y[4]**2

        #factor in eom \mathcal{H} = [1/3 a^2 U_0]^{1/2}
        H = N.sqrt((1.0/3.0)*(asq)*U + 0.5*(y[1]**2))

        #Set derivatives
        dydx = N.zeros((5,len(self.k)))

        #d\phi_0/d\eta = y_1
        dydx[0] = y[1]

        #d^2phi/d\eta^2 = -2Hphi^prime -a^2U_,phi
        dydx[1] = -2*H*y[1] - asq*dUdphi

        #dy_2/d\eta = \delta\phi_1^prime
        dydx[2] = y[3]

        #delta\phi^prime^prime
        dydx[3] = -2*H*y[3] - (self.k**2)*y[2] - asq*d2Udphi2*y[2]

        #da/d\eta = [1/3 a^2 U_0]^{1/2}*a
        dydx[4] = H*y[4]

        return dydx

    def plotresults(self, saveplot = False):
        """Plot results of simulation run on a graph."""

        if self.runcount == 0:
            raise ModelError("Model has not been run yet, cannot plot results!", self.tresult, self.yresult)

        P.plot(self.tresult, self.yresult[0], self.tresult, self.yresult[2])
        P.xlabel(self.tname)
        P.ylabel("")
        P.legend((self.ynames[0], self.ynames[2]))
        P.title(self.plottitle + self.argstring())
        P.show()
        return

    def plotallks(self, varindex=2, kfunction=None):
        """Plot results from all ks run in a plot where k is plotted by kfunction (e.g. log)."""
        if self.runcount == 0:
            raise ModelError("Model has not been run yet, cannot plot results!", self.tresult, self.yresult)

        x = self.tresult

        fig = P.figure()
        ax = axes3d.Axes3D(fig)
        for index in range(len(self.k)):
            z = self.yresult[:,varindex,index]
            if kfunction is None:
                y = self.k[index]*N.ones(len(x))
            else:
                y = kfunction(self.k[index])*N.ones(len(x))
            ax.plot3D(x,y,z)
        ax.set_xlabel(self.tname)
        if kfunction is None:
            ax.set_ylabel(r"$k$")
        else:
            ax.set_ylabel(r"Function of k: " + kfunction.__name__)
        ax.set_zlabel(self.ynames[varindex])
        P.title(self.plottitle + self.argstring())
        P.show()

        return
        <|endoftext|>


<commit_before>import math
from itertools import izip

from matplotlib import pyplot as plt
import matplotlib.ticker as ticker
import numpy as np

from pandas import DataFrame, datetools, DatetimeIndex, Series
from pandas.tseries.resample import _get_range_edges
from pandas.tseries.frequencies import to_offset, _is_annual, _is_weekly
import pandas.lib as lib
from matplotlib.finance import candlestick,\
             plot_day_summary

import IPython

IPython.core.pylabtools.figsize(15, 10)

CURRENT_FIGURE = None

class DateLocator(ticker.Locator):
    """
    Place a tick on every multiple of some base number of points
    plotted, eg on every 5th point.  It is assumed that you are doing
    index plotting; ie the axis is 0, len(data).  This is mainly
    useful for x ticks.
    """
    def __init__(self, index, min_ticks=5):
        'place ticks on the i-th data points where (i-offset)%base==0'
        self.index = index
        self.min_ticks = min_ticks
        self.index_type = None

    def __call__(self):
        'Return the locations of the ticks'
        vmin, vmax = self.axis.get_view_interval()
        xticks = self._process(vmin, vmax)
        return self.raise_if_exceeds(xticks)

    def _process(self, vmin, vmax):
        vmin = int(math.ceil(vmin))
        vmax = int(math.floor(vmax)) or len(self.index) - 1

        dmin = self.index[vmin]
        dmax = self.index[vmax]

        byIndex = self.infer_scale(dmin, dmax)
        self.index_type = byIndex

        sub_index = self.index[vmin:vmax]

        xticks = self.generate_xticks(sub_index, byIndex)
        return xticks

    def infer_scale(self, dmin, dmax):
        delta = datetools.relativedelta(dmax, dmin)

        numYears = (delta.years * 1.0)
        numMonths = (numYears * 12.0) + delta.months
        numDays = (numMonths * 31.0) + delta.days
        numWeeks = numDays // 7
        numHours = (numDays * 24.0) + delta.hours
        numMinutes = (numHours * 60.0) + delta.minutes
        nums = [('AS', numYears), ('M', numMonths), ('W', numWeeks), ('D', numDays), ('H', numHours),
                ('15min', numMinutes)]
        byIndex = None
        for key, num in nums:
            if num > self.min_ticks:
                byIndex = key
                break

        return byIndex

    def generate_xticks(self, index, freq):
        """
            Ticks are really just the bin edges.
        """
        start = index[0]
        end = index[-1]
        start, end = _get_range_edges(index, offset=freq, closed='right')
        ind = DatetimeIndex(start=start, end=end, freq=freq)
        bins = lib.generate_bins_dt64(index.asi8, ind.asi8, closed='right')
        bins = np.unique(bins)
        return bins

class DateFormatter(object):
    def __init__(self, index):
        self.index = index
        self._locator = None

    def format_date(self, x, pos=None):
        thisind = np.clip(int(x+0.5), 0, len(self.index)-1)
        date = self.index[thisind]
        index_type = self._locator.index_type
        if index_type == 'T':
            return date.strftime('%H:%M %m/%d/%y')
        if index_type == 'H':
            return date.strftime('%H:%M %m/%d/%y')
        if index_type in ['D', 'W']:
            return date.strftime('%m/%d/%Y')
        if index_type == 'M':
            return date.strftime('%m/%d/%Y')
        return date.strftime('%m/%d/%Y %H:%M')

    def set_formatter(self, ax):
        self._locator = DateLocator(self.index)
        ax.xaxis.set_major_locator(self._locator)
        ax.xaxis.set_major_formatter(ticker.FuncFormatter(self.format_date))
        ax.xaxis.grid(True)

def gcf():
    return CURRENT_FIGURE

def scf(figure):
    global CURRENT_FIGURE
    CURRENT_FIGURE = figure

class Figure(object):
    def __init__(self, rows=1, cols=1, skip_na=True):
        self.figure = plt.figure()
        self.rows = rows
        self.cols = cols
        self.ax = None
        self.axnum = None
        self.graphers = {}
        self.grapher = None
        self.skip_na = skip_na
        if rows == 1:
            self.set_ax(1)
        scf(self)

    def get_ax(self, axnum):
        if axnum not in self.graphers:
            return None
        return self.graphers[axnum].ax

    def _set_ax(self, axnum):
        self.axnum = axnum
        grapher = self.graphers[axnum]
        self.grapher = grapher
        self.ax = grapher.ax

    def init_ax(self, axnum, sharex=None, skip_na=None):
        if skip_na is None:
            skip_na = self.skip_na
        shared_df = None
        if type(sharex) == int:
            shared_df = self.graphers[sharex].df
        ax = plt.subplot(self.rows, self.cols, axnum)
        self.graphers[axnum] = Grapher(ax, skip_na, sharex=shared_df)

    def set_ax(self, axnum, sharex=None, skip_na=None):
        if self.get_ax(axnum) is None:
            self.init_ax(axnum ,sharex, skip_na)
        self._set_ax(axnum)

    def plot(self, name, series, index=None, fillna=None, **kwargs):
        if self.ax is None:
            print('NO AX set')
            return
        self.figure.autofmt_xdate()
        self.grapher.plot(name, series, index, fillna, **kwargs)

    def candlestick(self, *args, **kwargs):
        if self.ax is None:
            print('NO AX set')
            return
        self.figure.autofmt_xdate()
        self.grapher.candlestick(*args, **kwargs)

    def plot_markers(self, name, series, source=None, xindex=None, **kwargs):
        if self.ax is None:
            print('NO AX set')
            return
        self.grapher.plot_markers(name, series, source, xindex, **kwargs)

    def clear(self, axnum=None):
        if axnum is None:
            axnum = self.axnum

        grapher = self.graphers[axnum]
        ax = grapher.ax
        ax.clear()
        del self.graphers[axnum]
        self.ax = None
        self.set_ax(axnum)

class Grapher(object):
    def __init__(self, ax, skip_na=True, sharex=None):
        self.df = None
        self.formatter = None
        self.ax = ax
        self.skip_na = skip_na
        self.sharex = sharex

    def is_datetime(self):
        return self.df.index.inferred_type in ('datetime', 'date', 'datetime64')

    def plot(self, name, series, index=None, fillna=None, **kwargs):
        if self.sharex is not None:
            series = series.reindex(self.sharex.index, method=fillna)

        if self.df is None:
            self.df = DataFrame(index=series.index)

        is_datetime = self.is_datetime()
        if is_datetime:
            self.setup_datetime(self.df.index)

        # we add to df to reindex
        # not sure how to handle if we start with a
        # smaller index. i.e. hourly then trying to plot minute

        self.df[name] = series
        if name is not None:
            kwargs['label'] = name

        xax = self.df[name].index
        if self.skip_na and is_datetime:
            xax = np.arange(len(self.df))
            self.formatter.index = self.df.index

        plot_series = self.df[name]
        if fillna:
            plot_series = plot_series.fillna(method=fillna)
        self.ax.plot(xax, plot_series, **kwargs)
        plt.legend(loc=0)

    def setup_datetime(self, index):
        is_datetime = self.is_datetime()
        if self.formatter is None and self.skip_na and is_datetime:
            self.formatter = DateFormatter(index)
            self.formatter.set_formatter(self.ax)

    def candlestick(self, df, width=0.3):
        """
        """
        xax = np.arange(len(df.index))
        quotes = izip(xax, df.open, df.close, df.high, df.low)
        ax = self.ax
        self.df = df
        self.setup_datetime(df.index)
        candlestick(ax, quotes, width=width, colorup='g')

    def plot_markers(self, name, series, source=None, xindex=None, **kwargs):
        if source is not None:
            series = process_signal(series, source)
        props = {}
        props['linestyle'] = 'None'
        props['marker'] = 'o'
        props['markersize'] = 10
        props.update(kwargs)

        if xindex is not None:
            series.index = xindex

        self.plot(name, series, **props)

def plot_markers(series, source=None, xindex=None, **kwargs):
    if source is not None:
        series = process_signal(series, source)
    props = {}
    props['linestyle'] = 'None'
    props['marker'] = 'o'
    props['markersize'] = 10
    props.update(kwargs)

    index = series.index

    if xindex is not None:
        index = xindex

    plt.plot(index, series, **props)

def process_signal(series, source):
    temp = series.copy()
    temp[temp.nonzero()[0]] = 1
    temp *= source
    return temp[temp.nonzero()[0]]

def remove_series(label, axes=None):
    """ Based on label name, remove a line """
    if axes is None:
        axes = plt.axes()
    for line in axes.lines:
        if line.get_label() == label:
            line.remove()

def clear_chart():
    lines = plt.axes().lines
    while True:
        try:
            lines.pop(0)
        except:
            break
    plt.legend()

def remove_last_plot():
    lines = plt.axes().lines
    lines.pop(len(lines)-1)

# Monkey Patches
def series_plot(self, label=None, *args, **kwargs):
    label = label or kwargs.get('label')
    label = label and label or self.name
    try:
        prefix = kwargs.pop('prefix')
        label = prefix +' '+label
    except:
        pass
    fig = gcf()
    fig.plot(label, self, *args, **kwargs)

Series.fplot = series_plot

def df_plot(self, *args, **kwargs):
    if len(self.columns) > 20:
        print 'you crazy? too many columns'
        return;
    for col in self.columns:
        series = self[col]
        series.fplot(*args, **kwargs)

DataFrame.fplot = df_plot

def ohlc_plot(self, width=0.3, *args, **kwargs):
    fig = gcf()
    fig.candlestick(self)

DataFrame.ohlc_plot = ohlc_plot
<commit_msg>added fplot to TimeSeries<commit_after>import math
from itertools import izip

from matplotlib import pyplot as plt
import matplotlib.ticker as ticker
import numpy as np

from pandas import DataFrame, datetools, DatetimeIndex, Series, TimeSeries
from pandas.tseries.resample import _get_range_edges
from pandas.tseries.frequencies import to_offset, _is_annual, _is_weekly
import pandas.lib as lib
from matplotlib.finance import candlestick,\
             plot_day_summary

import IPython

IPython.core.pylabtools.figsize(15, 10)

CURRENT_FIGURE = None

class DateLocator(ticker.Locator):
    """
    Place a tick on every multiple of some base number of points
    plotted, eg on every 5th point.  It is assumed that you are doing
    index plotting; ie the axis is 0, len(data).  This is mainly
    useful for x ticks.
    """
    def __init__(self, index, min_ticks=5):
        'place ticks on the i-th data points where (i-offset)%base==0'
        self.index = index
        self.min_ticks = min_ticks
        self.index_type = None

    def __call__(self):
        'Return the locations of the ticks'
        vmin, vmax = self.axis.get_view_interval()
        xticks = self._process(vmin, vmax)
        return self.raise_if_exceeds(xticks)

    def _process(self, vmin, vmax):
        vmin = int(math.ceil(vmin))
        vmax = int(math.floor(vmax)) or len(self.index) - 1

        dmin = self.index[vmin]
        dmax = self.index[vmax]

        byIndex = self.infer_scale(dmin, dmax)
        self.index_type = byIndex

        sub_index = self.index[vmin:vmax]

        xticks = self.generate_xticks(sub_index, byIndex)
        return xticks

    def infer_scale(self, dmin, dmax):
        delta = datetools.relativedelta(dmax, dmin)

        numYears = (delta.years * 1.0)
        numMonths = (numYears * 12.0) + delta.months
        numDays = (numMonths * 31.0) + delta.days
        numWeeks = numDays // 7
        numHours = (numDays * 24.0) + delta.hours
        numMinutes = (numHours * 60.0) + delta.minutes
        nums = [('AS', numYears), ('M', numMonths), ('W', numWeeks), ('D', numDays), ('H', numHours),
                ('15min', numMinutes)]
        byIndex = None
        for key, num in nums:
            if num > self.min_ticks:
                byIndex = key
                break

        return byIndex

    def generate_xticks(self, index, freq):
        """
            Ticks are really just the bin edges.
        """
        start = index[0]
        end = index[-1]
        start, end = _get_range_edges(index, offset=freq, closed='right')
        ind = DatetimeIndex(start=start, end=end, freq=freq)
        bins = lib.generate_bins_dt64(index.asi8, ind.asi8, closed='right')
        bins = np.unique(bins)
        return bins

class DateFormatter(object):
    def __init__(self, index):
        self.index = index
        self._locator = None

    def format_date(self, x, pos=None):
        thisind = np.clip(int(x+0.5), 0, len(self.index)-1)
        date = self.index[thisind]
        index_type = self._locator.index_type
        if index_type == 'T':
            return date.strftime('%H:%M %m/%d/%y')
        if index_type == 'H':
            return date.strftime('%H:%M %m/%d/%y')
        if index_type in ['D', 'W']:
            return date.strftime('%m/%d/%Y')
        if index_type == 'M':
            return date.strftime('%m/%d/%Y')
        return date.strftime('%m/%d/%Y %H:%M')

    def set_formatter(self, ax):
        self._locator = DateLocator(self.index)
        ax.xaxis.set_major_locator(self._locator)
        ax.xaxis.set_major_formatter(ticker.FuncFormatter(self.format_date))
        ax.xaxis.grid(True)

def gcf():
    return CURRENT_FIGURE

def scf(figure):
    global CURRENT_FIGURE
    CURRENT_FIGURE = figure

class Figure(object):
    def __init__(self, rows=1, cols=1, skip_na=True):
        self.figure = plt.figure()
        self.rows = rows
        self.cols = cols
        self.ax = None
        self.axnum = None
        self.graphers = {}
        self.grapher = None
        self.skip_na = skip_na
        if rows == 1:
            self.set_ax(1)
        scf(self)

    def get_ax(self, axnum):
        if axnum not in self.graphers:
            return None
        return self.graphers[axnum].ax

    def _set_ax(self, axnum):
        self.axnum = axnum
        grapher = self.graphers[axnum]
        self.grapher = grapher
        self.ax = grapher.ax

    def init_ax(self, axnum, sharex=None, skip_na=None):
        if skip_na is None:
            skip_na = self.skip_na
        shared_df = None
        if type(sharex) == int:
            shared_df = self.graphers[sharex].df
        ax = plt.subplot(self.rows, self.cols, axnum)
        self.graphers[axnum] = Grapher(ax, skip_na, sharex=shared_df)

    def set_ax(self, axnum, sharex=None, skip_na=None):
        if self.get_ax(axnum) is None:
            self.init_ax(axnum ,sharex, skip_na)
        self._set_ax(axnum)

    def plot(self, name, series, index=None, fillna=None, **kwargs):
        if self.ax is None:
            print('NO AX set')
            return
        self.figure.autofmt_xdate()
        self.grapher.plot(name, series, index, fillna, **kwargs)

    def candlestick(self, *args, **kwargs):
        if self.ax is None:
            print('NO AX set')
            return
        self.figure.autofmt_xdate()
        self.grapher.candlestick(*args, **kwargs)

    def plot_markers(self, name, series, source=None, xindex=None, **kwargs):
        if self.ax is None:
            print('NO AX set')
            return
        self.grapher.plot_markers(name, series, source, xindex, **kwargs)

    def clear(self, axnum=None):
        if axnum is None:
            axnum = self.axnum

        grapher = self.graphers[axnum]
        ax = grapher.ax
        ax.clear()
        del self.graphers[axnum]
        self.ax = None
        self.set_ax(axnum)

class Grapher(object):
    def __init__(self, ax, skip_na=True, sharex=None):
        self.df = None
        self.formatter = None
        self.ax = ax
        self.skip_na = skip_na
        self.sharex = sharex

    def is_datetime(self):
        return self.df.index.inferred_type in ('datetime', 'date', 'datetime64')

    def plot(self, name, series, index=None, fillna=None, **kwargs):
        if self.sharex is not None:
            series = series.reindex(self.sharex.index, method=fillna)

        if self.df is None:
            self.df = DataFrame(index=series.index)

        is_datetime = self.is_datetime()
        if is_datetime:
            self.setup_datetime(self.df.index)

        # we add to df to reindex
        # not sure how to handle if we start with a
        # smaller index. i.e. hourly then trying to plot minute

        self.df[name] = series
        if name is not None:
            kwargs['label'] = name

        xax = self.df[name].index
        if self.skip_na and is_datetime:
            xax = np.arange(len(self.df))
            self.formatter.index = self.df.index

        plot_series = self.df[name]
        if fillna:
            plot_series = plot_series.fillna(method=fillna)
        self.ax.plot(xax, plot_series, **kwargs)
        plt.legend(loc=0)

    def setup_datetime(self, index):
        is_datetime = self.is_datetime()
        if self.formatter is None and self.skip_na and is_datetime:
            self.formatter = DateFormatter(index)
            self.formatter.set_formatter(self.ax)

    def candlestick(self, df, width=0.3):
        """
        """
        xax = np.arange(len(df.index))
        quotes = izip(xax, df.open, df.close, df.high, df.low)
        ax = self.ax
        self.df = df
        self.setup_datetime(df.index)
        candlestick(ax, quotes, width=width, colorup='g')

    def plot_markers(self, name, series, source=None, xindex=None, **kwargs):
        if source is not None:
            series = process_signal(series, source)
        props = {}
        props['linestyle'] = 'None'
        props['marker'] = 'o'
        props['markersize'] = 10
        props.update(kwargs)

        if xindex is not None:
            series.index = xindex

        self.plot(name, series, **props)

def plot_markers(series, source=None, xindex=None, **kwargs):
    if source is not None:
        series = process_signal(series, source)
    props = {}
    props['linestyle'] = 'None'
    props['marker'] = 'o'
    props['markersize'] = 10
    props.update(kwargs)

    index = series.index

    if xindex is not None:
        index = xindex

    plt.plot(index, series, **props)

def process_signal(series, source):
    temp = series.copy()
    temp[temp.nonzero()[0]] = 1
    temp *= source
    return temp[temp.nonzero()[0]]

def remove_series(label, axes=None):
    """ Based on label name, remove a line """
    if axes is None:
        axes = plt.axes()
    for line in axes.lines:
        if line.get_label() == label:
            line.remove()

def clear_chart():
    lines = plt.axes().lines
    while True:
        try:
            lines.pop(0)
        except:
            break
    plt.legend()

def remove_last_plot():
    lines = plt.axes().lines
    lines.pop(len(lines)-1)

# Monkey Patches
def series_plot(self, label=None, *args, **kwargs):
    label = label or kwargs.get('label')
    label = label and label or self.name
    try:
        prefix = kwargs.pop('prefix')
        label = prefix +' '+label
    except:
        pass
    fig = gcf()
    fig.plot(label, self, *args, **kwargs)

Series.fplot = series_plot
TimeSeries.fplot = series_plot

def df_plot(self, *args, **kwargs):
    if len(self.columns) > 20:
        print 'you crazy? too many columns'
        return;
    for col in self.columns:
        series = self[col]
        series.fplot(*args, **kwargs)

DataFrame.fplot = df_plot

def ohlc_plot(self, width=0.3, *args, **kwargs):
    fig = gcf()
    fig.candlestick(self)

DataFrame.ohlc_plot = ohlc_plot
<|endoftext|>


<commit_before>from string import upper, replace
import re

import utils
import S57names

lst_log = [] # Collector list for things to log ...

## Defining all the layer names that are to be found in a NIS
lstNISlayers = ['AidsToNavigationP',
                'CoastlineA', 'CoastlineL', 'CoastlineP',
                'CulturalFeaturesA', 'CulturalFeaturesL', 'CulturalFeaturesP',
                'DangersA', 'DangersL', 'DangersP',
                'DepthsA', 'DepthsL',
                'IceFeaturesA',
                'MetaDataA', 'MetaDataL', 'MetaDataP',
                'MilitaryFeaturesA', 'MilitaryFeaturesP',
                'NaturalFeaturesA', 'NaturalFeaturesL', 'NaturalFeaturesP',
                'OffshoreInstallationsA', 'OffshoreInstallationsL', 'OffshoreInstallationsP',
                'PortsAndServicesA', 'PortsAndServicesL', 'PortsAndServicesP',
                'RegulatedAreasAndLimitsA', 'RegulatedAreasAndLimitsL', 'RegulatedAreasAndLimitsP',
                'SeabedA', 'SeabedL', 'SeabedP',
                'SoundingsP',
                'TidesAndVariationsA', 'TidesAndVariationsL', 'TidesAndVariationsP',
                'TracksAndRoutesA', 'TracksAndRoutesL', 'TracksAndRoutesP',
                'UserDefinedFeaturesA', 'UserDefinedFeaturesL', 'UserDefinedFeaturesP']

class Rule:
    def __init__(self, theid, title, mode, fc, fcsubtype, condition, fixorlog, fixvalue):
        # The ID
        self.id = theid
        # Title
        self.title = title
        # Mode
        self.mode = upper(mode)
        if self.mode != "SQL":
            utils.log("Warning, rule {}: Only SQL mode is supported, unknown mode {}.".format(self.id, mode))
        # FC - self.fclist is a list, possibly with only one element
        if(fc=="*"):
            self.fclist = lstNISlayers
            if(fcsubtype != "*"):
                utils.log("Warning, rule {}: Feature class is *, but feature class subtype is {}.".format(self.id, fcsubtype))
        else:
            self.fclist = fc.split(",") # split will return a list, even if there are no commas
        # FCsubtype - self.fcsubtype is a comma-separated string, NOT a list
        fcsubtype = fcsubtype.strip()
        if fcsubtype == "*" or fcsubtype == "":
            self.fcsubtype = "*"
        elif(re.search("[A-Za-z]", fcsubtype) and len(self.fclist)>1): # can't handle s57-fcs with multiple fc
            utils.log("Error, rule {}: Can't handle fc subtype abbreviations ({}) for multiple feature classes ({}).".format(self.id, fcsubtype, fc))
            self.id = -1
            return None
        else:
            if("," in fcsubtype):
                # split the list by comma, convert each to int if needed, and glue with commas again
                self.fcsubtype = ",".join([GetFCSids(fcs, self.fclist[0]) for fcs in fcsubtype.split(",")])
            else:
                self.fcsubtype = GetFCSids(fcsubtype, self.fclist[0])
        # Condition
        self.condition = replace(condition, "!=", "<>")
        if ('"' in self.condition): # double quotes won't work
            if(not "'" in self.condition):
                self.condition = replace(self.condition, '"', "'")
                utils.log("Warning, rule {}: Can't have \"double quotes\" in condition, using 'single quotes' instead.".format(self.id))
            else:
                print "Error, rule {}: There are both \"double quotes\" and 'single quotes' in condition - this will fail.".format(self.id)
                utils.log("Error, rule {}: There are both \"double quotes\" and 'single quotes' in condition - this will fail.".format(self.id))
        # Fix or Log - self.dofix is a bool
        self.dofix = (fixorlog=="FIX")
        self.fixLst = []
        # Fix value
        # - for FIXes, self.fixLst is a list of pairs of fixes, e.g. PLTS_COMP_SCALE=90000,IS_CONFLATE=TRUE
        # - for LOGs, self.fixLst is a list of field names to include in log output
        if self.dofix:
            if fixvalue: # there's probably a cleaner way of doing this looping and cleaning...
                # TODO: recognize a fixvalue which is another column name
                self.fixLst = [fixpair.split("=") for fixpair in fixvalue.split(",")] # split on , and =
                self.fixLst = [[val.strip() for val in fixpair] for fixpair in self.fixLst] # strip whitespace
                for fix in self.fixLst:
#                     fix = [val.strip() for val in fix] # strip whitespace
                    fix[1] = CleanUpFixString(fix[1]) # ugly quote mark removal, recognising None, NULL, UNKNOWN and typecasting int/float
                #print self.fixLst
            else:
                self.dofix = False
                utils.log("Warning, rule {}: FIX with no repair values; treating as LOG.".format(str(self.id)))
                 # if the user didn't supply a fix value, this is more helpful than throwing an error
        elif fixvalue: # list of fields to report on
                self.fixLst = [val.strip() for val in fixvalue.split(',')] # split by comma and strip whitespace
    def GetWhereString(self):
        """Return a string with the WHERE clause for the rule, includes: condition and fcsubtype."""
        where = self.condition
        if(self.fcsubtype != "*"):
            where = where + " AND FCSubtype"
            if "," in self.fcsubtype:
                where = where + " IN (" + self.fcsubtype + ")"
            else:
                where = where + " = " + self.fcsubtype
        return where
    def __repr__(self):
        return "({}; {}; {}; {}; \"{}\"; {}:{})\n".format(\
        str(self.id), str(self.title), str(self.fclist), str(self.fcsubtype),
            self.GetWhereString(), str(self.dofix), str(self.fixLst))
# end class Rule

def GetFCSids(fcsubtype, fc):
    """Return the number (as string) for the fc subtype."""
    fcsubtype = fcsubtype.strip()
    if fcsubtype.isdigit():
        return fcsubtype
    else: # If it's not an integer, it may be an S-57 '6-letter-code'
        fcs_value = S57names.S57ABBFC2FCSNumber(fcsubtype, fc)
        if fcs_value > 0:
            return fcs_value
        fcs_value = S57names.S57ABBFC2FCSNumber(fcsubtype, upper(fc)) # if we didn't find it, convert to uppercase and try again
        if fcs_value > 0:
            return fcs_value
        utils.log("Warning: Can't interpret fcsubtype: {}.".format(fcsubtype))
        return -1

def CleanUpFixString(fixvalue):
    """ If string starts and ends with matching quote marks, remove these; also convert None, NULL (and UNKNOWN), and convert to int/double. """
    # This is ugly: we're stripping quote marks off the fixvalue, even when it's a string.
    # They're required in the test value, but not allowed in the fix value,
    # so we'll allow the user to enter them in both places, and remove them here.
    if(fixvalue and fixvalue[0] == fixvalue[-1] and (fixvalue[0]=="'" or fixvalue[0]=='"')):
        fixvalue = fixvalue[1:-1]
    #if fixvalue.upper() == "UNKNOWN":
    #    return -32767 # TODO: is this a good idea? other values to accept?
    if fixvalue.upper() == "NULL" or fixvalue.upper() == "NONE":
        return None # make sure to return, the following lines will choke on a None
    if fixvalue.isdigit():
        return int(fixvalue)
    if utils.isfloat(fixvalue): # this will also match on int, so check that first
        return float(fixvalue.replace(",", ".", 1)) # accept either , or . as decimal separator
    return fixvalue

def ReadRules(path):
    """Read rules from a file, and return a list of Rule objects"""
    lst_rules = list()
    try:
        with open(path, 'r') as f:
            for line in f:
                if(not line.strip() or line[0]=="#"):
                    continue
                if(line[0]=="%"):
                    utils.log("ignoring % lines, not implemented yet")
                    continue
                if(line[0]!=":"):
                    utils.log("Warning: ignoring invalid line starting with "+line[0]+" ("+line+")")
                    continue
                if("#" in line):
                    line = line.split("#")[0].strip()
                items = line.split(":")
                if len(items)!=10:
                   utils.log("Warning: Line does not contain the correct number of elements... \n\t"+line.strip()+"\n\t"+repr(items))
                # forget about number 0, since it's always an empty string (nothing in front of the first ':')
                # number 9 is just comments
                ruleid = items[1].strip()
                title = items[2].strip()
                mode = items[3].strip()
                featureclass = items[4].strip()
                fcsubtype = items[5].strip()
                condition = items[6].strip()
                fixorlog = items[7].strip()
                fixvalue = items[8].strip()
                r = Rule(ruleid, title, mode, featureclass, fcsubtype, condition, fixorlog, fixvalue)
                if r.id != -1:
                    lst_rules.append(r)
            print "Done reading rules."
    except IOError, e:
        print e.errno
        print e
        return 101
    return lst_rules
<commit_msg>Update rule_parser.py<commit_after>from string import upper, replace
import re

import utils
import S57names

## Defining all the layer names that are to be found in a NIS
lstNISlayers = ['AidsToNavigationP',
                'CoastlineA', 'CoastlineL', 'CoastlineP',
                'CulturalFeaturesA', 'CulturalFeaturesL', 'CulturalFeaturesP',
                'DangersA', 'DangersL', 'DangersP',
                'DepthsA', 'DepthsL',
                'IceFeaturesA',
                'MetaDataA', 'MetaDataL', 'MetaDataP',
                'MilitaryFeaturesA', 'MilitaryFeaturesP',
                'NaturalFeaturesA', 'NaturalFeaturesL', 'NaturalFeaturesP',
                'OffshoreInstallationsA', 'OffshoreInstallationsL', 'OffshoreInstallationsP',
                'PortsAndServicesA', 'PortsAndServicesL', 'PortsAndServicesP',
                'RegulatedAreasAndLimitsA', 'RegulatedAreasAndLimitsL', 'RegulatedAreasAndLimitsP',
                'SeabedA', 'SeabedL', 'SeabedP',
                'SoundingsP',
                'TidesAndVariationsA', 'TidesAndVariationsL', 'TidesAndVariationsP',
                'TracksAndRoutesA', 'TracksAndRoutesL', 'TracksAndRoutesP',
                'UserDefinedFeaturesA', 'UserDefinedFeaturesL', 'UserDefinedFeaturesP']

class Rule:
    def __init__(self, theid, title, mode, fc, fcsubtype, condition, fixorlog, fixvalue):
        # The ID
        self.id = theid
        # Title
        self.title = title
        # Mode
        self.mode = upper(mode)
        if self.mode != "SQL" and self.mode != "LOVE":
            utils.log("Warning, rule {}: Only SQL and LOVE modes are supported, unknown mode {}.".format(self.id, mode))
            return None
        # FC - self.fclist is a list, possibly with only one element
        if(fc=="*"):
            self.fclist = lstNISlayers
            if(fcsubtype != "*"):
                utils.log("Warning, rule {}: Feature class is *, but feature class subtype is {}.".format(self.id, fcsubtype))
        else:
            self.fclist = fc.split(",") # split will return a list, even if there are no commas
        # FCsubtype - self.fcsubtype is a comma-separated string, NOT a list
        fcsubtype = fcsubtype.strip()
        if fcsubtype == "*" or fcsubtype == "":
            self.fcsubtype = "*"
        elif(re.search("[A-Za-z]", fcsubtype) and len(self.fclist)>1): # can't handle s57-fcs with multiple fc
            utils.log("Error, rule {}: Can't handle fc subtype abbreviations ({}) for multiple feature classes ({}).".format(self.id, fcsubtype, fc))
            self.id = -1
            return None
        else:
            if("," in fcsubtype):
                # split the list by comma, convert each to int if needed, and glue with commas again
                self.fcsubtype = ",".join([GetFCSids(fcs, self.fclist[0]) for fcs in fcsubtype.split(",")])
            else:
                self.fcsubtype = GetFCSids(fcsubtype, self.fclist[0])
                if self.fcsubtype == -1:
                    utils.log("Error, rule {}: Invalid fc or fcsubtype, {}/{}.".format(self.id, fc, fcsubtype))
                    self.id = -1
                    return None
        # Condition
        self.condition = replace(condition, "!=", "<>")
        if ('"' in self.condition): # double quotes won't work
            if(not "'" in self.condition):
                self.condition = replace(self.condition, '"', "'")
                utils.log("Warning, rule {}: Can't have \"double quotes\" in condition, using 'single quotes' instead.".format(self.id))
            else:
                print "Error, rule {}: There are both \"double quotes\" and 'single quotes' in condition - this will fail.".format(self.id)
                utils.log("Error, rule {}: There are both \"double quotes\" and 'single quotes' in condition - this will fail.".format(self.id))
        if (self.mode == 'LOVE'):
            if (not '%' in self.condition):
                utils.log("Error, rule {}: LOVE rule without % character.".format(self.id))
                return None
            if (fixorlog == 'FIX'):
                utils.log("Warning, rule {}: LOVE rule with FIX. Treating as LOG.".format(self.id))
                fixorlog = 'LOG'
            self.condition = [val.strip() for val in condition.split('%')]
            self.condition[1] = [val.strip() for val in self.condition[1][1:-1].split(',') ]
        # Fix or Log - self.dofix is a bool
        self.dofix = (fixorlog=="FIX")
        self.fixLst = []
        # Fix value
        # - for FIXes, self.fixLst is a list of pairs of fixes, e.g. PLTS_COMP_SCALE=90000,IS_CONFLATE=TRUE
        # - for LOGs, self.fixLst is a list of field names to include in log output
        if self.dofix:
            if fixvalue: # there's probably a cleaner way of doing this looping and cleaning...
                # TODO: recognize a fixvalue which is another column name
                self.fixLst = [fixpair.split("=") for fixpair in fixvalue.split(",")] # split on , and =
                self.fixLst = [[val.strip() for val in fixpair] for fixpair in self.fixLst] # strip whitespace
                for fix in self.fixLst:
#                    fix = [val.strip() for val in fix] # strip whitespace
                    fix[1] = CleanUpFixString(fix[1]) # ugly quote mark removal, recognising None, NULL, UNKNOWN and typecasting int/float
                #print self.fixLst
            else:
                self.dofix = False
                utils.log("Warning, rule {}: FIX with no repair values; treating as LOG.".format(str(self.id)))
                # if the user didn't supply a fix value, this is more helpful than throwing an error
        elif fixvalue: # list of fields to report on
            self.fixLst = [val.strip() for val in fixvalue.split(',')] # split by comma and strip whitespace
        # compose Where String
        self.whereString = ''
        if self.fcsubtype != "*":
            fcs = "FCSubtype"
            if "," in self.fcsubtype:
                fcs = fcs + " IN (" + self.fcsubtype + ")"
            else:
                fcs = fcs + " = " + self.fcsubtype
            self.whereString = fcs + " AND "
        if self.mode == 'LOVE':
            # get values that contain a comma, or are individual and invalid
            self.whereString += "(" + self.condition[0] + " NOT IN (" + ','.join(["'"+s+"'" for s in self.condition[1]]) + ", '-32767', NULL) OR " + self.condition[0] + " LIKE '%,%')"
            #self.fixLst.append(self.condition[0])
        else:
            self.whereString += "(" + self.condition + ")"

    def GetWhereString(self):
        """Return a string with the WHERE clause for the rule, includes: condition and fcsubtype."""
        return self.whereString
        #if self.mode == 'LOVE':
        #    return self.condition[0] + " NOT IN (" + ','.join(["'"+s+"'" for s in self.condition[1]]) + ", '-32767') OR " + self.condition[0] + " LIKE '%,%'"
        #    #return self.condition[0] + " LIKE '%,%'" # TODO: takes 14 secs, vs. 27 sec.
        #where = self.condition
        #if(self.fcsubtype != "*"):
        #    fcs = "FCSubtype"
        #    if "," in self.fcsubtype:
        #        fcs = fcs + " IN (" + self.fcsubtype + ")"
        #    else:
        #        fcs = fcs + " = " + self.fcsubtype
        #    where = fcs + " AND " + where
        #return where
    def __repr__(self):
        return "({}; {}; {}; {}; \"{}\"; {}:{})\n".format(\
        str(self.id), str(self.title), str(self.fclist), str(self.fcsubtype),
            self.GetWhereString(), str(self.dofix), str(self.fixLst))
# end class Rule

def GetFCSids(fcsubtype, fc):
    """Return the number (as string) for the fc subtype."""
    fcsubtype = fcsubtype.strip()
    if fcsubtype.isdigit():
        return fcsubtype
    else: # If it's not an integer, it may be an S-57 '6-letter-code'
        fcs_value = S57names.S57ABBFC2FCSNumber(fcsubtype, fc)
        if fcs_value > 0:
            return fcs_value
        fcs_value = S57names.S57ABBFC2FCSNumber(fcsubtype, upper(fc)) # if we didn't find it, convert to uppercase and try again
        if fcs_value > 0:
            return fcs_value
        utils.log("Warning: Can't interpret fcsubtype: {}.".format(fcsubtype))
        return -1

def CleanUpFixString(fixvalue):
    """ If string starts and ends with matching quote marks, remove these; also convert None, NULL (and UNKNOWN), and convert to int/double. """
    # This is ugly: we're stripping quote marks off the fixvalue, even when it's a string.
    # They're required in the test value, but not allowed in the fix value,
    # so we'll allow the user to enter them in both places, and remove them here.
    if(fixvalue and fixvalue[0] == fixvalue[-1] and (fixvalue[0]=="'" or fixvalue[0]=='"')):
        fixvalue = fixvalue[1:-1]
    #if fixvalue.upper() == "UNKNOWN":
    #    return -32767 # TODO: is this a good idea? other values to accept?
    if fixvalue.upper() == "NULL" or fixvalue.upper() == "NONE":
        return None # make sure to return, the following lines will choke on a None
    if fixvalue.isdigit():
        return int(fixvalue)
    if utils.isFloat(fixvalue): # this will also match on int, so check that first
        return float(fixvalue.replace(",", ".", 1)) # accept either , or . as decimal separator
    return fixvalue

def ReadRules(path):
    """Read rules from a file, and return a list of Rule objects"""
    lst_rules = list()
    try:
        with open(path, 'r') as f:
            for line in f:
                if(not line.strip() or line[0]=="#"):
                    continue
                if(line[0]=="%"):
                    utils.log("ignoring % lines, not implemented yet")
                    continue
                if(line[0]!=":"):
                    utils.log("Warning: ignoring invalid line starting with "+line[0]+" ("+line+")")
                    continue
                if("#" in line):
                    line = line.split("#")[0].strip()
                items = line.split(":")
                if len(items)!=10: # SQL and LOVE rules both have 10 elements
                    utils.log("Warning: Line does not contain the correct number of elements. Ignoring this rule. \n\t"+line.strip()+"\n\t"+repr(items))
                    continue
                # forget about number 0, since it's always an empty string (nothing in front of the first ':')
                # number 9 is just comments
                ruleid = items[1].strip()
                title = items[2].strip()
                mode = items[3].strip()
                featureclass = items[4].strip()
                fcsubtype = items[5].strip()
                condition = items[6].strip()
                fixorlog = items[7].strip()
                fixvalue = items[8].strip()
                r = Rule(ruleid, title, mode, featureclass, fcsubtype, condition, fixorlog, fixvalue)
                if r.id != -1:
                    lst_rules.append(r)
            print "Done reading rules."
    except IOError, e:
        utils.log(e.errno)
        utils.log(e)
        return 101
    return lst_rules
<|endoftext|>


<commit_before>#!/usr/bin/env python
# coding: utf8

# Copyright 2014-2015 Vincent Jacques <vincent@vincent-jacques.net>

import os
import setuptools

version = "0.7.1"

setuptools.setup(
    name="LowVoltage",
    version=version,
    description="Standalone DynamoDB client not hiding any feature",
    author="Vincent Jacques",
    author_email="vincent@vincent-jacques.net",
    url="http://pythonhosted.org/LowVoltage",
    packages=setuptools.find_packages(),
    license="MIT",
    classifiers=[
        "Development Status :: 4 - Beta",
        "Environment :: Web Environment",
        "Intended Audience :: Developers",
        "License :: OSI Approved",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
        "Programming Language :: Python",
        "Programming Language :: Python :: 2",
        "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.4",
        "Topic :: Database",
    ],
    install_requires=["requests>=2.2"],
    tests_require=["testresources", "MockMockMock"],
    test_suite="LowVoltage.tests" if "AWS_ACCESS_KEY_ID" in os.environ else "LowVoltage.tests.local",
    test_loader="testresources:TestLoader",
    use_2to3=True,
    command_options={
        "build_sphinx": {
            "version": ("setup.py", version),
            "release": ("setup.py", version),
            "source_dir": ("setup.py", "doc"),
        },
    },
)
<commit_msg>Freeze MockMockMock's version before its interface changes<commit_after>#!/usr/bin/env python
# coding: utf8

# Copyright 2014-2015 Vincent Jacques <vincent@vincent-jacques.net>

import os
import setuptools

version = "0.7.2"

setuptools.setup(
    name="LowVoltage",
    version=version,
    description="Standalone DynamoDB client not hiding any feature",
    author="Vincent Jacques",
    author_email="vincent@vincent-jacques.net",
    url="http://pythonhosted.org/LowVoltage",
    packages=setuptools.find_packages(),
    license="MIT",
    classifiers=[
        "Development Status :: 4 - Beta",
        "Environment :: Web Environment",
        "Intended Audience :: Developers",
        "License :: OSI Approved",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
        "Programming Language :: Python",
        "Programming Language :: Python :: 2",
        "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.4",
        "Topic :: Database",
    ],
    install_requires=["requests>=2.1"],
    tests_require=["testresources", "MockMockMock<0.6.0"],
    test_suite="LowVoltage.tests" if "AWS_ACCESS_KEY_ID" in os.environ else "LowVoltage.tests.local",
    test_loader="testresources:TestLoader",
    use_2to3=True,
    command_options={
        "build_sphinx": {
            "version": ("setup.py", version),
            "release": ("setup.py", version),
            "source_dir": ("setup.py", "doc"),
        },
    },
)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from .api import get_data
from .common import BASE_URL, api_url_build

def _make_obj(obj):
    """Takes an object and returns a corresponding API class.

    The names and values of the data will match exactly with those found
    in the online docs at https://pokeapi.co/docsv2/ . In some cases, the data
    may be of a standard type, such as an integer or string. For those cases,
    the input value is simply returned, unchanged.

    :param obj: the object to be converted
    :return either the same value, if it does not need to be converted, or a
    APIResource or APIMetadata instance, depending on the data inputted.
    """

    if isinstance(obj, dict):
        if 'url' in obj.keys():
            url = obj['url']
            id_ = url.split('/')[-2]      # ID of the data.
            endpoint = url.split('/')[-3]  # Where the data is located.
            return APIResource(endpoint, id_, lazy_load=True)

        return APIMetadata(obj)

    return obj

def name_id_convert(endpoint, name_or_id):

    if isinstance(name_or_id, int):
        id_ = name_or_id
        name = _convert_id_to_name(endpoint, id_)

    elif isinstance(name_or_id, str):
        name = name_or_id
        id_ = _convert_name_to_id(endpoint, name)

    else:
        raise ValueError('the name or id \'{}\' could not be converted'
                         .format(name_or_id))

    return name, id_

def _convert_id_to_name(endpoint, id_):
    resource_data = get_data(endpoint)['results']

    for resource in resource_data:
        if resource['url'].split('/')[-2] == str(id_):

            # Return the matching name, or None if it doesn't exsist.
            return resource.get('name', None)

    return None

def _convert_name_to_id(endpoint, name):

    resource_data = get_data(endpoint)['results']

    for resource in resource_data:
        if resource.get('name') == name:
            return int(resource.get('url').split('/')[-2])

    return None

class APIResource(object):
    """Core API class, used for accessing the bulk of the data.

    The class uses a modified __getattr__ function to serve the appropriate
    data, so lookup data via the `.` operator, and use the `PokeAPI docs
    <https://pokeapi.co/docsv2/>`_ or the builtin `dir` function to see the
    possible lookups.

    This class takes the complexity out of lots of similar classes for each
    different kind of data served by the API, all of which are very similar,
    but not identical.
    """

    def __init__(self, endpoint, name_or_id, lazy_load=False, force_lookup=False):

        name, id_ = name_id_convert(endpoint, name_or_id)
        url = api_url_build(endpoint, id_)

        self.__dict__.update({'name': name,
                              'endpoint': endpoint,
                              'id_': id_,
                              'url': url})

        self.__loaded = False
        self.__force_lookup = force_lookup

        if not lazy_load:
            self._load()
            self.__loaded = True

    def __getattr__(self, attr):
        """Modified method to auto-load the data when it is needed.

        If the data has not yet been looked up, it is loaded, and then checked
        for the requested attribute. If it is not found, AttributeError is
        raised.
        """

        if not self.__loaded:
            self._load()
            self.__loaded = True

            return self.__getattribute__(attr)

        else:
            raise AttributeError('{} object has no attribute {}'
                                 .format(type(self), attr))

    def __str__(self):
        return str(self.name)

    def __repr__(self):
        return '<{}-{}>'.format(self.endpoint, self.name)

    def _load(self):
        """Function to collect reference data and connect it to the instance as
         attributes.

         Internal function, does not usually need to be called by the user, as
         it is called automatically when an attribute is requested.

        :return None
        """

        data = get_data(self.endpoint, self.id_, self.__force_lookup)

        # Make our custom objects from the data.
        for key, val in data.items():

            if isinstance(val, dict):
                data[key] = _make_obj(val)

            elif isinstance(val, list):
                data[key] = [_make_obj(i) for i in val]

        self.__dict__.update(data)

        return None

class APIResourceList(object):
    """Class for a data container.

    Used to access data corresponding to a category, rather than an individual
    reference. Ex. APIResourceList('berry') gives information about all
    berries, such as which ID's correspond to which berry names, and
    how many berries there are.

    You can iterate through all the names or all the urls, using the respective
    properties. You can also iterate on the object itself to run through the
    `dict`s with names and urls together, whatever floats your boat.
    """

    def __init__(self, endpoint, force_lookup=False):
        """Creates a new APIResourceList instance.

        :param name: the name of the resource to get (ex. 'berry' or 'move')
        """

        response = get_data(endpoint, force_lookup)

        self.name = endpoint
        self.__results = [i for i in response['results']]
        self.count = response['count']

    def __len__(self):
        return self.count

    def __iter__(self):
        return iter(self.__results)

    def __str__(self):
        return str(self.__results)

    @property
    def names(self):
        """Useful iterator for all the resource's names."""
        for result in self.__results:
            yield result.get('name', result['url'].split('/')[-2])

    @property
    def urls(self):
        """Useful iterator for all of the resource's urls."""
        for result in self.__results:
            yield result['url']

class APIMetadata(object):
    """Helper class for smaller references.

    This class emulates a dictionary, but attribute lookup is via the `.`
    operator, not indexing. (ex. instance.attr, not instance['attr']).

    Used for "Common Models" classes and APIResource helper classes.
    https://pokeapi.co/docsv2/#common-models
    """

    def __init__(self, data):

        for key, val in data.items():

            if isinstance(val, dict):
                data[key] = _make_obj(val)

        self.__dict__.update(data)
<commit_msg>fix force_lookup for APIResourceList<commit_after># -*- coding: utf-8 -*-

from .api import get_data
from .common import BASE_URL, api_url_build

def _make_obj(obj):
    """Takes an object and returns a corresponding API class.

    The names and values of the data will match exactly with those found
    in the online docs at https://pokeapi.co/docsv2/ . In some cases, the data
    may be of a standard type, such as an integer or string. For those cases,
    the input value is simply returned, unchanged.

    :param obj: the object to be converted
    :return either the same value, if it does not need to be converted, or a
    APIResource or APIMetadata instance, depending on the data inputted.
    """

    if isinstance(obj, dict):
        if 'url' in obj.keys():
            url = obj['url']
            id_ = url.split('/')[-2]      # ID of the data.
            endpoint = url.split('/')[-3]  # Where the data is located.
            return APIResource(endpoint, id_, lazy_load=True)

        return APIMetadata(obj)

    return obj

def name_id_convert(endpoint, name_or_id):

    if isinstance(name_or_id, int):
        id_ = name_or_id
        name = _convert_id_to_name(endpoint, id_)

    elif isinstance(name_or_id, str):
        name = name_or_id
        id_ = _convert_name_to_id(endpoint, name)

    else:
        raise ValueError('the name or id \'{}\' could not be converted'
                         .format(name_or_id))

    return name, id_

def _convert_id_to_name(endpoint, id_):
    resource_data = get_data(endpoint)['results']

    for resource in resource_data:
        if resource['url'].split('/')[-2] == str(id_):

            # Return the matching name, or None if it doesn't exsist.
            return resource.get('name', None)

    return None

def _convert_name_to_id(endpoint, name):

    resource_data = get_data(endpoint)['results']

    for resource in resource_data:
        if resource.get('name') == name:
            return int(resource.get('url').split('/')[-2])

    return None

class APIResource(object):
    """Core API class, used for accessing the bulk of the data.

    The class uses a modified __getattr__ function to serve the appropriate
    data, so lookup data via the `.` operator, and use the `PokeAPI docs
    <https://pokeapi.co/docsv2/>`_ or the builtin `dir` function to see the
    possible lookups.

    This class takes the complexity out of lots of similar classes for each
    different kind of data served by the API, all of which are very similar,
    but not identical.
    """

    def __init__(self, endpoint, name_or_id, lazy_load=False, force_lookup=False):

        name, id_ = name_id_convert(endpoint, name_or_id)
        url = api_url_build(endpoint, id_)

        self.__dict__.update({'name': name,
                              'endpoint': endpoint,
                              'id_': id_,
                              'url': url})

        self.__loaded = False
        self.__force_lookup = force_lookup

        if not lazy_load:
            self._load()
            self.__loaded = True

    def __getattr__(self, attr):
        """Modified method to auto-load the data when it is needed.

        If the data has not yet been looked up, it is loaded, and then checked
        for the requested attribute. If it is not found, AttributeError is
        raised.
        """

        if not self.__loaded:
            self._load()
            self.__loaded = True

            return self.__getattribute__(attr)

        else:
            raise AttributeError('{} object has no attribute {}'
                                 .format(type(self), attr))

    def __str__(self):
        return str(self.name)

    def __repr__(self):
        return '<{}-{}>'.format(self.endpoint, self.name)

    def _load(self):
        """Function to collect reference data and connect it to the instance as
         attributes.

         Internal function, does not usually need to be called by the user, as
         it is called automatically when an attribute is requested.

        :return None
        """

        data = get_data(self.endpoint, self.id_, self.__force_lookup)

        # Make our custom objects from the data.
        for key, val in data.items():

            if isinstance(val, dict):
                data[key] = _make_obj(val)

            elif isinstance(val, list):
                data[key] = [_make_obj(i) for i in val]

        self.__dict__.update(data)

        return None

class APIResourceList(object):
    """Class for a data container.

    Used to access data corresponding to a category, rather than an individual
    reference. Ex. APIResourceList('berry') gives information about all
    berries, such as which ID's correspond to which berry names, and
    how many berries there are.

    You can iterate through all the names or all the urls, using the respective
    properties. You can also iterate on the object itself to run through the
    `dict`s with names and urls together, whatever floats your boat.
    """

    def __init__(self, endpoint, force_lookup=False):
        """Creates a new APIResourceList instance.

        :param name: the name of the resource to get (ex. 'berry' or 'move')
        """

        response = get_data(endpoint, force_lookup=force_lookup)

        self.name = endpoint
        self.__results = [i for i in response['results']]
        self.count = response['count']

    def __len__(self):
        return self.count

    def __iter__(self):
        return iter(self.__results)

    def __str__(self):
        return str(self.__results)

    @property
    def names(self):
        """Useful iterator for all the resource's names."""
        for result in self.__results:
            yield result.get('name', result['url'].split('/')[-2])

    @property
    def urls(self):
        """Useful iterator for all of the resource's urls."""
        for result in self.__results:
            yield result['url']

class APIMetadata(object):
    """Helper class for smaller references.

    This class emulates a dictionary, but attribute lookup is via the `.`
    operator, not indexing. (ex. instance.attr, not instance['attr']).

    Used for "Common Models" classes and APIResource helper classes.
    https://pokeapi.co/docsv2/#common-models
    """

    def __init__(self, data):

        for key, val in data.items():

            if isinstance(val, dict):
                data[key] = _make_obj(val)

        self.__dict__.update(data)
<|endoftext|>


<commit_before>#!/usr/bin/env python

"""
    A python PONG implementation based on python-verbose_ping("script by George Notaras.
    Refer to python-ping.py for comments and authorship: http://www.g-loaded.eu/2009/10/30/python-ping/

	This file only calls python-ping functions in the same
	manner and order other PONG scripts do in their particular languages.

"""
import python-ping

if __name__ == '__main__':
    #     GOOGLE
    verbose_ping("8.8.8.8")
    verbose_ping("8.8.4.4")
    #    ==========================
    #     LEVEL3
    verbose_ping("4.2.2.1")
    verbose_ping("4.2.2.2")
    #   verbose_ping("4.2.2.3")
    #   verbose_ping("4.2.2.4")
    #   verbose_ping("4.2.2.5")
    #   verbose_ping("4.2.2.6")
    #    ==========================
    #     AUSTRALIA ns1.telstra.net
    verbose_ping("139.130.4.5")
    #    ==========================
    #     OpenDNS
    verbose_ping("208.67.222.222")
    verbose_ping("208.67.220.220")
    #    ==========================
    #     MSK-IX NTP stratum-1 server --- better not to do this often
    #   verbose_ping("194.190.168.1
    #    ==========================
    #    next tests require DNS to be working
    #    ==========================
    #     YANDEX
    verbose_ping("ya.ru")
    #    ==========================
    #     INFOBOX:
    #    ------------- AMSTERDAM (EUROPE)
    verbose_ping("ams.sandbox.infobox.ru")
    #    ------------- SPB (NORTH-WEST RUSSIA)
    verbose_ping("spb.sandbox.infobox.ru")
    #    ------------- KRASNOYARSK (SIBERIAN RUSSIA)
    verbose_ping("krs.sandbox.infobox.ru")
    #    ==========================
    #    verbose_ping("WIKIPEDIA
    verbose_ping("wikipedia.org")
    #    ==========================
    #    verbose_ping("USA
    verbose_ping("whitehouse.gov")
<commit_msg>add Russian IX route server IPs<commit_after>#!/usr/bin/env python

"""
    A python PONG implementation based on python-verbose_ping("script by George Notaras.
    Refer to python-ping.py for comments and authorship: http://www.g-loaded.eu/2009/10/30/python-ping/

	This file only calls python-ping functions in the same
	manner and order other PONG scripts do in their particular languages.

"""
import python-ping

if __name__ == '__main__':
    #     GOOGLE
    verbose_ping("8.8.8.8")
    verbose_ping("8.8.4.4")
    #    ==========================
    #     LEVEL3
    verbose_ping("4.2.2.1")
    verbose_ping("4.2.2.2")
    #   verbose_ping("4.2.2.3")
    #   verbose_ping("4.2.2.4")
    #   verbose_ping("4.2.2.5")
    #   verbose_ping("4.2.2.6")
    #    ==========================
    #     AUSTRALIA ns1.telstra.net
    verbose_ping("139.130.4.5")
    #    ==========================
    #     some Russian Internet eXchanges (IX):
    #     SPB-IX (Saint-Peterbourg/north-west Russia) (Part of MSK-IX association)
    verbose_ping("194.226.100.100")
    verbose_ping("194.226.102.100")
    #     MSK-IX (Moscow/central Russia) (Part of MSK-IX association)
    verbose_ping("195.208.208.100")
    verbose_ping("195.208.215.100")
    #     RND-IX (Rostov/southern Russia) (Part of MSK-IX association)
    verbose_ping("193.232.140.100")
    #verbose_ping("193.232.140.100") # only one route server at RND-IX
    #     EKT-IX (Ural) (Part of MSK-IX association)
    verbose_ping("194.85.107.100")
    verbose_ping("194.85.107.200")
    #     VLV-IX (Vladivostok/far east Russia) (Part of MSK-IX association)
    verbose_ping("193.232.136.100")
    #verbose_ping("193.232.136.100") # only one route server at VLV-IX
    #    ==========================
    #     OpenDNS
    verbose_ping("208.67.222.222")
    verbose_ping("208.67.220.220")
    #    ==========================
    #     MSK-IX NTP stratum-1 server --- better not to do this often
    #   verbose_ping("194.190.168.1
    #    ==========================
    #    next tests require DNS to be working
    #    ==========================
    #     YANDEX
    verbose_ping("ya.ru")
    #    ==========================
    #     INFOBOX:
    #    ------------- AMSTERDAM (EUROPE)
    verbose_ping("ams.sandbox.infobox.ru")
    #    ------------- SPB (NORTH-WEST RUSSIA)
    verbose_ping("spb.sandbox.infobox.ru")
    #    ------------- KRASNOYARSK (SIBERIAN RUSSIA)
    verbose_ping("krs.sandbox.infobox.ru")
    #    ==========================
    #    verbose_ping("WIKIPEDIA
    verbose_ping("wikipedia.org")
    #    ==========================
    #    verbose_ping("USA
    verbose_ping("whitehouse.gov")
<|endoftext|>