<commit_before>#!/usr/bin/python
# coding: utf-8

# imports
import socket
import sys
import hashlib
import binascii
import collections

# classes

class SockClient(object):
    """SockClient for handling the connection to the server"""
    def __init__(self):
        # Creates a TCP/IP socket
        try:
            self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        except socket.error, e:
            print >> sys.stderr, e
            sys.exit()

    def connect(self, address, port):
        try:
            self.client.connect((address, port))
        except socket.error, e:
            print >> sys.stderr, e
            self.client.close()
            sys.exit()

    def __receiveBytes(self, amount):
        try:
            received = self.client.recv(amount)
        except socket.error, e:
            print >> sys.stderr, e
            self.client.close()
            sys.exit()
        # Debug
        print "\nReceived: %d" % len(received)
        return received

    def __getPacketLength(self):
        try:
            self.packetlength
        except:
            self.packetlength = self.__receiveBytes(2)
        # Debug
        print "\n\nPacket Length: %d\n - Bytes: %s\n - Hex: %s" % \
            (sum([ ord(x) for x in self.packetlength ]),
                [ ord(x) for x in self.packetlength ],
                [ x.encode('hex') for x in self.packetlength])
        return self.packetlength

    def __getMD5Sum(self):
        try:
            self.md5sum
        except:
            self.md5sum = self.__receiveBytes(16)
        # Debug
        print "\n\nMD5 Sum: %s\n - Bytes: %s\n - Hex: %s" % \
                (self.md5sum.encode('hex'),
                [ ord(x) for x in self.md5sum ],
                [ x.encode('hex') for x in self.md5sum])
        return self.md5sum

    def __getData(self):
        try:
            self.data
        except:
            self.data = self.__receiveBytes(sum([ ord(x) for x in self.packetlength ]) - 16 - 2 - 1)
        # Debug
        print "\n\nData: %s\n - Bytes: %s\n - Hex: %s" % \
                (self.data.encode('hex'),
                [ ord(x) for x in self.data ],
                [ x.encode('hex') for x in self.data])
        return self.data

    def __getParityByte(self):
        try:
            self.parity
        except:
            self.parity = self.__receiveBytes(1)
        # Debug
        print "\n\nParity: %s\n - Bytes: %s\n - Hex: %s" % \
                (self.parity.encode('hex'),
                [ ord(x) for x in self.parity ],
                [ x.encode('hex') for x in self.parity])
        return self.parity

    def __checkMessageParity(self, bits):

        num_1bits = bits.count('1')

        # Check if parity byte exists
        if(int(bits[len(bits)-8:]) > 1):
            print "Parity byte does not exist!"
        else:
            if(bits[:len(bits)-8].count('1') % 2 == 0):
                print "Message number of 1 bits is Even (%d), checking parity byte..." % bits[:len(bits)-8].count('1')
                print "Parity byte is %s" % bits[len(bits)-8:]
            else:
                print "Message number of 1 bits is ODD (%d), checking parity byte..." % bits[:len(bits)-8].count('1')
                print "Parity byte is %s" % bits[len(bits)-8:]

        if(num_1bits % 2 == 0):
            print "Even number of 1 bits (%d), message parity is ok" % num_1bits
            return 0
        else:
            print "Odd number of 1 bits (%d), message parity is not ok" % num_1bits
            return 1

    def __checkDataMD5Sum(self):
        newmd5 = hashlib.md5()
        newmd5.update(self.data)
        if(newmd5.hexdigest() == self.md5sum.encode('hex')):
            print "Data MD5 sum is OK %s == %s" %  (self.md5sum.encode('hex'), newmd5.hexdigest())
        else:
            print "Data MD5 sum is NOT ok %s != %s" % (self.md5sum.encode('hex'), newmd5.hexdigest())

    def __getMostCommonByte(self):
        counts = collections.Counter([ x.encode('hex') for x in self.data]).most_common()
        self.mostcommonbyte = counts[0][0]
        print "Most commom byte in data is hex: %s" % self.mostcommonbyte
        return self.mostcommonbyte

    def __getCipherKey(self):
        self.cipherkey = int(self.mostcommonbyte,16) ^ 0x20
        print "Cipherkey: Int: %s - Hex: %s" % (self.cipherkey, hex(self.cipherkey)[2:])
        return self.cipherkey

    def __decodeData(self):
        mdata = [ x.encode('hex') for x in self.data ]
        self.decodedmessage = [ chr(int(x,16) ^ self.cipherkey) for x in mdata ]
        print self.decodedmessage
        print "Decoded data: %s" % ''.join(self.decodedmessage)
        self.decodedmessage = [ x.encode('hex') for x in self.decodedmessage]
        print self.decodedmessage
        return self.decodedmessage

    def __createMessage(self):

        nm_decodedmessage = self.decodedmessage

        datastr = ''.join(self.decodedmessage)
        print "\nDecoded data str: %s" % datastr

        newmd5 = hashlib.md5()
        newmd5.update(datastr)

        md5sum = newmd5.hexdigest()

        print "\nNM decoded data MD5 sum: %s" % md5sum
        nm_md5sum = [md5sum[i:i+2] for i in range(0, len(md5sum), 2)]
        print nm_md5sum

        nm_parity = 0x0

        nm_length = 2 + 16 + len(self.decodedmessage)
        hexnmlength = hex(nm_length)[2:]
        print "\nNM length: %d - Hex: %s" % (nm_length, hexnmlength)
        message_length = [hexnmlength[i:i+2] for i in range(0, len(hexnmlength), 2)]

        # Miau por falta de conhecimento como adicionar 0's em 2 bytes hex no python
        if(nm_length <= 0xff):
            print 'True'
            zb = ['00']
            zb.extend(message_length)
            nm_length = zb
            print nm_length
        else:
            nm_length = message_length

        nm_message = []
        nm_message.extend(nm_length)
        nm_message.extend(nm_md5sum)
        nm_message.extend(nm_decodedmessage)

        print "NM message: "
        print nm_message

        nm_binary = (bin(int(''.join(nm_message), 16))[2:]).zfill(len(''.join(nm_message)) * 4)

        print "\nNM binary: %s" % nm_binary

        nm_parity = self.__checkMessageParity(nm_binary)

        nm_parity = [nm_parity]
        nm_parity = [''.join('{:02x}'.format(x) for x in nm_parity)]
        nm_message.extend(nm_parity)

        nm_binary = (bin(int(''.join(nm_message), 16))[2:]).zfill(len(''.join(nm_message)) * 4)
        nm_parity = self.__checkMessageParity(nm_binary)

        print "\nNM binary: %s" % nm_binary

        print "NM message: "
        print nm_message

        self.nm_message = ''.join(nm_message)

        print "NM message str: %s" % self.nm_message

    def getMessage(self):
        self.__getPacketLength()
        self.__getMD5Sum()
        self.__getData()
        self.__getParityByte()

        self.message = self.packetlength + self.md5sum + self.data + self.parity

        binarymessage = (bin(int(self.message.encode('hex'), 16))[2:]).zfill(len(self.message.encode('hex')) * 4)

        print "\n\nMessage: %s\n - Hex: %s\n - Bin: %s" % \
                ([ ord(x) for x in self.message ],
                    self.message.encode('hex'),
                    binarymessage)

        self.__checkMessageParity(binarymessage)
        self.__checkDataMD5Sum()

        self.__getMostCommonByte()
        self.__getCipherKey()
        self.__decodeData()
        self.__createMessage()

    def disconnect(self):
        self.client.send(self.nm_message.decode('hex'))
        self.client.close()

<commit_msg>Corrigido geracao de hash para mensagem decriptada<commit_after>#!/usr/bin/python
# coding: utf-8

# imports
import socket
import sys
import hashlib
import binascii
import collections

# classes

class SockClient(object):
    """SockClient for handling the connection to the server"""
    def __init__(self):
        # Creates a TCP/IP socket
        try:
            self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        except socket.error, e:
            print >> sys.stderr, e
            sys.exit()

    def connect(self, address, port):
        try:
            self.client.connect((address, port))
        except socket.error, e:
            print >> sys.stderr, e
            self.client.close()
            sys.exit()

    def __receiveBytes(self, amount):
        try:
            received = self.client.recv(amount)
        except socket.error, e:
            print >> sys.stderr, e
            self.client.close()
            sys.exit()
        # Debug
        print "\nReceived: %d" % len(received)
        return received

    def __getPacketLength(self):
        try:
            self.packetlength
        except:
            self.packetlength = self.__receiveBytes(2)
        # Debug
        print "\n\nPacket Length: %d\n - Bytes: %s\n - Hex: %s" % \
            (sum([ ord(x) for x in self.packetlength ]),
                [ ord(x) for x in self.packetlength ],
                [ x.encode('hex') for x in self.packetlength])
        return self.packetlength

    def __getMD5Sum(self):
        try:
            self.md5sum
        except:
            self.md5sum = self.__receiveBytes(16)
        # Debug
        print "\n\nMD5 Sum: %s\n - Bytes: %s\n - Hex: %s" % \
                (self.md5sum.encode('hex'),
                [ ord(x) for x in self.md5sum ],
                [ x.encode('hex') for x in self.md5sum])
        return self.md5sum

    def __getData(self):
        try:
            self.data
        except:
            self.data = self.__receiveBytes(sum([ ord(x) for x in self.packetlength ]) - 16 - 2 - 1)
        # Debug
        print "\n\nData: %s\n - Bytes: %s\n - Hex: %s" % \
                (self.data.encode('hex'),
                [ ord(x) for x in self.data ],
                [ x.encode('hex') for x in self.data])
        return self.data

    def __getParityByte(self):
        try:
            self.parity
        except:
            self.parity = self.__receiveBytes(1)
        # Debug
        print "\n\nParity: %s\n - Bytes: %s\n - Hex: %s" % \
                (self.parity.encode('hex'),
                [ ord(x) for x in self.parity ],
                [ x.encode('hex') for x in self.parity])
        return self.parity

    def __checkMessageParity(self, bits):

        num_1bits = bits.count('1')

        # Check if parity byte exists
        if(int(bits[len(bits)-8:]) > 1):
            print "Parity byte does not exist!"
        else:
            if(bits[:len(bits)-8].count('1') % 2 == 0):
                print "Message number of 1 bits is Even (%d), checking parity byte..." % bits[:len(bits)-8].count('1')
                print "Parity byte is %s" % bits[len(bits)-8:]
            else:
                print "Message number of 1 bits is ODD (%d), checking parity byte..." % bits[:len(bits)-8].count('1')
                print "Parity byte is %s" % bits[len(bits)-8:]

        if(num_1bits % 2 == 0):
            print "Even number of 1 bits (%d), message parity is ok" % num_1bits
            return 0
        else:
            print "Odd number of 1 bits (%d), message parity is not ok" % num_1bits
            return 1

    def __checkDataMD5Sum(self):
        newmd5 = hashlib.md5()
        newmd5.update(self.data)
        md5sum = newmd5.hexdigest()
        if(md5sum == self.md5sum.encode('hex')):
            print "Data MD5 sum is OK %s == %s" %  (self.md5sum.encode('hex'), md5sum)
        else:
            print "Data MD5 sum is NOT ok %s != %s" % (self.md5sum.encode('hex'), md5sum)

    def __getMostCommonByte(self):
        counts = collections.Counter([ x.encode('hex') for x in self.data]).most_common()
        self.mostcommonbyte = counts[0][0]
        print "Most commom byte in data is hex: %s" % self.mostcommonbyte
        return self.mostcommonbyte

    def __getCipherKey(self):
        self.cipherkey = int(self.mostcommonbyte,16) ^ 0x20
        print "Cipherkey: Int: %s - Hex: %s" % (self.cipherkey, hex(self.cipherkey)[2:])
        return self.cipherkey

    def __decodeData(self):
        mdata = [ x.encode('hex') for x in self.data ]
        self.decodedmessage = [ chr(int(x,16) ^ self.cipherkey) for x in mdata ]
        print self.decodedmessage
        print "Decoded data hex: %s" % [ x.encode('hex') for x in self.decodedmessage]
        self.decodedmessage = ''.join(self.decodedmessage)
        print "\nDecoded data str: %s" % self.decodedmessage
        return self.decodedmessage

    def __createMessage(self):

        nm_length = 2 + 16 + len(self.decodedmessage) + 1
        hexnmlength = hex(nm_length)[2:]
        print "\nNM length: %d - Hex: %s" % (nm_length, hexnmlength)
        message_length = [hexnmlength[i:i+2] for i in range(0, len(hexnmlength), 2)]

        # Miau por falta de conhecimento como adicionar 0's em 2 bytes hex no python
        if(nm_length <= 0xff):
            print 'True'
            zb = ['00']
            zb.extend(message_length)
            nm_length = zb
            print nm_length
        else:
            nm_length = message_length

        nm_newmd5 = hashlib.md5()
        nm_newmd5.update(self.decodedmessage)
        md5sum = nm_newmd5.hexdigest()

        print "\nNM decoded data MD5 sum: %s" % md5sum
        nm_md5sum = [md5sum[i:i+2] for i in range(0, len(md5sum), 2)]
        print nm_md5sum

        nm_parity = 0x0

        nm_decodedmessage = [ x.encode('hex') for x in self.decodedmessage]

        nm_message = []
        nm_message.extend(nm_length)
        nm_message.extend(nm_md5sum)
        nm_message.extend(nm_decodedmessage)

        print "NM message: "
        print nm_message

        nm_binary = (bin(int(''.join(nm_message), 16))[2:]).zfill(len(''.join(nm_message)) * 4)

        print "\nNM binary: %s" % nm_binary

        nm_parity = self.__checkMessageParity(nm_binary)

        nm_parity = [nm_parity]
        nm_parity = [''.join('{:02x}'.format(x) for x in nm_parity)]
        nm_message.extend(nm_parity)

        nm_binary = (bin(int(''.join(nm_message), 16))[2:]).zfill(len(''.join(nm_message)) * 4)
        nm_parity = self.__checkMessageParity(nm_binary)

        print "\nNM binary: %s" % nm_binary

        print "NM message: "
        print nm_message

        self.nm_message = ''.join(nm_message)

        print "NM message str: %s" % self.nm_message

    def getMessage(self):
        self.__getPacketLength()
        self.__getMD5Sum()
        self.__getData()
        self.__getParityByte()

        self.message = self.packetlength + self.md5sum + self.data + self.parity

        binarymessage = (bin(int(self.message.encode('hex'), 16))[2:]).zfill(len(self.message.encode('hex')) * 4)

        print "\n\nMessage: %s\n - Hex: %s\n - Bin: %s" % \
                ([ ord(x) for x in self.message ],
                    self.message.encode('hex'),
                    binarymessage)

        self.__checkMessageParity(binarymessage)
        self.__checkDataMD5Sum()

        self.__getMostCommonByte()
        self.__getCipherKey()
        self.__decodeData()
        self.__createMessage()

    def disconnect(self):
        self.client.send(self.nm_message.decode('hex'))
        self.client.close()<|endoftext|>


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

zebra_lines = []
with open('zebra.txt') as f:
    zebra_lines = sorted(f, key=lambda s: s.casefold())
with open('zebra.txt', 'w') as f:
    for line in zebra_lines:
        f.write(line)
<commit_msg>Remove duplicate lines automatically<commit_after>#!/usr/bin/env python3

zebra_lines = []
with open('zebra.txt') as f:
    zebra_lines = sorted(set(f), key=lambda s: s.casefold())
with open('zebra.txt', 'w') as f:
    for line in zebra_lines:
        f.write(line)
<|endoftext|>


<commit_before>"""Module containing functions to compute the autocorrelation function
and estimate the associated autocorrelation length of series.

The estimate of the autocorrelation function is based on the method
described at http://www.math.nyu.edu/faculty/goodman/software/acor/
and implemented in the associated ``acor`` C++ code, though the
implementation details differ.

"""

import matplotlib.pyplot as plt
import numpy as np

def _next_power_of_two(i):
    pt = 2
    while pt < i:
        pt *= 2

    return pt

def autocorrelation_function(series, axis=0):
    """Returns the autocorrelation function of the given series.  The
    function is normalised so that it is 1 at zero lag.

    """
    series = np.atleast_1d(series)
    shape = np.array(series.shape)
    m = [slice(None)] * len(shape)

    n0 = shape[axis]
    n = _next_power_of_two(shape[axis]*2)
    m[axis] = slice(0, n0)
    shape[axis] = n

    padded_series = np.zeros(shape)
    padded_series[m] = series - np.mean(series, axis=axis)

    ps_tilde = np.fft.fft(padded_series, axis=axis)
    acf = np.real(np.fft.ifft(ps_tilde*np.conj(ps_tilde), axis=axis))[m]

    m[axis] = 0
    shape[axis] = 1
    acf /= acf[m].reshape(shape).repeat(n0, axis)

    return acf

def autocorrelation_length_estimate(series, acf=None, M=5, axis=0):
    r"""Returns an estimate of the autocorrelation length of the given
    series:

    .. math::

      L = \int_{-\infty}^\infty \rho(t) dt

    The estimate is the smallest :math:`L` such that

    .. math::

      L = \rho(0) + 2 \sum_{j = 1}^{M L} \rho(j)

    In words: the ACL is estimated over a window that is at least
    :math:`M` ACLs long, with the constraint that :math:`ML < N/2`.

    Defined in this way, the ACL gives the reduction factor between
    the number of samples and the "effective" number of samples.  In
    particular, the variance of the estimated mean of the series is
    given by

    .. math::

      \left\langle \left( \frac{1}{N} \sum_{i=0}^{N-1} x_i - \mu
      \right)^2 \right\rangle = \frac{\left\langle \left(x_i -
      \mu\right)^2 \right\rangle}{N/L}

    Returns ``None`` if there is no such estimate possible (because
    the series is too short to fit :math:`2M` ACLs).

    """
    if acf is None:
        acf = autocorrelation_function(series, axis=axis)
    m = [slice(None)] * len(acf.shape)
    nmax = acf.shape[axis]/2

    m[axis] = slice(0, nmax)
    acl_ests = 2.0*np.cumsum(acf[m], axis=axis) - 1.0

    shape = acf.shape[:axis] + (nmax,) + acf.shape[axis+1:]
    lags = np.cumsum(np.ones(shape), axis=axis) - 1.0

    diffs = M*acl_ests - lags
    if np.any(diffs < 0):
        i = np.argmin(np.abs(diffs), axis=axis)
        j = tuple(np.indices(i.shape))
        return acl_ests[j[:axis] + (i,) + j[axis:]]
    else:
        return None

def _default_burnin(M):
    return 1.0/(M + 1.0)

def emcee_chain_autocorrelation_lengths(chain, M=5, fburnin=None):
    r"""Returns an array giving the ACL for each parameter in the given
    emcee chain.

    :param chain: The emcee sampler chain.

    :param M: See :func:`autocorrelation_length_estimate`

    :param fburnin: Discard the first ``fburnin`` fraction of the
      samples as burn-in before computing the ACLs.  Default is to
      discard the first :math:`1/(M+1)`, ensuring that at least one
      ACL is discarded.

    """

    if fburnin is None:
        fburnin = _default_burnin(M)

    istart = int(round(fburnin*chain.shape[1]))

    return np.array([autocorrelation_length_estimate(np.mean(chain[:,istart:,k], axis=0)) for k in range(chain.shape[2])])

def emcee_ptchain_autocorrelation_lengths(ptchain, M=5, fburnin=None):
    r"""Returns an array of shape ``(Ntemp, Nparams)`` giving the estimated
    autocorrelation lengths for each parameter across each temperature
    of the parallel-tempered set of chains.  If a particular ACL
    cannot be estimated, that element of the array will be ``None``.
    See :func:`emcee_chain_autocorrelation_lengths` for a description
    of the optional arguments.

    """

    return np.array([emcee_chain_autocorrelation_lengths(ptchain[i,...], M=M, fburnin=fburnin) for i in range(ptchain.shape[0])])

def emcee_thinned_chain(chain, M=5, fburnin=None):
    r"""Returns a thinned, burned-in version of the emcee chain.

    :param chain: The emcee sampler chain.

    :param M: See :func:`autocorrelation_length_estimate`

    :param fburnin: Discard the first ``fburnin`` fraction of the
      samples as burn-in before computing the ACLs.  Default is to
      discard the first :math:`1/(M+1)`, ensuring that at least one
      ACL is discarded.

    """

    if fburnin is None:
        fburnin = _default_burnin(M)

    istart = int(round(fburnin*chain.shape[1]))

    acls = emcee_chain_autocorrelation_lengths(chain, M=M, fburnin=fburnin)

    if any(ac is None for ac in acls):
        return None

    tau = int(np.ceil(np.max(acls)))

    return chain[:,istart::tau,:]

def emcee_thinned_ptchain(ptchain, M=5, fburnin=None):
    r"""Returns a thinned, burned in version of the emcee parallel-tempered
    chains in ``ptchain``, or ``None`` if it is not possible to
    estimate an ACL for some component of the chain.

    """

    if fburnin is None:
        fburnin = _default_burnin(M)

    istart = int(round(fburnin*ptchain.shape[2]))

    acls = emcee_ptchain_autocorrelation_lengths(ptchain, M=M, fburnin=fburnin)

    if any(ac is None for ac in acls.flatten()):
        return None

    tau = int(np.ceil(np.max(acls)))

    return ptchain[:,:,istart::tau,:]

def plot_emcee_chain_autocorrelation_functions(chain, fburnin=None):
    r"""Plots a grid of the autocorrelation function (post burnin) for each
    of the parameters in the given chain.

    """

    if fburnin is None:
        fburnin = _default_burnin(5)

    istart = int(round(fburnin*chain.shape[1]))

    chain = chain[:,istart:,:]

    npar = chain.shape[2]
    nside = int(np.ceil(np.sqrt(npar)))

    for i in range(npar):
        plt.subplot(nside, nside, i+1)
        plt.plot(autocorrelation_function(np.mean(chain[:,:,i], axis=0)))
<commit_msg>Fixed bug in N-dimensional ACF code.<commit_after>"""Module containing functions to compute the autocorrelation function
and estimate the associated autocorrelation length of series.

The estimate of the autocorrelation function is based on the method
described at http://www.math.nyu.edu/faculty/goodman/software/acor/
and implemented in the associated ``acor`` C++ code, though the
implementation details differ.

"""

import matplotlib.pyplot as plt
import numpy as np
import numpy.ma as ma

def _next_power_of_two(i):
    pt = 2
    while pt < i:
        pt *= 2

    return pt

def autocorrelation_function(series, axis=0):
    """Returns the autocorrelation function of the given series.  The
    function is normalised so that it is 1 at zero lag.

    If ``series`` is an N-dimensional array, the ACF will be computed
    along ``axis`` and the result will have the same shape as
    ``series``.

    """
    series = np.atleast_1d(series)
    shape = np.array(series.shape)
    m = [slice(None)] * len(shape)

    n0 = shape[axis]
    n = _next_power_of_two(shape[axis]*2)
    m[axis] = slice(0, n0)
    shape[axis] = n

    padded_series = np.zeros(shape)
    padded_series[m] = series - np.expand_dims(series.mean(axis=axis), axis=axis)

    ps_tilde = np.fft.fft(padded_series, axis=axis)
    acf = np.real(np.fft.ifft(ps_tilde*np.conj(ps_tilde), axis=axis))[m]

    m[axis] = 0
    shape[axis] = 1
    acf /= acf[m].reshape(shape).repeat(n0, axis)

    return acf

def autocorrelation_length_estimate(series, acf=None, M=5, axis=0):
    r"""Returns an estimate of the autocorrelation length of the given
    series:

    .. math::

      L = \int_{-\infty}^\infty \rho(t) dt

    The estimate is the smallest :math:`L` such that

    .. math::

      L = \rho(0) + 2 \sum_{j = 1}^{M L} \rho(j)

    In words: the ACL is estimated over a window that is at least
    :math:`M` ACLs long, with the constraint that :math:`ML < N/2`.

    Defined in this way, the ACL gives the reduction factor between
    the number of samples and the "effective" number of samples.  In
    particular, the variance of the estimated mean of the series is
    given by

    .. math::

      \left\langle \left( \frac{1}{N} \sum_{i=0}^{N-1} x_i - \mu
      \right)^2 \right\rangle = \frac{\left\langle \left(x_i -
      \mu\right)^2 \right\rangle}{N/L}

    Returns ``nan`` if there is no such estimate possible (because
    the series is too short to fit :math:`2M` ACLs).

    For an N-dimensional array, returns an array of ACLs of the same
    shape as ``series``, but with the dimension along ``axis``
    removed.

    """
    if acf is None:
        acf = autocorrelation_function(series, axis=axis)
    m = [slice(None)] * len(acf.shape)
    nmax = acf.shape[axis]/2

    # Generate ACL candidates.
    m[axis] = slice(0, nmax)
    acl_ests = 2.0*np.cumsum(acf[m], axis=axis) - 1.0

    # Build array of lags (like arange, but N-dimensional).
    shape = acf.shape[:axis] + (nmax,) + acf.shape[axis+1:]
    lags = np.cumsum(np.ones(shape), axis=axis) - 1.0

    # Mask out unwanted lags and set corresponding ACLs to nan.
    mask = M*acl_ests >= lags
    acl_ests[mask] = np.nan
    i = ma.masked_greater(mask, lags, copy=False)

    # Now get index of smallest unmasked lag -- if all are masked, this will be 0.
    j = i.argmin(axis=axis)
    k = tuple(np.indices(j.shape))
    return acl_ests[k[:axis] + (j,) + k[axis:]]

def _default_burnin(M):
    return 1.0/(M + 1.0)

def emcee_chain_autocorrelation_lengths(chain, M=5, fburnin=None):
    r"""Returns an array giving the ACL for each parameter in the given
    emcee chain.

    :param chain: The emcee sampler chain.

    :param M: See :func:`autocorrelation_length_estimate`

    :param fburnin: Discard the first ``fburnin`` fraction of the
      samples as burn-in before computing the ACLs.  Default is to
      discard the first :math:`1/(M+1)`, ensuring that at least one
      ACL is discarded.

    """

    if fburnin is None:
        fburnin = _default_burnin(M)

    istart = int(round(fburnin*chain.shape[1]))

    return np.array([autocorrelation_length_estimate(np.mean(chain[:,istart:,k], axis=0)) for k in range(chain.shape[2])])

def emcee_ptchain_autocorrelation_lengths(ptchain, M=5, fburnin=None):
    r"""Returns an array of shape ``(Ntemp, Nparams)`` giving the estimated
    autocorrelation lengths for each parameter across each temperature
    of the parallel-tempered set of chains.  If a particular ACL
    cannot be estimated, that element of the array will be ``None``.
    See :func:`emcee_chain_autocorrelation_lengths` for a description
    of the optional arguments.

    """

    return np.array([emcee_chain_autocorrelation_lengths(ptchain[i,...], M=M, fburnin=fburnin) for i in range(ptchain.shape[0])])

def emcee_thinned_chain(chain, M=5, fburnin=None):
    r"""Returns a thinned, burned-in version of the emcee chain.

    :param chain: The emcee sampler chain.

    :param M: See :func:`autocorrelation_length_estimate`

    :param fburnin: Discard the first ``fburnin`` fraction of the
      samples as burn-in before computing the ACLs.  Default is to
      discard the first :math:`1/(M+1)`, ensuring that at least one
      ACL is discarded.

    """

    if fburnin is None:
        fburnin = _default_burnin(M)

    istart = int(round(fburnin*chain.shape[1]))

    acls = emcee_chain_autocorrelation_lengths(chain, M=M, fburnin=fburnin)

    if any(ac is None for ac in acls):
        return None

    tau = int(np.ceil(np.max(acls)))

    return chain[:,istart::tau,:]

def emcee_thinned_ptchain(ptchain, M=5, fburnin=None):
    r"""Returns a thinned, burned in version of the emcee parallel-tempered
    chains in ``ptchain``, or ``None`` if it is not possible to
    estimate an ACL for some component of the chain.

    """

    if fburnin is None:
        fburnin = _default_burnin(M)

    istart = int(round(fburnin*ptchain.shape[2]))

    acls = emcee_ptchain_autocorrelation_lengths(ptchain, M=M, fburnin=fburnin)

    if any(ac is None for ac in acls.flatten()):
        return None

    tau = int(np.ceil(np.max(acls)))

    return ptchain[:,:,istart::tau,:]

def plot_emcee_chain_autocorrelation_functions(chain, fburnin=None):
    r"""Plots a grid of the autocorrelation function (post burnin) for each
    of the parameters in the given chain.

    """

    if fburnin is None:
        fburnin = _default_burnin(5)

    istart = int(round(fburnin*chain.shape[1]))

    chain = chain[:,istart:,:]

    npar = chain.shape[2]
    nside = int(np.ceil(np.sqrt(npar)))

    for i in range(npar):
        plt.subplot(nside, nside, i+1)
        plt.plot(autocorrelation_function(np.mean(chain[:,:,i], axis=0)))
<|endoftext|>


<commit_before>###
# Copyright (c) 2005, Jeremiah Fincher
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

from supybot.test import *

import sqlite3

class KarmaTestCase(ChannelPluginTestCase):
    plugins = ('Karma',)
    def testKarma(self):
        self.assertError('karma')
        self.assertRegexp('karma foobar', 'neutral karma')
        try:
            conf.replyWhenNotCommand = True
            self.assertNoResponse('foobar++', 2)
        finally:
            conf.replyWhenNotCommand = False
        self.assertRegexp('karma foobar', 'increased 1.*total.*1')
        self.assertRegexp('karma FOOBAR', 'increased 1.*total.*1')
        self.assertNoResponse('foobar--', 2)
        self.assertRegexp('karma foobar', 'decreased 1.*total.*0')
        self.assertRegexp('karma FOOBAR', 'decreased 1.*total.*0')
        self.assertNoResponse('FOO++', 2)
        self.assertNoResponse('BAR--', 2)
        self.assertRegexp('karma foo bar foobar', '.*foo.*foobar.*bar.*')
        self.assertRegexp('karma FOO BAR FOOBAR', '.*foo.*foobar.*bar.*')
        self.assertRegexp('karma FOO BAR FOOBAR',
                          '.*FOO.*foobar.*BAR.*', flags=0)
        self.assertRegexp('karma foo bar foobar asdfjkl', 'asdfjkl')
        # Test case-insensitive
        self.assertNoResponse('MOO++', 2)
        self.assertRegexp('karma moo',
                          'Karma for [\'"]moo[\'"].*increased 1.*total.*1')
        self.assertRegexp('karma MoO',
                          'Karma for [\'"]MoO[\'"].*increased 1.*total.*1')

    def testKarmaRankingDisplayConfigurable(self):
        try:
            orig = conf.supybot.plugins.Karma.response()
            conf.supybot.plugins.Karma.response.setValue(True)
            original = conf.supybot.plugins.Karma.rankingDisplay()
            self.assertNotError('foo++')
            self.assertNotError('foo++')
            self.assertNotError('foo++')
            self.assertNotError('foo++')
            self.assertNotError('bar++')
            self.assertNotError('bar++')
            self.assertNotError('bar++')
            self.assertNotError('baz++')
            self.assertNotError('baz++')
            self.assertNotError('quux++')
            self.assertNotError('xuuq--')
            self.assertNotError('zab--')
            self.assertNotError('zab--')
            self.assertNotError('rab--')
            self.assertNotError('rab--')
            self.assertNotError('rab--')
            self.assertNotError('oof--')
            self.assertNotError('oof--')
            self.assertNotError('oof--')
            self.assertNotError('oof--')
            self.assertRegexp('karma', 'foo.*bar.*baz.*oof.*rab.*zab')
            conf.supybot.plugins.Karma.rankingDisplay.setValue(4)
            self.assertRegexp('karma', 'foo.*bar.*baz.*quux')
        finally:
            conf.supybot.plugins.Karma.response.setValue(orig)
            conf.supybot.plugins.Karma.rankingDisplay.setValue(original)

    def testMost(self):
        self.assertError('most increased')
        self.assertError('most decreased')
        self.assertError('most active')
        self.assertHelp('most aldsfkj')
        self.assertNoResponse('foo++', 1)
        self.assertNoResponse('foo++', 1)
        self.assertNoResponse('bar++', 1)
        self.assertNoResponse('bar--', 1)
        self.assertNoResponse('bar--', 1)
        self.assertRegexp('karma most active', 'bar.*foo')
        self.assertRegexp('karma most increased', 'foo.*bar')
        self.assertRegexp('karma most decreased', 'bar.*foo')
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertRegexp('karma most active', 'foo.*bar')
        self.assertRegexp('karma most increased', 'foo.*bar')
        self.assertRegexp('karma most decreased', 'foo.*bar')

    def testSimpleOutput(self):
        try:
            orig = conf.supybot.plugins.Karma.simpleOutput()
            conf.supybot.plugins.Karma.simpleOutput.setValue(True)
            self.assertNoResponse('foo++', 2)
            self.assertResponse('karma foo', 'foo: 1')
            self.assertNoResponse('bar--', 2)
            self.assertResponse('karma bar', 'bar: -1')
        finally:
            conf.supybot.plugins.Karma.simpleOutput.setValue(orig)

    def testSelfRating(self):
        nick = self.nick
        try:
            orig = conf.supybot.plugins.Karma.allowSelfRating()
            conf.supybot.plugins.Karma.allowSelfRating.setValue(False)
            self.assertError('%s++' % nick)
            self.assertResponse('karma %s' % nick,
                                '%s has neutral karma.' % nick)
            conf.supybot.plugins.Karma.allowSelfRating.setValue(True)
            self.assertNoResponse('%s++' % nick, 2)
            self.assertRegexp('karma %s' % nick,
                  'Karma for [\'"]%s[\'"].*increased 1.*total.*1' % nick)
        finally:
            conf.supybot.plugins.Karma.allowSelfRating.setValue(orig)

    def testKarmaOutputConfigurable(self):
        self.assertNoResponse('foo++', 2)
        try:
            orig = conf.supybot.plugins.Karma.response()
            conf.supybot.plugins.Karma.response.setValue(True)
            self.assertNotError('foo++')
        finally:
            conf.supybot.plugins.Karma.response.setValue(orig)

    def testKarmaMostDisplayConfigurable(self):
        self.assertNoResponse('foo++', 1)
        self.assertNoResponse('foo++', 1)
        self.assertNoResponse('bar++', 1)
        self.assertNoResponse('bar--', 1)
        self.assertNoResponse('bar--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        try:
            orig = conf.supybot.plugins.Karma.mostDisplay()
            conf.supybot.plugins.Karma.mostDisplay.setValue(1)
            self.assertRegexp('karma most active', '(?!bar)')
            conf.supybot.plugins.Karma.mostDisplay.setValue(25)
            self.assertRegexp('karma most active', 'bar')
        finally:
            conf.supybot.plugins.Karma.mostDisplay.setValue(orig)

    def testIncreaseKarmaWithNickNotCallingInvalidCommand(self):
        self.assertSnarfNoResponse('%s: foo++' % self.irc.nick, 3)

    def testClear(self):
        self.assertNoResponse('foo++', 1)
        self.assertRegexp('karma foo', '1')
        self.assertNotError('karma clear foo')
        self.assertRegexp('karma foo', '0')
        self.assertNotRegexp('karma foo', '1')

#        def testNoKarmaDunno(self):
#            self.assertNotError('load Infobot')
#            self.assertNoResponse('foo++')

    def testMultiWordKarma(self):
        self.assertNoResponse('(foo bar)++', 1)
        self.assertRegexp('karma "foo bar"', '1')

    def testUnaddressedKarma(self):
        karma = conf.supybot.plugins.Karma
        resp = karma.response()
        unaddressed = karma.allowUnaddressedKarma()
        try:
            karma.response.setValue(True)
            karma.allowUnaddressedKarma.setValue(True)
            for m in ('++', '--'):
                self.assertRegexp('foo%s' % m, 'is now')
                self.assertSnarfRegexp('foo%s' % m, 'is now')
                #self.assertNoResponse('foo bar%s' % m)
                #self.assertSnarfNoResponse('foo bar%s' % m)
                self.assertRegexp('(foo bar)%s' % m, 'is now')
                self.assertSnarfRegexp('(foo bar)%s' % m, 'is now')
        finally:
            karma.response.setValue(resp)
            karma.allowUnaddressedKarma.setValue(unaddressed)

# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
<commit_msg>Karma: Fix tests.<commit_after>###
# Copyright (c) 2005, Jeremiah Fincher
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

from supybot.test import *

import sqlite3

class KarmaTestCase(ChannelPluginTestCase):
    plugins = ('Karma',)
    def testKarma(self):
        self.assertError('karma')
        self.assertRegexp('karma foobar', 'neutral karma')
        try:
            conf.replyWhenNotCommand = True
            self.assertNoResponse('foobar++', 2)
        finally:
            conf.replyWhenNotCommand = False
        self.assertRegexp('karma foobar', 'increased 1.*total.*1')
        self.assertRegexp('karma FOOBAR', 'increased 1.*total.*1')
        self.assertNoResponse('foobar--', 2)
        self.assertRegexp('karma foobar', 'decreased 1.*total.*0')
        self.assertRegexp('karma FOOBAR', 'decreased 1.*total.*0')
        self.assertNoResponse('FOO++', 2)
        self.assertNoResponse('BAR--', 2)
        self.assertRegexp('karma foo bar foobar', '.*foo.*foobar.*bar.*')
        self.assertRegexp('karma FOO BAR FOOBAR', '.*foo.*foobar.*bar.*')
        self.assertRegexp('karma FOO BAR FOOBAR',
                          '.*FOO.*foobar.*BAR.*', flags=0)
        self.assertRegexp('karma foo bar foobar asdfjkl', 'asdfjkl')
        # Test case-insensitive
        self.assertNoResponse('MOO++', 2)
        self.assertRegexp('karma moo',
                          'Karma for [\'"]moo[\'"].*increased 1.*total.*1')
        self.assertRegexp('karma MoO',
                          'Karma for [\'"]MoO[\'"].*increased 1.*total.*1')

    def testKarmaRankingDisplayConfigurable(self):
        try:
            orig = conf.supybot.plugins.Karma.response()
            conf.supybot.plugins.Karma.response.setValue(True)
            original = conf.supybot.plugins.Karma.rankingDisplay()
            self.assertNotError('foo++')
            self.assertNotError('foo++')
            self.assertNotError('foo++')
            self.assertNotError('foo++')
            self.assertNotError('bar++')
            self.assertNotError('bar++')
            self.assertNotError('bar++')
            self.assertNotError('baz++')
            self.assertNotError('baz++')
            self.assertNotError('quux++')
            self.assertNotError('xuuq--')
            self.assertNotError('zab--')
            self.assertNotError('zab--')
            self.assertNotError('rab--')
            self.assertNotError('rab--')
            self.assertNotError('rab--')
            self.assertNotError('oof--')
            self.assertNotError('oof--')
            self.assertNotError('oof--')
            self.assertNotError('oof--')
            self.assertRegexp('karma', 'foo.*bar.*baz.*oof.*rab.*zab')
            conf.supybot.plugins.Karma.rankingDisplay.setValue(4)
            self.assertRegexp('karma', 'foo.*bar.*baz.*quux')
        finally:
            conf.supybot.plugins.Karma.response.setValue(orig)
            conf.supybot.plugins.Karma.rankingDisplay.setValue(original)

    def testMost(self):
        self.assertError('most increased')
        self.assertError('most decreased')
        self.assertError('most active')
        self.assertHelp('most aldsfkj')
        self.assertNoResponse('foo++', 1)
        self.assertNoResponse('foo++', 1)
        self.assertNoResponse('bar++', 1)
        self.assertNoResponse('bar--', 1)
        self.assertNoResponse('bar--', 1)
        self.assertRegexp('karma most active', 'bar.*foo')
        self.assertRegexp('karma most increased', 'foo.*bar')
        self.assertRegexp('karma most decreased', 'bar.*foo')
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertRegexp('karma most active', 'foo.*bar')
        self.assertRegexp('karma most increased', 'foo.*bar')
        self.assertRegexp('karma most decreased', 'foo.*bar')

    def testSimpleOutput(self):
        try:
            orig = conf.supybot.plugins.Karma.simpleOutput()
            conf.supybot.plugins.Karma.simpleOutput.setValue(True)
            self.assertNoResponse('foo++', 2)
            self.assertResponse('karma foo', 'foo: 1')
            self.assertNoResponse('bar--', 2)
            self.assertResponse('karma bar', 'bar: -1')
        finally:
            conf.supybot.plugins.Karma.simpleOutput.setValue(orig)

    def testSelfRating(self):
        nick = self.nick
        try:
            orig = conf.supybot.plugins.Karma.allowSelfRating()
            conf.supybot.plugins.Karma.allowSelfRating.setValue(False)
            self.assertError('%s++' % nick)
            self.assertResponse('karma %s' % nick,
                                '%s has neutral karma.' % nick)
            conf.supybot.plugins.Karma.allowSelfRating.setValue(True)
            self.assertNoResponse('%s++' % nick, 2)
            self.assertRegexp('karma %s' % nick,
                  'Karma for [\'"]%s[\'"].*increased 1.*total.*1' % nick)
        finally:
            conf.supybot.plugins.Karma.allowSelfRating.setValue(orig)

    def testKarmaOutputConfigurable(self):
        self.assertNoResponse('foo++', 2)
        try:
            orig = conf.supybot.plugins.Karma.response()
            conf.supybot.plugins.Karma.response.setValue(True)
            self.assertNotError('foo++')
        finally:
            conf.supybot.plugins.Karma.response.setValue(orig)

    def testKarmaMostDisplayConfigurable(self):
        self.assertNoResponse('foo++', 1)
        self.assertNoResponse('foo++', 1)
        self.assertNoResponse('bar++', 1)
        self.assertNoResponse('bar--', 1)
        self.assertNoResponse('bar--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        self.assertNoResponse('foo--', 1)
        try:
            orig = conf.supybot.plugins.Karma.mostDisplay()
            conf.supybot.plugins.Karma.mostDisplay.setValue(1)
            self.assertRegexp('karma most active', '(?!bar)')
            conf.supybot.plugins.Karma.mostDisplay.setValue(25)
            self.assertRegexp('karma most active', 'bar')
        finally:
            conf.supybot.plugins.Karma.mostDisplay.setValue(orig)

    def testIncreaseKarmaWithNickNotCallingInvalidCommand(self):
        self.assertSnarfNoResponse('%s: foo++' % self.irc.nick, 3)

    def testClear(self):
        self.assertNoResponse('foo++', 1)
        self.assertRegexp('karma foo', '1')
        self.assertNotError('karma clear foo')
        self.assertRegexp('karma foo', 'neutral')
        self.assertNotRegexp('karma foo', '1')

#        def testNoKarmaDunno(self):
#            self.assertNotError('load Infobot')
#            self.assertNoResponse('foo++')

    def testMultiWordKarma(self):
        self.assertNoResponse('(foo bar)++', 1)
        self.assertRegexp('karma "foo bar"', '1')

    def testUnaddressedKarma(self):
        karma = conf.supybot.plugins.Karma
        resp = karma.response()
        unaddressed = karma.allowUnaddressedKarma()
        try:
            karma.response.setValue(True)
            karma.allowUnaddressedKarma.setValue(True)
            for m in ('++', '--'):
                self.assertRegexp('foo%s' % m, 'is now')
                self.assertSnarfRegexp('foo%s' % m, 'is now')
                #self.assertNoResponse('foo bar%s' % m)
                #self.assertSnarfNoResponse('foo bar%s' % m)
                self.assertRegexp('(foo bar)%s' % m, 'is now')
                self.assertSnarfRegexp('(foo bar)%s' % m, 'is now')
        finally:
            karma.response.setValue(resp)
            karma.allowUnaddressedKarma.setValue(unaddressed)

# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
<|endoftext|>


<commit_before>import plugintypes
from urllib import request
from urllib import error

class CalculatorPlugin(plugintypes.TelegramPlugin):
    """
    Calculator that uses mathjs to evaluate
    """
    patterns = [
        "^!calc (.*)$",
        ]

    usage = [
        "!calc expression: evaluates the expression and sends the result.",
    ]

    def run(self, msg, matches):
        return self.mathjs(matches.group(1))

    def mathjs(self, expr):
        try:
            base_url = 'http://api.mathjs.org/v1'
            resp = request.urlopen("{0}/?expr={1}".format(base_url, expr))
        except error.HTTPError as err:
            if err.code == 400:
                return err.read().decode('utf-8')
            else:
                return "Unexpected error\nIs api.mathjs.org up?"

        text = 'Result: ' + resp.read().decode('utf-8')

        return text<commit_msg>Quote the HTML entities in expression<commit_after>import plugintypes
from urllib import request, error, parse

class CalculatorPlugin(plugintypes.TelegramPlugin):
    """
    Calculator that uses mathjs to evaluate
    """
    patterns = [
        "^!calc (.*)$",
        ]

    usage = [
        "!calc expression: evaluates the expression and sends the result.",
    ]

    def run(self, msg, matches):
        return self.mathjs(matches.group(1))

    def mathjs(self, expr):
        try:
            base_url = 'http://api.mathjs.org/v1'
            resp = request.urlopen("{0}/?expr={1}".format(base_url,
                parse.quote_plus(expr)))
        except error.HTTPError as err:
            if err.code == 400:
                return err.read().decode('utf-8')
            else:
                return "Unexpected error\nIs api.mathjs.org up?"

        text = 'Result: ' + resp.read().decode('utf-8')

        return text
<|endoftext|>


<commit_before>from plugins.categories import IRegularCommand
import string
import random
from threading import Timer

class PlayList:
    def __init__(self):
        self.players = []

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

    def append(self,item):
        self.players.append(item)

    def find(self, user):
        ii = 0
        for player in self.players:
            if player.name == user:
                return (player, ii)
            ii = ii + 1
        return False

class Player:
    def __init__(self, name, lives):
        self.name = name
        self.lives = lives

class TheMysteryBox (IRegularCommand):

    def __init__(self):
        self.playerlist = PlayList()
        self.box = 0
        self.playerIndex = 0
        self.playing = False
        self.admin = ''

    def command_mysterybox(self, user, channel, args):
        if(len(args) == 0):
            if (not self.playing):
                return self.register(user)
        elif (args[0] == '2' or args[0] == '1'):
            if (self.playing and  self.playerlist.find(user) and self.playerIndex == self.playerlist.find(user)[1]):
                return self.move(int(float(args[0])),user)
        elif (args[0] == 'stop' and user == self.admin):
            return self.stop()
        elif (args[0] == 'start' and self.playing == False):
            return self.start(user)

    def register(self, name):
        if (self.playerlist.find(name) == False):
            self.playerlist.append(Player(name,2))
            return string.split(name,'!')[0] + ' has been registered'
        else:
            return string.split(name,'!')[0] + ' is already registered'

    def start(self, admin):
        if (len(self.playerlist.players) < 2):
            return 'less than 2 people registered!'
        else:
            self.playing = True
            self.box = random.randrange(8,14)
            self.admin = admin
            self.playerIndex = random.randrange(0,len(self.playerlist.players))
            self.timeout = Timer(20.0,self.boom,[self, self.playerlist.players[self.playerIndex].name])
            self.timeout.start()
            return string.split(str(self.playerlist.players[self.playerIndex].name),'!')[0] +' has the box with ' + str(self.box) + ' left on the clock'

    def stop(self):
        self.playerlist = PlayList()
        self.box = 0
        self.playerIndex = 0
        self.playing = False
        self.admin = ''
        self.timeout.cancel()
        return 'Admin has stopped the game'

    def move(self, move, user):
        self.timeout.cancel()
        if (move > self.box):
            returnstr = self.boom(user)
        elif (move == self.box):
            returnstr = self.boom(self.playerlist.players[(self.playerlist.find(user)[1]+1)%len(self.playerlist.players)].name)
        else:
            self.box = self.box - move
            self.playerIndex = (self.playerIndex+1)%len(self.playerlist.players)
            returnstr = string.split(user,'!')[0] + ' passed the box to ' + string.split(self.playerlist.players[self.playerIndex].name,'!')[0] + '. The box displays the number ' + str(self.box) + '!'
        if (self.playing): #restart the timeout
            self.timeout = Timer(20.0,self.boom,[self, self.playerlist.players[self.playerIndex].name])
            self.timeout.start()
        return returnstr

    def boom(self, user):
        player = self.playerlist.find(user)[0]
        player.lives = player.lives-1
        returnstr = 'Boom! ' + string.split(user,'!')[0] + ' has been blown up! They have ' + str(player.lives) + ' lives left.'
        if (player.lives < 1):
            returnstr = returnstr + ' ' + string.split(player.name,'!')[0] + ' is now dead.'
            self.playerlist.players.remove(self.playerlist.find(user)[0])
        if (len(self.playerlist.players) == 1):
            returnstr = returnstr + '\n' + string.split(self.playerlist.players[0].name,'!')[0] + ' has won!'
            self.playerlist = PlayList()
            self.box = 0
            self.playerIndex = 0
            self.playing = False
            self.admin = ''
        else:
            self.box = self.box = random.randrange(8,14)
            self.playerIndex = random.randrange(0,len(self.playerlist.players))
            returnstr = returnstr + '\n' + string.split(str(self.playerlist.players[self.playerIndex].name),'!')[0] +' has the box with ' + str(self.box) + ' left on the clock'
        return returnstr<commit_msg>fixed Timer call<commit_after>from plugins.categories import IRegularCommand
import string
import random
from threading import Timer

class PlayList:
    def __init__(self):
        self.players = []

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

    def append(self,item):
        self.players.append(item)

    def find(self, user):
        ii = 0
        for player in self.players:
            if player.name == user:
                return (player, ii)
            ii = ii + 1
        return False

class Player:
    def __init__(self, name, lives):
        self.name = name
        self.lives = lives

class TheMysteryBox (IRegularCommand):

    def __init__(self):
        self.playerlist = PlayList()
        self.box = 0
        self.playerIndex = 0
        self.playing = False
        self.admin = ''

    def command_mysterybox(self, user, channel, args):
        if(len(args) == 0):
            if (not self.playing):
                return self.register(user)
        elif (args[0] == '2' or args[0] == '1'):
            if (self.playing and  self.playerlist.find(user) and self.playerIndex == self.playerlist.find(user)[1]):
                return self.move(int(float(args[0])),user)
        elif (args[0] == 'stop' and user == self.admin):
            return self.stop()
        elif (args[0] == 'start' and self.playing == False):
            return self.start(user)

    def register(self, name):
        if (self.playerlist.find(name) == False):
            self.playerlist.append(Player(name,2))
            return string.split(name,'!')[0] + ' has been registered'
        else:
            return string.split(name,'!')[0] + ' is already registered'

    def start(self, admin):
        if (len(self.playerlist.players) < 2):
            return 'less than 2 people registered!'
        else:
            self.playing = True
            self.box = random.randrange(8,14)
            self.admin = admin
            self.playerIndex = random.randrange(0,len(self.playerlist.players))
            self.timeout = Timer(20.0,self.boom,[self.playerlist.players[self.playerIndex].name])
            self.timeout.start()
            return string.split(str(self.playerlist.players[self.playerIndex].name),'!')[0] +' has the box with ' + str(self.box) + ' left on the clock'

    def stop(self):
        self.playerlist = PlayList()
        self.box = 0
        self.playerIndex = 0
        self.playing = False
        self.admin = ''
        self.timeout.cancel()
        return 'Admin has stopped the game'

    def move(self, move, user):
        self.timeout.cancel()
        if (move > self.box):
            returnstr = self.boom(user)
        elif (move == self.box):
            returnstr = self.boom(self.playerlist.players[(self.playerlist.find(user)[1]+1)%len(self.playerlist.players)].name)
        else:
            self.box = self.box - move
            self.playerIndex = (self.playerIndex+1)%len(self.playerlist.players)
            returnstr = string.split(user,'!')[0] + ' passed the box to ' + string.split(self.playerlist.players[self.playerIndex].name,'!')[0] + '. The box displays the number ' + str(self.box) + '!'
        if (self.playing): #restart the timeout
            self.timeout = Timer(20.0,self.boom,[self.playerlist.players[self.playerIndex].name])
            self.timeout.start()
        return returnstr

    def boom(self, user):
        player = self.playerlist.find(user)[0]
        player.lives = player.lives-1
        returnstr = 'Boom! ' + string.split(user,'!')[0] + ' has been blown up! They have ' + str(player.lives) + ' lives left.'
        if (player.lives < 1):
            returnstr = returnstr + ' ' + string.split(player.name,'!')[0] + ' is now dead.'
            self.playerlist.players.remove(self.playerlist.find(user)[0])
        if (len(self.playerlist.players) == 1):
            returnstr = returnstr + '\n' + string.split(self.playerlist.players[0].name,'!')[0] + ' has won!'
            self.playerlist = PlayList()
            self.box = 0
            self.playerIndex = 0
            self.playing = False
            self.admin = ''
        else:
            self.box = self.box = random.randrange(8,14)
            self.playerIndex = random.randrange(0,len(self.playerlist.players))
            returnstr = returnstr + '\n' + string.split(str(self.playerlist.players[self.playerIndex].name),'!')[0] +' has the box with ' + str(self.box) + ' left on the clock'
        return returnstr<|endoftext|>


<commit_before>import math
import re
import time

from util import hook, urlnorm, timesince

url_re = re.compile(r'([a-zA-Z]+://|www\.)[^ ]*')

expiration_period = 60 * 60 * 24  # 1 day

rate_limit_period = 60 * 3 # 3 minutes
rate_limit_count = 3

ignored_urls = [urlnorm.normalize("http://google.com")]

def db_connect(bot, server):
    "check to see that our db has the the seen table and return a dbection."
    db = bot.get_db_connection(server)
    db.execute("create table if not exists urlhistory"
                 "(chan, url, nick, time)")
    db.commit()
    return db

def insert_history(db, chan, url, nick):
    now = time.time()
    db.execute("insert into urlhistory(chan, url, nick, time) "
                 "values(?,?,?,?)", (chan, url, nick, time.time()))
    db.commit()

def get_history_duration(db, chan, url, duration):
    return db.execute("select nick, time from urlhistory where "
            "chan=? and url=? order by time desc", (chan, url)).fetchall()

def get_history(db, chan, url):
    db.execute("delete from urlhistory where time < ?",
                 (time.time() - duration,))
    return get_history_duration(db, chan, url, expiration_period)

def get_recent_links_count(db, chan, url):
    return len(get_history_duration(db, chan, url, rate_limit_period))

def nicklist(nicks):
    nicks = sorted(dict(nicks), key=unicode.lower)
    if len(nicks) <= 2:
        return ' and '.join(nicks)
    else:
        return ', and '.join((', '.join(nicks[:-1]), nicks[-1]))

def format_reply(history):
    if not history:
        return

    last_nick, recent_time = history[0]
    last_time = timesince.timesince(recent_time)

    if len(history) == 1:
        return "%s linked that %s ago." % (last_nick, last_time)

    hour_span = math.ceil((time.time() - history[-1][1]) / 3600)
    hour_span = '%.0f hours' % hour_span if hour_span > 1 else 'hour'

    hlen = len(history)
    ordinal = ["once", "twice", "%d times" % hlen][min(hlen, 3) - 1]

    if len(dict(history)) == 1:
        last = "last linked %s ago" % last_time
    else:
        last = "last linked by %s %s ago" % (last_nick, last_time)

    return "that url has been posted %s in the past %s by %s (%s)." % (ordinal,
            hour_span, nicklist(history), last)

@hook.command(hook=r'(.*)', prefix=False)
def urlinput(inp, nick='', chan='', server='', reply=None, bot=None):
    m = url_re.search(inp.encode('utf8'))
    if not m:
        return

    # URL detected
    db = db_connect(bot, server)
    url = urlnorm.normalize(m.group(0))
    if url not in ignored_urls:
        history = get_history(db, chan, url)
        recent_history = get_recent_links_count(db, chan, url)
        insert_history(db, chan, url, nick)
        if nick not in dict(history) and recent_history < rate_limit_count:
            return format_reply(history)
<commit_msg>revert urlhistory to non-broken version<commit_after>import math
import re
import time

from util import hook, urlnorm, timesince

url_re = re.compile(r'([a-zA-Z]+://|www\.)[^ ]*')

expiration_period = 60 * 60 * 24  # 1 day

ignored_urls = [urlnorm.normalize("http://google.com")]

def db_connect(bot, server):
    "check to see that our db has the the seen table and return a dbection."
    db = bot.get_db_connection(server)
    db.execute("create table if not exists urlhistory"
                 "(chan, url, nick, time)")
    db.commit()
    return db

def insert_history(db, chan, url, nick):
    now = time.time()
    db.execute("insert into urlhistory(chan, url, nick, time) "
                 "values(?,?,?,?)", (chan, url, nick, time.time()))
    db.commit()

def get_history(db, chan, url):
    db.execute("delete from urlhistory where time < ?",
                 (time.time() - expiration_period,))
    return db.execute("select nick, time from urlhistory where "
            "chan=? and url=? order by time desc", (chan, url)).fetchall()

def nicklist(nicks):
    nicks = sorted(dict(nicks), key=unicode.lower)
    if len(nicks) <= 2:
        return ' and '.join(nicks)
    else:
        return ', and '.join((', '.join(nicks[:-1]), nicks[-1]))

def format_reply(history):
    if not history:
        return

    last_nick, recent_time = history[0]
    last_time = timesince.timesince(recent_time)

    if len(history) == 1:
        return "%s linked that %s ago." % (last_nick, last_time)

    hour_span = math.ceil((time.time() - history[-1][1]) / 3600)
    hour_span = '%.0f hours' % hour_span if hour_span > 1 else 'hour'

    hlen = len(history)
    ordinal = ["once", "twice", "%d times" % hlen][min(hlen, 3) - 1]

    if len(dict(history)) == 1:
        last = "last linked %s ago" % last_time
    else:
        last = "last linked by %s %s ago" % (last_nick, last_time)

    return "that url has been posted %s in the past %s by %s (%s)." % (ordinal,
            hour_span, nicklist(history), last)

@hook.command(hook=r'(.*)', prefix=False)
def urlinput(inp, nick='', chan='', server='', reply=None, bot=None):
    m = url_re.search(inp.encode('utf8'))
    if not m:
        return

    # URL detected
    db = db_connect(bot, server)
    url = urlnorm.normalize(m.group(0))
    if url not in ignored_urls:
        history = get_history(db, chan, url)
        insert_history(db, chan, url, nick)
        if nick not in dict(history):
            return format_reply(history)
<|endoftext|>


<commit_before>from flask import request, make_response, url_for, session, render_template, abort, redirect
from pmg_frontend import app
import requests
from datetime import datetime, date
from pmg_frontend import logger

API_HOST = app.config['API_HOST']
error_bad_request = 400

def url(action, param=None):
    u =  "http://{host}/{action}/".format(host=API_HOST, action=action)
    if param:
        return u + param + "/"
    return u

@app.route('/')
def root():
    return redirect('/bills/')

@app.route('/bills/all/')
@app.route('/bills/<year>/')
@app.route('/bills/<bill_type>/all/')
@app.route('/bills/<bill_type>/<year>/')
def index(year=None, bill_type=None):
    """
    Display a list of available bills, with some summary info and a link to each bill's detail page.
    """

    logger.debug("landing page called")
    try:
        if year is not None:
            year = int(year)
    except ValueError as e:
        logger.debug(e)
        abort(error_bad_request)

    start_year = date.today().year
    if not year:
        year = start_year

    tmp = "bill"
    page_title = "All bills"
    bills = []

    while (not bills) and (start_year > 2007):  # ensure that we start on the first page that actually has content
        year_list = range(start_year, 2005, -1)

        if bill_type and bill_type.lower() in ["pmb", "draft", "current"]:
            tmp = bill_type.lower()
            if bill_type.lower() == "pmb":
                page_title = "Private Member Bills"
                year_list = range(start_year, 2011, -1)
            elif bill_type.lower() == "draft":
                page_title = "Draft Bills"
                year_list = range(start_year, 2007, -1)
            elif bill_type.lower() == "current":
                page_title = "Current Bills"
                start_year = None
                year = None
                year_list = []

        api_url = url(tmp)

        if year:
            r = requests.get(api_url + "year/" + str(year) + "/")
        else:
            r = requests.get(api_url)
        bills = r.json()
        if not bills and bill_type.lower() != "current":
            start_year -= 1
            year -= 1

    status_dict = {
        "na": ("in progress", "label-primary"),
        "ncop": ("in progress", "label-primary"),
        "assent": ("submitted to the president", "label-warning"),
        "enacted": ("signed into law", "label-success"),
        "withdrawn": ("withdrawn", "label-default"),
        "lapsed": ("lapsed", "label-default"),
        }

    return render_template(
        'index.html', title=page_title, bill_type=bill_type, year_list=year_list,
        year=year, bills=bills, status_dict=status_dict, api_url=api_url
    )

@app.route('/bills/')
def bills():

    return render_template('bills.html')

@app.route('/bills/explained/')
def bills_explained():

    return render_template('bills_explained.html')

@app.route('/bill/<bill_prefix>-<bill_year>/')
def bill_redirect(bill_prefix, bill_year):
    """
    Redirect to bill's page, based on the bill's code, rather than a backend ID.
    """

    try:
        bill_year = int(bill_year)
        bill_code = bill_prefix + "-" + str(bill_year)
    except:
        abort(error_bad_request)

    api_url = url("bill", str(bill_code))
    r = requests.get(api_url)
    bill = r.json()

    return redirect('/bill/' + str(bill['bill_id']) + "/", 301)

@app.route('/bill/<bill_id>/')
def detail(bill_id=None):
    """
    Display all available detail for the requested bill.
    """

    try:
        bill_id = int(bill_id)
    except:
        abort(error_bad_request)

    logger.debug("detail page called")
    api_url = url("bill", str(bill_id))
    r = requests.get(api_url)
    bill = r.json()

    entries = bill["entries"]

    # separate special entries from the rest of the list
    version_types = ["bill-version", "act"]
    special_types = ["gazette", "whitepaper", "memorandum", "greenpaper", "draft"]

    bill["entries"] = [entry for entry in entries if entry["type"] not in version_types + special_types]

    bill["versions"] = [entry for entry in entries if entry["type"] in version_types]
    special_entries = [entry for entry in entries if entry["type"] in special_types]
    for entry in special_entries:
        bill[entry["type"]] = entry

    return render_template('detail.html', bill=bill)
<commit_msg>Pass on HTTP errors between frontend and API to user, rather than just showing generic 500 error.<commit_after>from flask import request, make_response, url_for, session, render_template, abort, redirect
from pmg_frontend import app
import requests
from datetime import datetime, date
from pmg_frontend import logger

API_HOST = app.config['API_HOST']
error_bad_request = 400

def url(action, param=None):
    u =  "http://{host}/{action}/".format(host=API_HOST, action=action)
    if param:
        return u + param + "/"
    return u

@app.route('/')
def root():
    return redirect('/bills/')

@app.route('/bills/all/')
@app.route('/bills/<year>/')
@app.route('/bills/<bill_type>/all/')
@app.route('/bills/<bill_type>/<year>/')
def index(year=None, bill_type=None):
    """
    Display a list of available bills, with some summary info and a link to each bill's detail page.
    """

    logger.debug("landing page called")
    try:
        if year is not None:
            year = int(year)
    except ValueError as e:
        logger.debug(e)
        abort(error_bad_request)

    start_year = date.today().year
    if not year:
        year = start_year

    tmp = "bill"
    page_title = "All bills"
    bills = []

    while (not bills) and (start_year > 2007):  # ensure that we start on the first page that actually has content
        year_list = range(start_year, 2005, -1)

        if bill_type and bill_type.lower() in ["pmb", "draft", "current"]:
            tmp = bill_type.lower()
            if bill_type.lower() == "pmb":
                page_title = "Private Member Bills"
                year_list = range(start_year, 2011, -1)
            elif bill_type.lower() == "draft":
                page_title = "Draft Bills"
                year_list = range(start_year, 2007, -1)
            elif bill_type.lower() == "current":
                page_title = "Current Bills"
                start_year = None
                year = None
                year_list = []

        api_url = url(tmp)

        if year:
            r = requests.get(api_url + "year/" + str(year) + "/")
        else:
            r = requests.get(api_url)

        if not r.status_code == 200:
            return(r.text, r.status_code)

        bills = r.json()
        if not bills and bill_type.lower() != "current":
            start_year -= 1
            year -= 1

    status_dict = {
        "na": ("in progress", "label-primary"),
        "ncop": ("in progress", "label-primary"),
        "assent": ("submitted to the president", "label-warning"),
        "enacted": ("signed into law", "label-success"),
        "withdrawn": ("withdrawn", "label-default"),
        "lapsed": ("lapsed", "label-default"),
        }

    return render_template(
        'index.html', title=page_title, bill_type=bill_type, year_list=year_list,
        year=year, bills=bills, status_dict=status_dict, api_url=api_url
    )

@app.route('/bills/')
def bills():

    return render_template('bills.html')

@app.route('/bills/explained/')
def bills_explained():

    return render_template('bills_explained.html')

@app.route('/bill/<bill_prefix>-<bill_year>/')
def bill_redirect(bill_prefix, bill_year):
    """
    Redirect to bill's page, based on the bill's code, rather than a backend ID.
    """

    try:
        bill_year = int(bill_year)
        bill_code = bill_prefix + "-" + str(bill_year)
    except:
        abort(error_bad_request)

    api_url = url("bill", str(bill_code))
    r = requests.get(api_url)
    if not r.status_code == 200:
        return(r.text, r.status_code)
    bill = r.json()

    return redirect('/bill/' + str(bill['bill_id']) + "/", 301)

@app.route('/bill/<bill_id>/')
def detail(bill_id=None):
    """
    Display all available detail for the requested bill.
    """

    try:
        bill_id = int(bill_id)
    except:
        abort(error_bad_request)

    logger.debug("detail page called")
    api_url = url("bill", str(bill_id))
    r = requests.get(api_url)
    if not r.status_code == 200:
        return(r.text, r.status_code)
    bill = r.json()

    entries = bill["entries"]

    # separate special entries from the rest of the list
    version_types = ["bill-version", "act"]
    special_types = ["gazette", "whitepaper", "memorandum", "greenpaper", "draft"]

    bill["entries"] = [entry for entry in entries if entry["type"] not in version_types + special_types]

    bill["versions"] = [entry for entry in entries if entry["type"] in version_types]
    special_entries = [entry for entry in entries if entry["type"] in special_types]
    for entry in special_entries:
        bill[entry["type"]] = entry

    return render_template('detail.html', bill=bill)
<|endoftext|>


<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""OdkPrompt."""
import textwrap
from pmix.ppp.config import TEMPLATE_ENV

class OdkPrompt:
    """
    Class to represent a single ODK prompt from an XLSForm.

    This is described in a single row of an XLSForm.
    """

    select_types = (
        'select_one',
        'select_multiple'
    )
    response_types = (
        'integer',
        'decimal',
        'geopoint',
        'barcode',
        'image',
        'text',
        'date',
        'dateTime'
    )
    non_response_types = (
        'note',
    )
    media_fields = ['image', 'media::image', 'audio', 'media::audio',
                    'video', 'media::video']
    language_dependent_fields = \
        ['label', 'hint', 'constraint_message'] + media_fields
    truncatable_fields = ['constraint', 'relevant']

    def __init__(self, row, choices=None):
        """Initialize the XLSForm prompt (a single row of a specific type).

        Row is a dict object. It is guaranteed to have the "simple_type" key
        with a value from the class member variables `select_types`,
        `response_types`, or `non_response_types`.

        :param row: (dict) XLSForm headers as keys, row entries as values.
        :param choices: An Odkchoices object, or None if not a select type.
        """
        self.row = row
        self.choices = choices
        self.odktype = self.row['simple_type']
        self.is_section_header = True if self.row['name'].startswith('sect_') \
            else False

    def __repr__(self):
        """Print representation of instance."""
        return "<OdkPrompt {}>".format(self.row['name'])

    @staticmethod
    def truncate_text(text):
        """Truncate text and add an ellipsis when text is too long.

        :param text: (str) The text.
        :return: (str) Truncated text.
        """
        if len(text) > 100:
            text = text[0:98] + ' …'
        return text

    @staticmethod
    def reformat_double_line_breaks(prompt):
        """Convert labels and hints from strings to lists.

        This conversion process allows for line breaks to be rendered properly
        in html.
        """
        for k, v in prompt.items():
            # if k.startswith('label' or 'hint' or 'constraint_message'):
            if k.startswith('label') or k.startswith('hint') \
                    or k.startswith('constraint_message'):
                if v:
                    prompt[k] = v.split('\n\n')
        return prompt

    @staticmethod
    def reformat_default_lang_vars(row, lang):
        """Reformat default language variables.

        Reformat ::<language_name> style variable names to remove the
        ::<language_name>, leaving just the variable.

        :param row: (str) The dictionary row of the component.
        :param lang: (str) The language.
        :return: (str) Dictionary row of the component, reformatted.
        """
        for field in OdkPrompt.language_dependent_fields:
            if field + '::' + lang in row:
                row[field] = row[field + '::' + lang]
        return row

    # pylint: disable=too-many-branches
    @staticmethod
    def create_additional_media_fields(row, prefix):
        """Create additional media fields."""
        fields_to_add = []
        for key, val in row.items():
            for field in OdkPrompt.media_fields:
                if key.startswith(field) and len(val) > 0:
                    if field not in row:
                        fields_to_add.append(field)
                    if field.startswith(prefix):
                        non_prefixed_mf = field.replace(
                            prefix, '')
                        if non_prefixed_mf not in row:
                            fields_to_add.append(non_prefixed_mf)

        for field in fields_to_add:
            row[field] = ''
        if len(fields_to_add) > 0:
            row['media'] = []
        return row

    @staticmethod
    def set_grouped_media_field(row):
        """Format the text representing any media to be enclosed in brackets.

        :param row: (str) The dictionary row of the component.
        :return: (str) Dictionary row of the component, reformatted.
        """
        for key, val in row.items():
            for field in OdkPrompt.media_fields:
                if val and key.startswith(field) and val not in row['media']:
                    row['media'].append(val)
        return row

    @staticmethod
    def text_relevant():
        # def text_relevant(self, lang):
        # TODO: Create this method.
        """Find the relevant text for this row."""
        pass

    def truncate_fields(self, row):
        """Call truncate_text() method for all truncatable fields in component.

        :param row: (str) The dictionary row of the component.
        :return: (d) Dictionary row of the component, reformatted.
        """
        for field in OdkPrompt.truncatable_fields:
            row[field + '_original'] = row[field]
            row[field] = self.truncate_text(row[field])
        return row

    def format_media_labels(self, input_row):
        """Format media labels."""
        arbitrary_media_prefix = 'media::'
        row = self.create_additional_media_fields(input_row,
                                                  arbitrary_media_prefix)
        for key, val in row.items():
            for field in OdkPrompt.media_fields:
                if key.startswith(field) and len(val) > 0:
                    if val[0] is not '[' and val[-1] is not ']':
                        formatted_media_label = '[' + val + ']'
                    row[field] = formatted_media_label
                    row[key] = formatted_media_label
                    if field.startswith(arbitrary_media_prefix):
                        non_prefixed_mf = field.replace(
                            arbitrary_media_prefix, '')
                        row[non_prefixed_mf] = formatted_media_label
        return row

    def text_field(self, field, lang):
        """Find a row value given a field and language.

        An example of field and language might be "label" and "English".

        :param field: (str) The field from the header row.
        :param lang: (str) The language.
        :return: (str) The value found from this row.
        """
        value = None
        try:
            if lang:
                key = '{}::{}'.format(field, lang)
                value = self.row[key]
            else:
                keys = (k for k in self.row if k.startswith(field))
                first = sorted(keys)[0]
                value = self.row[first]
        except (KeyError, IndexError):
            # KeyError: self.row does not have the key '{}::{}'
            # IndexError: `keys` (filtered by field) is empty list
            pass
        return value

    def to_text_relevant(self, lang):
        """Get the relevant text for this prompt.

        :param lang: (str) The language.
        :return: (str) The text representation of the relevant.
        """
        formatted_relevant = None
        relevant_text = self.text_field('relevant_text', lang)
        if relevant_text:
            formatted_relevant = '[{}]'.format(relevant_text).rjust(50)
        return formatted_relevant

    def to_text_response(self, lang, numbered=False):
        """Get the response field for this prompt.

        This is a text representation of the area of a paper questionnaire
        where the response is recorded.

        :param lang: (str) The language.
        :param numbered: (bool) Should choice options be numbered?
        :return: (str) The text representation of the response entry field.
        """
        text_str = None
        if self.odktype == 'select_multiple':
            choices = self.choices.labels(lang=lang)
            if numbered:
                choices = ['{}. {}'.format(i+1, c) for i, c in
                           enumerate(choices)]
            text_str = '\n'.join(('_ {}'.format(i) for i in choices))
        elif self.odktype == 'select_one':
            choices = self.choices.labels(lang=lang)
            if numbered:
                choices = ['{}. {}'.format(i+1, c) for i, c in
                           enumerate(choices)]
            text_str = '\n'.join(('* {}'.format(i) for i in choices))
        elif self.odktype in OdkPrompt.response_types:
            text_str = '_'*30 + '({})'.format(self.odktype)

        # try:
        #     question_type = self.odktype in Odkprompt.response_types or \
        #                     self.odktype in Odkprompt.select_types
        #     if text_str and self.row['read_only'] and question_type:
        #         # TODO fix read_only lookup
        #         text_str = '\n'.join(('[Read only]', text_str))
        # except KeyError:  # Unable to find 'read_only'
        #     pass

        if text_str:
            text_str = textwrap.indent(text_str, '  ')

        return text_str

    def to_html_input_field(self, lang):
        """Get the response field for this prompt.

        This is a representation of the area of a paper questionnaire where
        the response is recorded.

        :param lang: (str) The language.
        :return: (str or dict) The representation of the entry field.
        """
        field = None
        if self.odktype in ['select_multiple', 'select_one']:
            field = self.choices.name_labels(lang=lang)
        elif self.odktype in OdkPrompt.response_types:
            field = '_' * 30 + '({})'.format(self.odktype)
        return field

    def to_text(self, lang):
        """Get the text representation of the full prompt.

        :param lang: (str) The language.
        :return: (str) The text from all parts of the prompt.
        """
        # Note: May not need 'relevant_text'.
        # relevant_text = self.text_field('relevant_text', lang)
        label = self.text_field('label', lang)
        hint = self.text_field('hint', lang)
        # Need done: Audio, Image, Video, Relevant
        fields = (
            self.to_text_relevant(lang),
            label,
            hint,
            self.to_text_response(lang)
        )
        text = filter(None, fields)
        result = '\n\n'.join(text)
        return result

    def to_dict(self, lang, **kwargs):
        """Get the text representation of the full prompt.

        :param lang: (str) The language.
        :return: (dict) The text from all parts of the prompt.
        """
        # TODO: Refactor so that the dict row is only looped through once
        # to make all of the changes below.
        prompt = self.format_media_labels(self.row)
        prompt = self.set_grouped_media_field(prompt)
        prompt = self.reformat_default_lang_vars(prompt, lang)
        prompt = self.truncate_fields(prompt)
        prompt = self.reformat_double_line_breaks(prompt)
        prompt['input_field'] = self.to_html_input_field(lang)
        if self.is_section_header:
            prompt['is_section_header'] = True
        if 'bottom_border' in kwargs:
            prompt['bottom_border'] = True
        return prompt

    def to_html(self, lang, highlighting, **kwargs):
        """Convert to html."""
        # pylint: disable=no-member
        return TEMPLATE_ENV.get_template('content/content-tr-base.html')\
            .render(question=self.to_dict(lang=lang, **kwargs),
                    highlighting=highlighting)
<commit_msg>Refactored docstring for OdkPrompt.to_html_input_field().<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""OdkPrompt."""
import textwrap
from pmix.ppp.config import TEMPLATE_ENV

class OdkPrompt:
    """
    Class to represent a single ODK prompt from an XLSForm.

    This is described in a single row of an XLSForm.
    """

    select_types = (
        'select_one',
        'select_multiple'
    )
    response_types = (
        'integer',
        'decimal',
        'geopoint',
        'barcode',
        'image',
        'text',
        'date',
        'dateTime'
    )
    non_response_types = (
        'note',
    )
    media_fields = ['image', 'media::image', 'audio', 'media::audio',
                    'video', 'media::video']
    language_dependent_fields = \
        ['label', 'hint', 'constraint_message'] + media_fields
    truncatable_fields = ['constraint', 'relevant']

    def __init__(self, row, choices=None):
        """Initialize the XLSForm prompt (a single row of a specific type).

        Row is a dict object. It is guaranteed to have the "simple_type" key
        with a value from the class member variables `select_types`,
        `response_types`, or `non_response_types`.

        :param row: (dict) XLSForm headers as keys, row entries as values.
        :param choices: An Odkchoices object, or None if not a select type.
        """
        self.row = row
        self.choices = choices
        self.odktype = self.row['simple_type']
        self.is_section_header = True if self.row['name'].startswith('sect_') \
            else False

    def __repr__(self):
        """Print representation of instance."""
        return "<OdkPrompt {}>".format(self.row['name'])

    @staticmethod
    def truncate_text(text):
        """Truncate text and add an ellipsis when text is too long.

        :param text: (str) The text.
        :return: (str) Truncated text.
        """
        if len(text) > 100:
            text = text[0:98] + ' …'
        return text

    @staticmethod
    def reformat_double_line_breaks(prompt):
        """Convert labels and hints from strings to lists.

        This conversion process allows for line breaks to be rendered properly
        in html.
        """
        for k, v in prompt.items():
            # if k.startswith('label' or 'hint' or 'constraint_message'):
            if k.startswith('label') or k.startswith('hint') \
                    or k.startswith('constraint_message'):
                if v:
                    prompt[k] = v.split('\n\n')
        return prompt

    @staticmethod
    def reformat_default_lang_vars(row, lang):
        """Reformat default language variables.

        Reformat ::<language_name> style variable names to remove the
        ::<language_name>, leaving just the variable.

        :param row: (str) The dictionary row of the component.
        :param lang: (str) The language.
        :return: (str) Dictionary row of the component, reformatted.
        """
        for field in OdkPrompt.language_dependent_fields:
            if field + '::' + lang in row:
                row[field] = row[field + '::' + lang]
        return row

    # pylint: disable=too-many-branches
    @staticmethod
    def create_additional_media_fields(row, prefix):
        """Create additional media fields."""
        fields_to_add = []
        for key, val in row.items():
            for field in OdkPrompt.media_fields:
                if key.startswith(field) and len(val) > 0:
                    if field not in row:
                        fields_to_add.append(field)
                    if field.startswith(prefix):
                        non_prefixed_mf = field.replace(
                            prefix, '')
                        if non_prefixed_mf not in row:
                            fields_to_add.append(non_prefixed_mf)

        for field in fields_to_add:
            row[field] = ''
        if len(fields_to_add) > 0:
            row['media'] = []
        return row

    @staticmethod
    def set_grouped_media_field(row):
        """Format the text representing any media to be enclosed in brackets.

        :param row: (str) The dictionary row of the component.
        :return: (str) Dictionary row of the component, reformatted.
        """
        for key, val in row.items():
            for field in OdkPrompt.media_fields:
                if val and key.startswith(field) and val not in row['media']:
                    row['media'].append(val)
        return row

    @staticmethod
    def text_relevant():
        # def text_relevant(self, lang):
        # TODO: Create this method.
        """Find the relevant text for this row."""
        pass

    def truncate_fields(self, row):
        """Call truncate_text() method for all truncatable fields in component.

        :param row: (str) The dictionary row of the component.
        :return: (d) Dictionary row of the component, reformatted.
        """
        for field in OdkPrompt.truncatable_fields:
            row[field + '_original'] = row[field]
            row[field] = self.truncate_text(row[field])
        return row

    def format_media_labels(self, input_row):
        """Format media labels."""
        arbitrary_media_prefix = 'media::'
        row = self.create_additional_media_fields(input_row,
                                                  arbitrary_media_prefix)
        for key, val in row.items():
            for field in OdkPrompt.media_fields:
                if key.startswith(field) and len(val) > 0:
                    if val[0] is not '[' and val[-1] is not ']':
                        formatted_media_label = '[' + val + ']'
                    row[field] = formatted_media_label
                    row[key] = formatted_media_label
                    if field.startswith(arbitrary_media_prefix):
                        non_prefixed_mf = field.replace(
                            arbitrary_media_prefix, '')
                        row[non_prefixed_mf] = formatted_media_label
        return row

    def text_field(self, field, lang):
        """Find a row value given a field and language.

        An example of field and language might be "label" and "English".

        :param field: (str) The field from the header row.
        :param lang: (str) The language.
        :return: (str) The value found from this row.
        """
        value = None
        try:
            if lang:
                key = '{}::{}'.format(field, lang)
                value = self.row[key]
            else:
                keys = (k for k in self.row if k.startswith(field))
                first = sorted(keys)[0]
                value = self.row[first]
        except (KeyError, IndexError):
            # KeyError: self.row does not have the key '{}::{}'
            # IndexError: `keys` (filtered by field) is empty list
            pass
        return value

    def to_text_relevant(self, lang):
        """Get the relevant text for this prompt.

        :param lang: (str) The language.
        :return: (str) The text representation of the relevant.
        """
        formatted_relevant = None
        relevant_text = self.text_field('relevant_text', lang)
        if relevant_text:
            formatted_relevant = '[{}]'.format(relevant_text).rjust(50)
        return formatted_relevant

    def to_text_response(self, lang, numbered=False):
        """Get the response field for this prompt.

        This is a text representation of the area of a paper questionnaire
        where the response is recorded.

        :param lang: (str) The language.
        :param numbered: (bool) Should choice options be numbered?
        :return: (str) The text representation of the response entry field.
        """
        text_str = None
        if self.odktype == 'select_multiple':
            choices = self.choices.labels(lang=lang)
            if numbered:
                choices = ['{}. {}'.format(i+1, c) for i, c in
                           enumerate(choices)]
            text_str = '\n'.join(('_ {}'.format(i) for i in choices))
        elif self.odktype == 'select_one':
            choices = self.choices.labels(lang=lang)
            if numbered:
                choices = ['{}. {}'.format(i+1, c) for i, c in
                           enumerate(choices)]
            text_str = '\n'.join(('* {}'.format(i) for i in choices))
        elif self.odktype in OdkPrompt.response_types:
            text_str = '_'*30 + '({})'.format(self.odktype)

        # try:
        #     question_type = self.odktype in Odkprompt.response_types or \
        #                     self.odktype in Odkprompt.select_types
        #     if text_str and self.row['read_only'] and question_type:
        #         # TODO fix read_only lookup
        #         text_str = '\n'.join(('[Read only]', text_str))
        # except KeyError:  # Unable to find 'read_only'
        #     pass

        if text_str:
            text_str = textwrap.indent(text_str, '  ')

        return text_str

    def to_html_input_field(self, lang):
        """Get the response field for this prompt.

        This is a representation of the area of a paper questionnaire where
        the response is recorded.

        Args:
            lang (str): The language.

        Returns:
            (str or dict): The representation of the entry field.
        """
        field = None
        if self.odktype in ['select_multiple', 'select_one']:
            field = self.choices.name_labels(lang=lang)
        elif self.odktype in OdkPrompt.response_types:
            field = '_' * 30 + '({})'.format(self.odktype)
        return field

    def to_text(self, lang):
        """Get the text representation of the full prompt.

        :param lang: (str) The language.
        :return: (str) The text from all parts of the prompt.
        """
        # Note: May not need 'relevant_text'.
        # relevant_text = self.text_field('relevant_text', lang)
        label = self.text_field('label', lang)
        hint = self.text_field('hint', lang)
        # Need done: Audio, Image, Video, Relevant
        fields = (
            self.to_text_relevant(lang),
            label,
            hint,
            self.to_text_response(lang)
        )
        text = filter(None, fields)
        result = '\n\n'.join(text)
        return result

    def to_dict(self, lang, **kwargs):
        """Get the text representation of the full prompt.

        :param lang: (str) The language.
        :return: (dict) The text from all parts of the prompt.
        """
        # TODO: Refactor so that the dict row is only looped through once
        # to make all of the changes below.
        prompt = self.format_media_labels(self.row)
        prompt = self.set_grouped_media_field(prompt)
        prompt = self.reformat_default_lang_vars(prompt, lang)
        prompt = self.truncate_fields(prompt)
        prompt = self.reformat_double_line_breaks(prompt)
        prompt['input_field'] = self.to_html_input_field(lang)
        if self.is_section_header:
            prompt['is_section_header'] = True
        if 'bottom_border' in kwargs:
            prompt['bottom_border'] = True
        return prompt

    def to_html(self, lang, highlighting, **kwargs):
        """Convert to html."""
        # pylint: disable=no-member
        return TEMPLATE_ENV.get_template('content/content-tr-base.html')\
            .render(question=self.to_dict(lang=lang, **kwargs),
                    highlighting=highlighting)
<|endoftext|>


<commit_before>__version__ = '1.0.1'
<commit_msg>The next version will be 1.0.2.<commit_after>__version__ = '1.0.2dev'
<|endoftext|>