<commit_before>#####################################################################################
#
#  Copyright (C) Tavendo GmbH
#
#  Unless a separate license agreement exists between you and Tavendo GmbH (e.g. you
#  have purchased a commercial license), the license terms below apply.
#
#  Should you enter into a separate license agreement after having received a copy of
#  this software, then the terms of such license agreement replace the terms below at
#  the time at which such license agreement becomes effective.
#
#  In case a separate license agreement ends, and such agreement ends without being
#  replaced by another separate license agreement, the license terms below apply
#  from the time at which said agreement ends.
#
#  LICENSE TERMS
#
#  This program is free software: you can redistribute it and/or modify it under the
#  terms of the GNU Affero General Public License, version 3, as published by the
#  Free Software Foundation. This program is distributed in the hope that it will be
#  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
#  See the GNU Affero General Public License Version 3 for more details.
#
#  You should have received a copy of the GNU Affero General Public license along
#  with this program. If not, see <http://www.gnu.org/licenses/agpl-3.0.en.html>.
#
#####################################################################################

import sys
import socket
import platform

from twisted.internet import fdesc, tcp, ssl
from twisted.python.runtime import platformType

# Flag indiciating support for creating shared sockets with in-kernel
# load-balancing (!). Note that while FreeBSD had SO_REUSEPORT for ages,
# it does NOT (currently) implement load-balancing. Linux >= 3.9 and
# DragonFly BSD does.
_HAS_SHARED_LOADBALANCED_SOCKET = False

if sys.platform.startswith('linux'):
    try:
        # get Linux kernel version, like: (3, 19)
        _LINUX_KERNEL_VERSION = tuple(platform.uname()[2].split('.')[:2])

        # SO_REUSEPORT only supported for Linux kernels >= 3.9
        if _LINUX_KERNEL_VERSION[0] >= 3 and _LINUX_KERNEL_VERSION[1] >= 9:
            _HAS_SHARED_LOADBALANCED_SOCKET = True

            # monkey patch missing constant if needed
            if not hasattr(socket, 'SO_REUSEPORT'):
                socket.SO_REUSEPORT = 15
    except:
        pass

elif sys.platform == 'win32':
    # http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t/14388707#14388707
    _HAS_SHARED_LOADBALANCED_SOCKET = True

# FIXME: DragonFly BSD claims support: http://lists.dragonflybsd.org/pipermail/commits/2013-May/130083.html

__all__ = ('create_stream_socket', 'SharedPort', 'SharedTLSPort')

def create_stream_socket(addressFamily, shared=False):
    """
    Create a new socket for use with Twisted's IReactor.adoptStreamPort.

    :param addressFamily: The socket address family.
    :type addressFamily: One of socket.AF_INET, socket.AF_INET6, socket.AF_UNIX
    :param shared: If `True`, request to create a shared, load-balanced socket.
                   When this feature is not available, throw an exception.
    :type shared: bool
    :returns obj -- A socket.
    """
    s = socket.socket(addressFamily, socket.SOCK_STREAM)
    s.setblocking(0)
    fdesc._setCloseOnExec(s.fileno())

    if platformType == "posix" and sys.platform != "cygwin":
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    if shared:
        if addressFamily not in [socket.AF_INET, socket.AF_INET6]:
            raise Exception("shared sockets are only supported for IPv4 and IPv6")

        if _HAS_SHARED_LOADBALANCED_SOCKET:
            if sys.platform.startswith('linux'):
                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
            elif sys.platform == 'win32':
                # http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t/14388707#14388707
                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                # s.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
            else:
                raise Exception("logic error")
        else:
            raise Exception("shared sockets unsupported on this system")

    return s

class SharedPort(tcp.Port):
    """
    A custom TCP port which allows to set socket options for sharing TCP ports between multiple processes.
    """

    def __init__(self, port, factory, backlog=50, interface='', reactor=None, shared=False):
        if shared and not _HAS_SHARED_LOADBALANCED_SOCKET:
            raise Exception("shared sockets unsupported on this system")
        else:
            self._shared = shared

        tcp.Port.__init__(self, port, factory, backlog, interface, reactor)

    def createInternetSocket(self):
        s = tcp.Port.createInternetSocket(self)
        if self._shared:
            if _HAS_SHARED_LOADBALANCED_SOCKET:
                if sys.platform.startswith('linux'):
                    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
                elif sys.platform == 'win32':
                    # http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t/14388707#14388707
                    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                    # s.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
                else:
                    raise Exception("logic error")
            else:
                raise Exception("shared sockets unsupported on this system")
        return s

class SharedTLSPort(SharedPort, ssl.Port):
    """
    A custom TLS port which allows to set socket options for sharing (the underlying) TCP ports between multiple processes.
    """

    def __init__(self, port, factory, ctxFactory, backlog=50, interface='', reactor=None, shared=False):
        if shared and not _HAS_SHARED_LOADBALANCED_SOCKET:
            raise Exception("shared sockets unsupported on this system")
        else:
            self._shared = shared

        ssl.Port.__init__(self, port, factory, ctxFactory, backlog, interface, reactor)
<commit_msg>fix shared TCP sockets support detection<commit_after>#####################################################################################
#
#  Copyright (C) Tavendo GmbH
#
#  Unless a separate license agreement exists between you and Tavendo GmbH (e.g. you
#  have purchased a commercial license), the license terms below apply.
#
#  Should you enter into a separate license agreement after having received a copy of
#  this software, then the terms of such license agreement replace the terms below at
#  the time at which such license agreement becomes effective.
#
#  In case a separate license agreement ends, and such agreement ends without being
#  replaced by another separate license agreement, the license terms below apply
#  from the time at which said agreement ends.
#
#  LICENSE TERMS
#
#  This program is free software: you can redistribute it and/or modify it under the
#  terms of the GNU Affero General Public License, version 3, as published by the
#  Free Software Foundation. This program is distributed in the hope that it will be
#  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
#  See the GNU Affero General Public License Version 3 for more details.
#
#  You should have received a copy of the GNU Affero General Public license along
#  with this program. If not, see <http://www.gnu.org/licenses/agpl-3.0.en.html>.
#
#####################################################################################

import sys
import socket
import platform

from twisted.internet import fdesc, tcp, ssl
from twisted.python.runtime import platformType

# Flag indiciating support for creating shared sockets with in-kernel
# load-balancing (!). Note that while FreeBSD had SO_REUSEPORT for ages,
# it does NOT (currently) implement load-balancing. Linux >= 3.9 and
# DragonFly BSD does.
_HAS_SHARED_LOADBALANCED_SOCKET = False

if sys.platform.startswith('linux'):
    try:
        # get Linux kernel version, like: (3, 19)
        _LINUX_KERNEL_VERSION = [int(x) for x in tuple(platform.uname()[2].split('.')[:2])]

        # SO_REUSEPORT only supported for Linux kernels >= 3.9
        if _LINUX_KERNEL_VERSION[0] >= 3 and _LINUX_KERNEL_VERSION[1] >= 9:
            _HAS_SHARED_LOADBALANCED_SOCKET = True

            # monkey patch missing constant if needed
            if not hasattr(socket, 'SO_REUSEPORT'):
                socket.SO_REUSEPORT = 15
    except:
        pass

elif sys.platform == 'win32':
    # http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t/14388707#14388707
    _HAS_SHARED_LOADBALANCED_SOCKET = True

# FIXME: DragonFly BSD claims support: http://lists.dragonflybsd.org/pipermail/commits/2013-May/130083.html

__all__ = ('create_stream_socket', 'SharedPort', 'SharedTLSPort')

def create_stream_socket(addressFamily, shared=False):
    """
    Create a new socket for use with Twisted's IReactor.adoptStreamPort.

    :param addressFamily: The socket address family.
    :type addressFamily: One of socket.AF_INET, socket.AF_INET6, socket.AF_UNIX
    :param shared: If `True`, request to create a shared, load-balanced socket.
                   When this feature is not available, throw an exception.
    :type shared: bool
    :returns obj -- A socket.
    """
    s = socket.socket(addressFamily, socket.SOCK_STREAM)
    s.setblocking(0)
    fdesc._setCloseOnExec(s.fileno())

    if platformType == "posix" and sys.platform != "cygwin":
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    if shared:
        if addressFamily not in [socket.AF_INET, socket.AF_INET6]:
            raise Exception("shared sockets are only supported for IPv4 and IPv6")

        if _HAS_SHARED_LOADBALANCED_SOCKET:
            if sys.platform.startswith('linux'):
                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
            elif sys.platform == 'win32':
                # http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t/14388707#14388707
                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                # s.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
            else:
                raise Exception("logic error")
        else:
            raise Exception("shared sockets unsupported on this system")

    return s

class SharedPort(tcp.Port):
    """
    A custom TCP port which allows to set socket options for sharing TCP ports between multiple processes.
    """

    def __init__(self, port, factory, backlog=50, interface='', reactor=None, shared=False):
        if shared and not _HAS_SHARED_LOADBALANCED_SOCKET:
            raise Exception("shared sockets unsupported on this system")
        else:
            self._shared = shared

        tcp.Port.__init__(self, port, factory, backlog, interface, reactor)

    def createInternetSocket(self):
        s = tcp.Port.createInternetSocket(self)
        if self._shared:
            if _HAS_SHARED_LOADBALANCED_SOCKET:
                if sys.platform.startswith('linux'):
                    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
                elif sys.platform == 'win32':
                    # http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t/14388707#14388707
                    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                    # s.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
                else:
                    raise Exception("logic error")
            else:
                raise Exception("shared sockets unsupported on this system")
        return s

class SharedTLSPort(SharedPort, ssl.Port):
    """
    A custom TLS port which allows to set socket options for sharing (the underlying) TCP ports between multiple processes.
    """

    def __init__(self, port, factory, ctxFactory, backlog=50, interface='', reactor=None, shared=False):
        if shared and not _HAS_SHARED_LOADBALANCED_SOCKET:
            raise Exception("shared sockets unsupported on this system")
        else:
            self._shared = shared

        ssl.Port.__init__(self, port, factory, ctxFactory, backlog, interface, reactor)
<|endoftext|>


<commit_before>import binascii

import six
from boto import kms
from boto.exception import BotoServerError
from boto.kms.exceptions import InvalidCiphertextException
from cfn_sphere.exceptions import CfnSphereBotoError, InvalidEncryptedValueException
from cfn_sphere.util import with_boto_retry
if six.PY3:
    from base64 import decodebytes as base64decode
else:
    from base64 import decodestring as base64decode

class KMS(object):
    def __init__(self, region="eu-west-1"):
        self.conn = kms.connect_to_region(region)

    @with_boto_retry()
    def decrypt(self, encrypted_value):
        try:
            response = self.conn.decrypt(base64decode(encrypted_value.encode("utf-8")))
        except TypeError as e:
            raise InvalidEncryptedValueException("Could not decode encrypted value: {0}".format(e), e)
        except binascii.Error as e:
            raise InvalidEncryptedValueException("Could not decode encrypted value: {0}".format(e), e)
        except InvalidCiphertextException as e:
            raise InvalidEncryptedValueException("Could not decrypted value: {0}".format(e), e)
        except BotoServerError as e:
            raise CfnSphereBotoError(e)

        return response['Plaintext']
<commit_msg>fix encoding<commit_after>import binascii

from boto import kms
from boto.exception import BotoServerError
from boto.kms.exceptions import InvalidCiphertextException
from cfn_sphere.exceptions import CfnSphereBotoError, InvalidEncryptedValueException
from cfn_sphere.util import with_boto_retry
import base64

class KMS(object):
    def __init__(self, region="eu-west-1"):
        self.conn = kms.connect_to_region(region)

    @with_boto_retry()
    def decrypt(self, encrypted_value):
        try:
            response = self.conn.decrypt(base64.b64decode(encrypted_value.encode()))
        except TypeError as e:
            raise InvalidEncryptedValueException("Could not decode encrypted value: {0}".format(e), e)
        except binascii.Error as e:
            raise InvalidEncryptedValueException("Could not decode encrypted value: {0}".format(e))
        except InvalidCiphertextException as e:
            raise InvalidEncryptedValueException("Could not decrypted value: {0}".format(e))
        except BotoServerError as e:
            raise CfnSphereBotoError(e)

        return response['Plaintext']
<|endoftext|>


<commit_before># -*- encoding: utf-8 -*-
##############################################################################
#
#    Copyright (C) 2014 Compassion CH (http://www.compassion.ch)
#    Releasing children from poverty in Jesus' name
#    @author: Cyril Sester, Emanuel Cino
#
#    The licence is in the file __openerp__.py
#
##############################################################################

import time

from openerp.osv import orm, fields
from openerp.tools.translate import _
from openerp.tools import mod10r
from openerp import netsvc

class contract_group(orm.Model):
    """ Add BVR on groups and add BVR ref and analytics_id
    in invoices """
    _inherit = 'recurring.contract.group'

    def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        res = []
        for gr in self.browse(cr, uid, ids, context):
            name = ''
            if gr.payment_term_id:
                name = gr.payment_term_id.name
            if gr.bvr_reference:
                name += ' ' + gr.bvr_reference
            if name == '':
                name = gr.partner_id.name + ' ' + str(gr.id)
            res.append((gr.id, name))
        return res

    def _get_op_payment_term(self, cr, uid, context=None):
        """ Get Permanent Order Payment Term, to set it by default. """
        payment_term_id = self.pool.get('account.payment.term').search(
            cr, uid, [('name', '=', 'Permanent Order')],
            context={'lang': 'en_US'})
        return payment_term_id[0]

    _columns = {
        'bvr_reference': fields.char(size=32, string=_('BVR Ref'),
                                     track_visibility="onchange"),
        'payment_term_id': fields.many2one(
            'account.payment.term', _('Payment Term'),
            domain=['|', '|', '|', ('name', 'ilike', 'BVR'),
                    ('name', 'ilike', 'LSV'),
                    ('name', 'ilike', 'Postfinance'),
                    ('name', 'ilike', 'Permanent')],
            track_visibility="onchange"),
    }

    _defaults = {
        'payment_term_id': _get_op_payment_term,
        # Clean invoices when group is modified
        'change_method': 'clean_invoices',
    }

    def on_change_partner_id(self, cr, uid, ids, partner_id, context=None):
        res = dict()
        if not partner_id:
            return {'value': {'bvr_reference': ''}}
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id,
                                                      context=context)
        if partner.ref:
            computed_ref = self.compute_partner_bvr_ref(cr, uid, ids,
                                                        partner, context)
            if computed_ref:
                res['value'] = {'bvr_reference': computed_ref}
            else:
                res['warning'] = {'title': _('Warning'),
                                  'message': _('The reference of the partner '
                                               'has not been set, or is in '
                                               'wrong format. Please make sure'
                                               ' to enter a valid BVR '
                                               'reference for the contract.')}
        return res

    def on_change_payment_term(self, cr, uid, ids, payment_term_id, bvr_ref,
                               partner_id, context=None):
        """ Generate new bvr_reference if payment term is Permanent Order
        or BVR """
        if not context:
            ctx = dict()
        else:
            ctx = context.copy()
            ctx['lang'] = 'en_US'
        res = {'value': dict()}
        payment_term_obj = self.pool.get('account.payment.term')
        need_bvr_ref_term_ids = payment_term_obj.search(cr, uid, [
            '|', ('name', 'in', ('Permanent Order', 'BVR')),
            ('name', 'like', 'multi-months')], context=ctx)
        lsv_term_ids = payment_term_obj.search(
            cr, uid, [('name', 'like', 'LSV')], context=ctx)
        if payment_term_id in need_bvr_ref_term_ids:
            is_lsv = payment_term_id in lsv_term_ids
            partner = self.pool.get('res.partner').browse(cr, uid, partner_id,
                                                          context=context)
            if partner.ref and (not bvr_ref or is_lsv):
                res['value'].update({
                    'bvr_reference': self.compute_partner_bvr_ref(
                        cr, uid, ids, partner, is_lsv, context)})

        return res

    def on_change_bvr_ref(self, cr, uid, ids, bvr_reference,
                          context=None):
        """ Test the validity of a reference number. """
        is_valid = bvr_reference and bvr_reference.isdigit()
        if is_valid and len(bvr_reference) == 26:
            bvr_reference = mod10r(bvr_reference)
        elif is_valid and len(bvr_reference) == 27:
            valid_ref = mod10r(bvr_reference[:-1])
            is_valid = (valid_ref == bvr_reference)
        else:
            is_valid = False

        res = dict()
        if is_valid:
            res['value'] = {'bvr_reference': bvr_reference}
        elif bvr_reference:
            res['warning'] = {'title': _('Warning'),
                              'message': _('The reference of the partner '
                                           'has not been set, or is in '
                                           'wrong format. Please make sure'
                                           ' to enter a valid BVR '
                                           'reference for the contract.')}
        return res

    def compute_partner_bvr_ref(self, cr, uid, ids, partner=None,
                                is_lsv=False, context=None):
        """ Generates a new BVR Reference.
        See file \\nas\it\devel\Code_ref_BVR.xls for more information."""
        partner = partner or self.browse(cr, uid, ids[0], context).partner_id
        result = '0' * (9 + (7 - len(partner.ref))) + partner.ref
        count_groups = str(self.search(
            cr, uid, [('partner_id', '=', partner.id)], context=context,
            count=True))
        result += '0' * (5 - len(count_groups)) + count_groups
        # Type '0' = Sponsorship
        result += '0'
        result += '0' * 4

        if is_lsv:
            result = '004874969' + result[9:]
        if len(result) == 26:
            return mod10r(result)

    def _setup_inv_data(self, cr, uid, con_gr, journal_ids, invoicer_id,
                        context=None):
        """ Inherit to add BVR ref """
        inv_data = super(contract_group, self)._setup_inv_data(
            cr, uid, con_gr, journal_ids, invoicer_id, context)

        ref = ''
        if con_gr.bvr_reference:
            ref = con_gr.bvr_reference
        elif (con_gr.payment_term_id and
              (_('LSV') in con_gr.payment_term_id.name or
               _('Direct Debit') in con_gr.payment_term_id.name)):
            seq = self.pool['ir.sequence']
            ref = mod10r(seq.next_by_code(cr, uid, 'contract.bvr.ref'))
        inv_data.update({
            'bvr_reference': ref})

        return inv_data

    def _setup_inv_line_data(self, cr, uid, contract_line, invoice_id,
                             context=None):
        """ Inherit to add analytic distribution """
        inv_line_data = super(contract_group, self)._setup_inv_line_data(
            cr, uid, contract_line, invoice_id, context)

        product_id = contract_line.product_id.id
        partner_id = contract_line.contract_id.partner_id.id
        analytic = self.pool.get('account.analytic.default').account_get(
            cr, uid, product_id, partner_id, uid, time.strftime('%Y-%m-%d'),
            context=context)
        if analytic and analytic.analytics_id:
            inv_line_data.update({'analytics_id': analytic.analytics_id.id})

        return inv_line_data

    def _get_gen_states(self):
        return ['waiting', 'active']

    def write(self, cr, uid, ids, vals, context=None):
        """ If sponsor changes his payment term to LSV or DD,
        change the state of related contracts so that we wait
        for a valid mandate before generating new invoices.
        """
        contract_ids = list()
        inv_vals = dict()
        if 'payment_term_id' in vals:
            inv_vals['payment_term'] = vals['payment_term_id']
            payment_term = self.pool.get('account.payment.term').browse(
                cr, uid, vals['payment_term_id'], {'lang': 'en_US'})
            payment_name = payment_term.name
            wf_service = netsvc.LocalService('workflow')
            for group in self.browse(cr, uid, ids, context):
                old_term = group.payment_term_id.name
                for contract in group.contract_ids:
                    contract_ids.append(contract.id)
                    if 'LSV' in payment_name or 'Postfinance' in payment_name:
                        wf_service.trg_validate(
                            uid, 'recurring.contract', contract.id,
                            'will_pay_by_lsv_dd', cr)
                        # LSV/DD Contracts need no reference
                        if group.bvr_reference and \
                                'multi-months' not in payment_name:
                            vals['bvr_reference'] = False
                    elif 'LSV' in old_term or 'Postfinance' in old_term:
                        wf_service.trg_validate(
                            uid, 'recurring.contract', contract.id,
                            'mandate_validated', cr)
        if 'bvr_reference' in vals:
            inv_vals['bvr_reference'] = vals['bvr_reference']
            for group in self.browse(cr, uid, ids, context):
                contract_ids.extend([c.id for c in group.contract_ids])

        res = super(contract_group, self).write(cr, uid, ids, vals, context)

        if contract_ids:
            # Update related open invoices to reflect the changes
            inv_line_obj = self.pool.get('account.invoice.line')
            inv_obj = self.pool.get('account.invoice')
            inv_line_ids = inv_line_obj.search(cr, uid, [
                ('contract_id', 'in', contract_ids),
                ('state', 'not in', ('paid', 'cancel'))], context=context)
            invoice_ids = list(set([inv_l.invoice_id.id for inv_l in
                                    inv_line_obj.browse(cr, uid, inv_line_ids,
                                                        context)]))
            inv_obj.action_cancel(cr, uid, invoice_ids, context)
            inv_obj.action_cancel_draft(cr, uid, invoice_ids)
            inv_obj.write(cr, uid, invoice_ids, inv_vals, context)
            wf_service = netsvc.LocalService('workflow')
            for invoice_id in invoice_ids:
                wf_service.trg_validate(uid, 'account.invoice', invoice_id,
                                        'invoice_open', cr)
        return res

    def clean_invoices(self, cr, uid, group, context=None):
        """ Override clean_invoices to delete cancelled invoices """
        inv_ids = super(contract_group, self).clean_invoices(
            cr, uid, group, context=None)
        if inv_ids:
            inv_ids = list(inv_ids)
            cr.execute(
                "DELETE FROM account_invoice "
                "WHERE id IN ({0})".format(
                    ','.join([str(id) for id in inv_ids])))
        return inv_ids
<commit_msg>Correct on_change_payment_term_id<commit_after># -*- encoding: utf-8 -*-
##############################################################################
#
#    Copyright (C) 2014 Compassion CH (http://www.compassion.ch)
#    Releasing children from poverty in Jesus' name
#    @author: Cyril Sester, Emanuel Cino
#
#    The licence is in the file __openerp__.py
#
##############################################################################

import time

from openerp.osv import orm, fields
from openerp.tools.translate import _
from openerp.tools import mod10r
from openerp import netsvc

class contract_group(orm.Model):
    """ Add BVR on groups and add BVR ref and analytics_id
    in invoices """
    _inherit = 'recurring.contract.group'

    def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        res = []
        for gr in self.browse(cr, uid, ids, context):
            name = ''
            if gr.payment_term_id:
                name = gr.payment_term_id.name
            if gr.bvr_reference:
                name += ' ' + gr.bvr_reference
            if name == '':
                name = gr.partner_id.name + ' ' + str(gr.id)
            res.append((gr.id, name))
        return res

    def _get_op_payment_term(self, cr, uid, context=None):
        """ Get Permanent Order Payment Term, to set it by default. """
        payment_term_id = self.pool.get('account.payment.term').search(
            cr, uid, [('name', '=', 'Permanent Order')],
            context={'lang': 'en_US'})
        return payment_term_id[0]

    _columns = {
        'bvr_reference': fields.char(size=32, string=_('BVR Ref'),
                                     track_visibility="onchange"),
        'payment_term_id': fields.many2one(
            'account.payment.term', _('Payment Term'),
            domain=['|', '|', '|', ('name', 'ilike', 'BVR'),
                    ('name', 'ilike', 'LSV'),
                    ('name', 'ilike', 'Postfinance'),
                    ('name', 'ilike', 'Permanent')],
            track_visibility="onchange"),
    }

    _defaults = {
        'payment_term_id': _get_op_payment_term,
        # Clean invoices when group is modified
        'change_method': 'clean_invoices',
    }

    def on_change_partner_id(self, cr, uid, ids, partner_id, context=None):
        res = dict()
        if not partner_id:
            return {'value': {'bvr_reference': ''}}
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id,
                                                      context=context)
        if partner.ref:
            computed_ref = self.compute_partner_bvr_ref(cr, uid, ids,
                                                        partner, context)
            if computed_ref:
                res['value'] = {'bvr_reference': computed_ref}
            else:
                res['warning'] = {'title': _('Warning'),
                                  'message': _('The reference of the partner '
                                               'has not been set, or is in '
                                               'wrong format. Please make sure'
                                               ' to enter a valid BVR '
                                               'reference for the contract.')}
        return res

    def on_change_payment_term(self, cr, uid, ids, payment_term_id, bvr_ref,
                               partner_id, context=None):
        """ Generate new bvr_reference if payment term is Permanent Order
        or BVR """
        if not context:
            ctx = dict()
        else:
            ctx = context.copy()
            ctx['lang'] = 'en_US'
        res = {'value': dict()}
        payment_term_obj = self.pool.get('account.payment.term')
        need_bvr_ref_term_ids = payment_term_obj.search(cr, uid, [
            '|', ('name', 'in', ('Permanent Order', 'BVR')),
            ('name', 'like', 'multi-months')], context=ctx)
        lsv_term_ids = payment_term_obj.search(
            cr, uid, [('name', 'like', 'LSV')], context=ctx)
        if payment_term_id in need_bvr_ref_term_ids:
            is_lsv = payment_term_id in lsv_term_ids
            partner = self.pool.get('res.partner').browse(cr, uid, partner_id,
                                                          context=context)
            if partner.ref and (not bvr_ref or is_lsv):
                res['value'].update({
                    'bvr_reference': self.compute_partner_bvr_ref(
                        cr, uid, ids, partner, is_lsv, context)})
        else:
            res['value'].update({'bvr_reference': False})

        return res

    def on_change_bvr_ref(self, cr, uid, ids, bvr_reference,
                          context=None):
        """ Test the validity of a reference number. """
        is_valid = bvr_reference and bvr_reference.isdigit()
        if is_valid and len(bvr_reference) == 26:
            bvr_reference = mod10r(bvr_reference)
        elif is_valid and len(bvr_reference) == 27:
            valid_ref = mod10r(bvr_reference[:-1])
            is_valid = (valid_ref == bvr_reference)
        else:
            is_valid = False

        res = dict()
        if is_valid:
            res['value'] = {'bvr_reference': bvr_reference}
        elif bvr_reference:
            res['warning'] = {'title': _('Warning'),
                              'message': _('The reference of the partner '
                                           'has not been set, or is in '
                                           'wrong format. Please make sure'
                                           ' to enter a valid BVR '
                                           'reference for the contract.')}
        return res

    def compute_partner_bvr_ref(self, cr, uid, ids, partner=None,
                                is_lsv=False, context=None):
        """ Generates a new BVR Reference.
        See file \\nas\it\devel\Code_ref_BVR.xls for more information."""
        if ids:
            # If group was already existing, retrieve any existing reference
            ref = self.browse(cr, uid, ids[0], context).bvr_reference
            if ref:
                return ref
        partner = partner or self.browse(cr, uid, ids[0], context).partner_id
        result = '0' * (9 + (7 - len(partner.ref))) + partner.ref
        count_groups = str(self.search(
            cr, uid, [('partner_id', '=', partner.id)], context=context,
            count=True))
        result += '0' * (5 - len(count_groups)) + count_groups
        # Type '0' = Sponsorship
        result += '0'
        result += '0' * 4

        if is_lsv:
            result = '004874969' + result[9:]
        if len(result) == 26:
            return mod10r(result)

    def _setup_inv_data(self, cr, uid, con_gr, journal_ids, invoicer_id,
                        context=None):
        """ Inherit to add BVR ref """
        inv_data = super(contract_group, self)._setup_inv_data(
            cr, uid, con_gr, journal_ids, invoicer_id, context)

        ref = ''
        if con_gr.bvr_reference:
            ref = con_gr.bvr_reference
        elif (con_gr.payment_term_id and
              (_('LSV') in con_gr.payment_term_id.name or
               _('Direct Debit') in con_gr.payment_term_id.name)):
            seq = self.pool['ir.sequence']
            ref = mod10r(seq.next_by_code(cr, uid, 'contract.bvr.ref'))
        inv_data.update({
            'bvr_reference': ref})

        return inv_data

    def _setup_inv_line_data(self, cr, uid, contract_line, invoice_id,
                             context=None):
        """ Inherit to add analytic distribution """
        inv_line_data = super(contract_group, self)._setup_inv_line_data(
            cr, uid, contract_line, invoice_id, context)

        product_id = contract_line.product_id.id
        partner_id = contract_line.contract_id.partner_id.id
        analytic = self.pool.get('account.analytic.default').account_get(
            cr, uid, product_id, partner_id, uid, time.strftime('%Y-%m-%d'),
            context=context)
        if analytic and analytic.analytics_id:
            inv_line_data.update({'analytics_id': analytic.analytics_id.id})

        return inv_line_data

    def _get_gen_states(self):
        return ['waiting', 'active']

    def write(self, cr, uid, ids, vals, context=None):
        """ If sponsor changes his payment term to LSV or DD,
        change the state of related contracts so that we wait
        for a valid mandate before generating new invoices.
        """
        contract_ids = list()
        inv_vals = dict()
        if 'payment_term_id' in vals:
            inv_vals['payment_term'] = vals['payment_term_id']
            payment_term = self.pool.get('account.payment.term').browse(
                cr, uid, vals['payment_term_id'], {'lang': 'en_US'})
            payment_name = payment_term.name
            wf_service = netsvc.LocalService('workflow')
            for group in self.browse(cr, uid, ids, context):
                old_term = group.payment_term_id.name
                for contract in group.contract_ids:
                    contract_ids.append(contract.id)
                    if 'LSV' in payment_name or 'Postfinance' in payment_name:
                        wf_service.trg_validate(
                            uid, 'recurring.contract', contract.id,
                            'will_pay_by_lsv_dd', cr)
                        # LSV/DD Contracts need no reference
                        if group.bvr_reference and \
                                'multi-months' not in payment_name:
                            vals['bvr_reference'] = False
                    elif 'LSV' in old_term or 'Postfinance' in old_term:
                        wf_service.trg_validate(
                            uid, 'recurring.contract', contract.id,
                            'mandate_validated', cr)
        if 'bvr_reference' in vals:
            inv_vals['bvr_reference'] = vals['bvr_reference']
            for group in self.browse(cr, uid, ids, context):
                contract_ids.extend([c.id for c in group.contract_ids])

        res = super(contract_group, self).write(cr, uid, ids, vals, context)

        if contract_ids:
            # Update related open invoices to reflect the changes
            inv_line_obj = self.pool.get('account.invoice.line')
            inv_obj = self.pool.get('account.invoice')
            inv_line_ids = inv_line_obj.search(cr, uid, [
                ('contract_id', 'in', contract_ids),
                ('state', 'not in', ('paid', 'cancel'))], context=context)
            invoice_ids = list(set([inv_l.invoice_id.id for inv_l in
                                    inv_line_obj.browse(cr, uid, inv_line_ids,
                                                        context)]))
            inv_obj.action_cancel(cr, uid, invoice_ids, context)
            inv_obj.action_cancel_draft(cr, uid, invoice_ids)
            inv_obj.write(cr, uid, invoice_ids, inv_vals, context)
            wf_service = netsvc.LocalService('workflow')
            for invoice_id in invoice_ids:
                wf_service.trg_validate(uid, 'account.invoice', invoice_id,
                                        'invoice_open', cr)
        return res

    def clean_invoices(self, cr, uid, group, context=None):
        """ Override clean_invoices to delete cancelled invoices """
        inv_ids = super(contract_group, self).clean_invoices(
            cr, uid, group, context=None)
        if inv_ids:
            inv_ids = list(inv_ids)
            cr.execute(
                "DELETE FROM account_invoice "
                "WHERE id IN ({0})".format(
                    ','.join([str(id) for id in inv_ids])))
        return inv_ids
<|endoftext|>


<commit_before>import py, os, math, time
from spyvm import model, model_display, storage_contexts, constants, primitives, wrapper, display
from spyvm.primitives import prim_table, PrimitiveFailedError
from spyvm.plugins import bitblt
from rpython.rlib.rfloat import isinf, isnan
from rpython.rlib.rarithmetic import intmask, r_uint
from rpython.rtyper.lltypesystem import lltype, rffi
from .util import create_space, copy_to_module, cleanup_module, TestInterpreter, very_slow_test

IMAGENAME = "anImage.image"

def mock(space, stack, context = None):
    mapped_stack = [space.w(x) for x in stack]
    frame = context
    for i in range(len(stack)):
        frame.as_context_get_shadow(space).push(stack[i])
    interp = TestInterpreter(space)
    interp.space._image_name.set(IMAGENAME)
    return interp, frame, len(stack)

def _prim(space, code, stack, context = None):
    interp, w_frame, argument_count = mock(space, stack, context)
    prim_table[code](interp, w_frame.as_context_get_shadow(space), argument_count-1, context and context.as_context_get_shadow(space).w_method())
    res = w_frame.as_context_get_shadow(space).pop()
    s_frame = w_frame.as_context_get_shadow(space)
    assert not s_frame.stackdepth() - s_frame.tempsize() # check args are consumed
    return res

def prim(code, stack, context = None):
    return _prim(space, code, stack, context)

def external_call(module_name, method_name, stack):
    w_description = model.W_PointersObject(space, space.classtable['w_Array'], 2)
    w_description.atput0(space, 0, space.w(module_name))
    w_description.atput0(space, 1, space.w(method_name))
    context = new_frame("<not called>", [w_description], stack[0], stack[1:])[0]
    return prim(primitives.EXTERNAL_CALL, stack, context)

def setup_module():
    space = create_space(bootstrap = True)
    wrap = space.w
    bootstrap_class = space.bootstrap_class
    new_frame = space.make_frame
    copy_to_module(locals(), __name__)

def teardown_module():
    cleanup_module(__name__)

def test_fileplugin_filedelete(monkeypatch):
    def remove(file_path):
        assert file_path == 'myFile'
        return 0
    monkeypatch.setattr(os, "remove", remove)
    try:
        stack = [space.w(1), space.wrap_string("myFile")]
        w_c = external_call('FilePlugin', 'primitiveFileDelete', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_filedelete_raises(monkeypatch):
    def remove(file_path):
        raise OSError()
    monkeypatch.setattr(os, "remove", remove)

    try:
        with py.test.raises(PrimitiveFailedError):
            stack = [space.w(1), space.wrap_string("myFile")]
            w_c = external_call('FilePlugin', 'primitiveFileDelete', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_dircreate(monkeypatch):
    def mkdir(dir_path, mode):
        assert dir_path == 'myPrimDir'
        assert mode == 0777
        return 0
    monkeypatch.setattr(os, "mkdir", mkdir)
    try:
        stack = [space.w(1), space.wrap_string("myPrimDir")]
        w_c = external_call('FilePlugin', 'primitiveDirectoryCreate', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_dircreate_raises(monkeypatch):
    def mkdir(dir_path, mode):
        raise OSError()
    monkeypatch.setattr(os, "mkdir", mkdir)

    try:
        with py.test.raises(PrimitiveFailedError):
            stack = [space.w(1), space.wrap_string("myPrimDir")]
            w_c = external_call('FilePlugin', 'primitiveDirectoryCreate', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_dirdelete(monkeypatch):
    def rmdir(dir_path):
        assert dir_path == 'myPrimDir'
        return 0
    monkeypatch.setattr(os, "rmdir", rmdir)
    try:
        stack = [space.w(1), space.wrap_string("myPrimDir")]
        w_c = external_call('FilePlugin', 'primitiveDirectoryDelete', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_filewrite_bytes(monkeypatch):
    def write(fd, data):
        assert len(data) == 4
        assert data == 'abcd'
    monkeypatch.setattr(os, "write", write)

    content = model.W_BytesObject(space, space.w_String, 4)
    content.bytes = ["a", "b", "c", "d"]
    try:
        stack = [space.w(1), space.w(1), content, space.w(0), space.w(4)]
        w_c = external_call('FilePlugin', 'primitiveFileWrite', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_filewrite_words(monkeypatch):
    def write(fd, data):
        assert len(data) == 4
        assert data == 'dcba'
    monkeypatch.setattr(os, "write", write)

    content = model.W_WordsObject(space, space.w_String, 1)
    content.words = [rffi.r_uint(1633837924)]
    try:
        stack = [space.w(1), space.w(1), content, space.w(0), space.w(1)]
        w_c = external_call('FilePlugin', 'primitiveFileWrite', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_dirdelete_raises(monkeypatch):
    def rmdir(dir_path):
        raise OSError()
    monkeypatch.setattr(os, "rmdir", rmdir)

    try:
        with py.test.raises(PrimitiveFailedError):
            stack = [space.w(1), space.wrap_string("myPrimDir")]
            w_c = external_call('FilePlugin', 'primitiveDirectoryDelete', stack)
    finally:
        monkeypatch.undo()
<commit_msg>Removed newline.<commit_after>import py, os, math, time
from spyvm import model, model_display, storage_contexts, constants, primitives, wrapper, display
from spyvm.primitives import prim_table, PrimitiveFailedError
from spyvm.plugins import bitblt
from rpython.rlib.rfloat import isinf, isnan
from rpython.rlib.rarithmetic import intmask, r_uint
from rpython.rtyper.lltypesystem import lltype, rffi
from .util import create_space, copy_to_module, cleanup_module, TestInterpreter, very_slow_test

IMAGENAME = "anImage.image"

def mock(space, stack, context = None):
    mapped_stack = [space.w(x) for x in stack]
    frame = context
    for i in range(len(stack)):
        frame.as_context_get_shadow(space).push(stack[i])
    interp = TestInterpreter(space)
    interp.space._image_name.set(IMAGENAME)
    return interp, frame, len(stack)

def _prim(space, code, stack, context = None):
    interp, w_frame, argument_count = mock(space, stack, context)
    prim_table[code](interp, w_frame.as_context_get_shadow(space), argument_count-1, context and context.as_context_get_shadow(space).w_method())
    res = w_frame.as_context_get_shadow(space).pop()
    s_frame = w_frame.as_context_get_shadow(space)
    assert not s_frame.stackdepth() - s_frame.tempsize() # check args are consumed
    return res

def prim(code, stack, context = None):
    return _prim(space, code, stack, context)

def external_call(module_name, method_name, stack):
    w_description = model.W_PointersObject(space, space.classtable['w_Array'], 2)
    w_description.atput0(space, 0, space.w(module_name))
    w_description.atput0(space, 1, space.w(method_name))
    context = new_frame("<not called>", [w_description], stack[0], stack[1:])[0]
    return prim(primitives.EXTERNAL_CALL, stack, context)

def setup_module():
    space = create_space(bootstrap = True)
    wrap = space.w
    bootstrap_class = space.bootstrap_class
    new_frame = space.make_frame
    copy_to_module(locals(), __name__)

def teardown_module():
    cleanup_module(__name__)

def test_fileplugin_filedelete(monkeypatch):
    def remove(file_path):
        assert file_path == 'myFile'
        return 0
    monkeypatch.setattr(os, "remove", remove)
    try:
        stack = [space.w(1), space.wrap_string("myFile")]
        w_c = external_call('FilePlugin', 'primitiveFileDelete', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_filedelete_raises(monkeypatch):
    def remove(file_path):
        raise OSError()
    monkeypatch.setattr(os, "remove", remove)

    try:
        with py.test.raises(PrimitiveFailedError):
            stack = [space.w(1), space.wrap_string("myFile")]
            w_c = external_call('FilePlugin', 'primitiveFileDelete', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_dircreate(monkeypatch):
    def mkdir(dir_path, mode):
        assert dir_path == 'myPrimDir'
        assert mode == 0777
        return 0
    monkeypatch.setattr(os, "mkdir", mkdir)
    try:
        stack = [space.w(1), space.wrap_string("myPrimDir")]
        w_c = external_call('FilePlugin', 'primitiveDirectoryCreate', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_dircreate_raises(monkeypatch):
    def mkdir(dir_path, mode):
        raise OSError()
    monkeypatch.setattr(os, "mkdir", mkdir)

    try:
        with py.test.raises(PrimitiveFailedError):
            stack = [space.w(1), space.wrap_string("myPrimDir")]
            w_c = external_call('FilePlugin', 'primitiveDirectoryCreate', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_dirdelete(monkeypatch):
    def rmdir(dir_path):
        assert dir_path == 'myPrimDir'
        return 0
    monkeypatch.setattr(os, "rmdir", rmdir)
    try:
        stack = [space.w(1), space.wrap_string("myPrimDir")]
        w_c = external_call('FilePlugin', 'primitiveDirectoryDelete', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_filewrite_bytes(monkeypatch):
    def write(fd, data):
        assert len(data) == 4
        assert data == 'abcd'
    monkeypatch.setattr(os, "write", write)

    content = model.W_BytesObject(space, space.w_String, 4)
    content.bytes = ["a", "b", "c", "d"]
    try:
        stack = [space.w(1), space.w(1), content, space.w(0), space.w(4)]
        w_c = external_call('FilePlugin', 'primitiveFileWrite', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_filewrite_words(monkeypatch):
    def write(fd, data):
        assert len(data) == 4
        assert data == 'dcba'
    monkeypatch.setattr(os, "write", write)

    content = model.W_WordsObject(space, space.w_String, 1)
    content.words = [rffi.r_uint(1633837924)]
    try:
        stack = [space.w(1), space.w(1), content, space.w(0), space.w(1)]
        w_c = external_call('FilePlugin', 'primitiveFileWrite', stack)
    finally:
        monkeypatch.undo()

def test_fileplugin_dirdelete_raises(monkeypatch):
    def rmdir(dir_path):
        raise OSError()
    monkeypatch.setattr(os, "rmdir", rmdir)

    try:
        with py.test.raises(PrimitiveFailedError):
            stack = [space.w(1), space.wrap_string("myPrimDir")]
            w_c = external_call('FilePlugin', 'primitiveDirectoryDelete', stack)
    finally:
        monkeypatch.undo()<|endoftext|>


<commit_before>import yaml
from pi_gpio import app, socketio
from flask.ext import restful
from flask import render_template
import RPi.GPIO as GPIO
from handlers import PinList, PinDetail

api = restful.Api(app)
api.add_resource(PinList, '/api/v1/pin')
api.add_resource(PinDetail, '/api/v1/pin/<string:pin_num>')

PINS_YML = './config/pins.yml'
EDGE = {
    'RISING': GPIO.RISING,
    'FALLING': GPIO.FALLING,
    'BOTH': GPIO.BOTH
}
PINS = None

def read_pin_config():
    with open(PINS_YML) as file_data:
        pins = yaml.safe_load(file_data)
    return pins

def event_callback(num):
    data = {
        'num': num
    }
    socketio.emit('pin:event', data)
    print(data)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
    GPIO.add_event_detect(23, GPIO.RISING, callback=event_callback)
    return render_template('index.html')
<commit_msg>Interesting<commit_after>import yaml
from pi_gpio import app, socketio
from flask.ext import restful
from flask import render_template
import RPi.GPIO as GPIO
from handlers import PinList, PinDetail

api = restful.Api(app)
api.add_resource(PinList, '/api/v1/pin')
api.add_resource(PinDetail, '/api/v1/pin/<string:pin_num>')

PINS_YML = './config/pins.yml'
EDGE = {
    'RISING': GPIO.RISING,
    'FALLING': GPIO.FALLING,
    'BOTH': GPIO.BOTH
}
PINS = None

def read_pin_config():
    with open(PINS_YML) as file_data:
        pins = yaml.safe_load(file_data)
    return pins

def event_callback(num):
    data = {
        'num': num
    }
    socketio.emit('pin:event', data)
    print(data)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
    GPIO.add_event_detect(23, GPIO.RISING, callback=event_callback, bouncetime=500)
    return render_template('index.html')
<|endoftext|>


<commit_before>import datetime
import logging
import random
import sys

from mopidy import core

import pykka

logger = logging.getLogger(__name__)

class AutoFrontend(pykka.ThreadingActor, core.CoreListener):
    def __init__(self, config, core):
        super(AutoFrontend, self).__init__()
        self.config = config
        self.core = core

        # One history per section
        self.history = {0: [], 1: [], 2: []}

        # Keep track of the current playing section
        self.section = None

        # Where sections folders are located
        self.base_path = self.config['auto']['base_path']

        # Maximum amount of tracks to add per album
        self.max_tracks = self.config['auto']['max_tracks']
        if self.max_tracks <= 0:
            self.max_tracks = sys.maxsize

        logger.info("Auto base path: %s, max tracks: %d", self.base_path, self.max_tracks)

        # Read all section config to an array. Ready for dynamic amount of sections
        self.sections = []
        section = 0

        exists = "s{}_folder".format(section) in self.config['auto']
        while exists:
            self.sections.append({
                "start": self.config['auto']["s{}_start".format(section)],
                "folder": self.config['auto']["s{}_folder".format(section)],
                "max_volume": self.config['auto']["s{}_max_volume".format(section)]
            })
            section += 1
            exists = "s{}_folder".format(section) in self.config['auto']

        if not self.sections:
            logger.error('Could not find any auto sections')
        else:
            logger.info('Found the following auto sections:')
            for section in self.sections:
                logger.info("Start: %s Folder: %s, Max Volume: %d%%",
                            section['start'],
                            section['folder'],
                            section['max_volume'])

    # Mopidy events
    # If tracklist has changed and is empty, play a random album
    def tracklist_changed(self):
        if self.core.tracklist.get_length().get() == 0:
            self.play_random_album()

    # If playback has stopped,
    def track_playback_ended(self, tl_track, time_position):
        # on the last track of the tracklist, play a random album
        if self.core.tracklist.index(tl_track).get() == self.core.tracklist.get_length().get() - 1:
            self.play_random_album()
        # and time is in new section, play random album
        elif self.section != self.get_section_by_time()[1]:
            logger.info("Time has changed to new section, play random album")
            self.play_random_album()

    # When resuming playback, check that time still in correct section, otherwise play new random album
    def track_playback_resumed(self, tl_track, time_position):
        if self.section != self.get_section_by_time()[1]:
            logger.info("Time has changed to new section, play random album")
            self.play_random_album()

    # Functions
    def play_random_album(self):
        section_index, self.section = self.get_section_by_time()
        logger.info("Auto play of random album, folder: %s", self.section['folder'])

        # Decrease volume if it's higher than sections max_volume
        if self.core.mixer.get_volume().get() > self.section['max_volume']:
            self.core.mixer.set_volume(self.section['max_volume'])

        # Find a random album from this sections URI
        uri = self.base_path + self.section['folder']
        tracks = self.get_random_album(uri, section_index)

        # If no tracks were found, try again
        while not tracks:
            tracks = self.get_random_album(uri, section_index)

        # Play the tracks
        self.play_uris(tracks)

    def get_section_by_time(self):
        now = datetime.datetime.now()
        now_minutes = now.hour * 60 + now.minute

        # Loop through sections in reverse to find the most accurate section
        for section in reversed(self.sections):
            section_start = section['start'].split(':')
            section_minutes = int(section_start[0]) * 60 + int(section_start[1])
            logger.info('Get section by time: {} >= {}? {}'
                        .format(now_minutes, section_minutes, now_minutes >= section_minutes))
            if now_minutes >= section_minutes:
                return self.sections.index(section), section

        return None

    def get_random_album(self, uri, section_index):
        track_uris = []
        logger.info("Navigating file structure, URI: %s", uri)

        # Get all directories or tracks at the current URI
        refs = self.core.library.browse(uri).get()

        # Loop through all refs looking for tracks
        for ref in refs:
            if ref.type == 'track':
                track_uris.append(ref.uri)

                # Limit amount of tracks added
                if len(track_uris) >= self.max_tracks:
                    logger.info("Reached maximum tracks from same folder: %d", self.max_tracks)
                    break

        # If tracks were found, save album to history and return tracks
        if len(track_uris) > 0:
            self.history[section_index].append(uri)
            return track_uris

        # If not, limit refs to unplayed ones
        refs = self.get_unplayed_directories(refs, section_index)

        # Handle empty leaf folder
        if len(refs) <= 0:
            logger.warning('Empty leaf folder found: %s', uri)
            return None

        #  and recursively get a random one
        rand_idx = random.randint(0, len(refs) - 1)
        return self.get_random_album(refs[rand_idx].uri, section_index)

    def play_uris(self, uris):
        logger.info("Found %d tracks", len(uris))

        # Clear tracklist
        self.core.tracklist.clear()

        # Add all tracks by URIs
        self.core.tracklist.add(uris=uris)

        # Start playing
        self.core.playback.play()

    def get_unplayed_directories(self, refs, section_index):
        # Get all directories that are not in this sections history
        unplayed = [x for x in refs if x.uri not in self.history[section_index]]

        # If all albums have been played:
        if len(unplayed) == 0:
            logger.info("Unique albums depleted. Clearing history")

            # Return a list containing all albums except the last one played
            unplayed = [x for x in refs if x.uri != self.history[section_index][-1]]

            # And clear the sections history
            self.history[section_index] = []

        return unplayed
<commit_msg>Log version when starting<commit_after>import datetime
import logging
import random
import sys

from mopidy import core

import pykka

import mopidy_auto

logger = logging.getLogger(__name__)

class AutoFrontend(pykka.ThreadingActor, core.CoreListener):
    def __init__(self, config, core):
        super(AutoFrontend, self).__init__()
        self.config = config
        self.core = core

        # One history per section
        self.history = {0: [], 1: [], 2: []}

        # Keep track of the current playing section
        self.section = None

        # Where sections folders are located
        self.base_path = self.config['auto']['base_path']

        # Maximum amount of tracks to add per album
        self.max_tracks = self.config['auto']['max_tracks']
        if self.max_tracks <= 0:
            self.max_tracks = sys.maxsize

        logger.info("Mopidy-Auto v%s", mopidy_auto.__version__)
        logger.info("Auto base path: %s, max tracks: %d", self.base_path, self.max_tracks)

        # Read all section config to an array. Ready for dynamic amount of sections
        self.sections = []
        section = 0

        exists = "s{}_folder".format(section) in self.config['auto']
        while exists:
            self.sections.append({
                "start": self.config['auto']["s{}_start".format(section)],
                "folder": self.config['auto']["s{}_folder".format(section)],
                "max_volume": self.config['auto']["s{}_max_volume".format(section)]
            })
            section += 1
            exists = "s{}_folder".format(section) in self.config['auto']

        if not self.sections:
            logger.error('Could not find any auto sections')
        else:
            logger.info('Found the following auto sections:')
            for section in self.sections:
                logger.info("Start: %s Folder: %s, Max Volume: %d%%",
                            section['start'],
                            section['folder'],
                            section['max_volume'])

    # Mopidy events
    # If tracklist has changed and is empty, play a random album
    def tracklist_changed(self):
        if self.core.tracklist.get_length().get() == 0:
            self.play_random_album()

    # If playback has stopped,
    def track_playback_ended(self, tl_track, time_position):
        # on the last track of the tracklist, play a random album
        if self.core.tracklist.index(tl_track).get() == self.core.tracklist.get_length().get() - 1:
            self.play_random_album()
        # and time is in new section, play random album
        elif self.section != self.get_section_by_time()[1]:
            logger.info("Time has changed to new section, play random album")
            self.play_random_album()

    # When resuming playback, check that time still in correct section, otherwise play new random album
    def track_playback_resumed(self, tl_track, time_position):
        if self.section != self.get_section_by_time()[1]:
            logger.info("Time has changed to new section, play random album")
            self.play_random_album()

    # Functions
    def play_random_album(self):
        section_index, self.section = self.get_section_by_time()
        logger.info("Auto play of random album, folder: %s", self.section['folder'])

        # Decrease volume if it's higher than sections max_volume
        if self.core.mixer.get_volume().get() > self.section['max_volume']:
            self.core.mixer.set_volume(self.section['max_volume'])

        # Find a random album from this sections URI
        uri = self.base_path + self.section['folder']
        tracks = self.get_random_album(uri, section_index)

        # If no tracks were found, try again
        while not tracks:
            tracks = self.get_random_album(uri, section_index)

        # Play the tracks
        self.play_uris(tracks)

    def get_section_by_time(self):
        now = datetime.datetime.now()
        now_minutes = now.hour * 60 + now.minute

        # Loop through sections in reverse to find the most accurate section
        for section in reversed(self.sections):
            section_start = section['start'].split(':')
            section_minutes = int(section_start[0]) * 60 + int(section_start[1])
            logger.info('Get section by time: {} >= {}? {}'
                        .format(now_minutes, section_minutes, now_minutes >= section_minutes))
            if now_minutes >= section_minutes:
                return self.sections.index(section), section

        return None

    def get_random_album(self, uri, section_index):
        track_uris = []
        logger.info("Navigating file structure, URI: %s", uri)

        # Get all directories or tracks at the current URI
        refs = self.core.library.browse(uri).get()

        # Loop through all refs looking for tracks
        for ref in refs:
            if ref.type == 'track':
                track_uris.append(ref.uri)

                # Limit amount of tracks added
                if len(track_uris) >= self.max_tracks:
                    logger.info("Reached maximum tracks from same folder: %d", self.max_tracks)
                    break

        # If tracks were found, save album to history and return tracks
        if len(track_uris) > 0:
            self.history[section_index].append(uri)
            return track_uris

        # If not, limit refs to unplayed ones
        refs = self.get_unplayed_directories(refs, section_index)

        # Handle empty leaf folder
        if len(refs) <= 0:
            logger.warning('Empty leaf folder found: %s', uri)
            return None

        #  and recursively get a random one
        rand_idx = random.randint(0, len(refs) - 1)
        return self.get_random_album(refs[rand_idx].uri, section_index)

    def play_uris(self, uris):
        logger.info("Found %d tracks", len(uris))

        # Clear tracklist
        self.core.tracklist.clear()

        # Add all tracks by URIs
        self.core.tracklist.add(uris=uris)

        # Start playing
        self.core.playback.play()

    def get_unplayed_directories(self, refs, section_index):
        # Get all directories that are not in this sections history
        unplayed = [x for x in refs if x.uri not in self.history[section_index]]

        # If all albums have been played:
        if len(unplayed) == 0:
            logger.info("Unique albums depleted. Clearing history")

            # Return a list containing all albums except the last one played
            unplayed = [x for x in refs if x.uri != self.history[section_index][-1]]

            # And clear the sections history
            self.history[section_index] = []

        return unplayed
<|endoftext|>


<commit_before>"""Takes the variants from two eVCF files and tracks the fate of each variant's category
between the two files."""
import time

import numpy as np
import pysam

from mitty.lib.evcfparse import parse_line

import logging
logger = logging.getLogger(__name__)

def create_partition_list():
  partitions = {
    '{}-{}'.format(a, b): {'count': {vtype: 0 for vtype in ['SNP', 'INDEL']}, 'fp': None}
    for a in ['TP', 'FN', 'GT']
    for b in ['TP', 'FN', 'GT']
  }

  partitions.update(
    {'{}-{}'.format(a, b): {'count': {vtype: 0 for vtype in ['SNP', 'INDEL']}, 'fp': None}
     for a in ['FP', 'N']
     for b in ['FP', 'N']
     if not (a == b == 'N')
     }
  )

  return partitions

def key_v(v, alt):
  return '{}.{}.{}.{}'.format(v.chrom, v.pos, v.ref, alt)

def process_v(v):
  cat, sz, alt = parse_line(v) if v is not None else (None, None, None)
  k = key_v(v, alt) if sz is not None else None
  return k, cat, sz

def update(v, var_k, var_cat, var_type, file_no, partitions, working_dict):
  """
  :param v: the variant
  :param var_k:
  :param var_cat:
  :param var_type: 'SNP' or 'INDEL'
  :param file_no: 0 or 1 depending on which file the variant came from
  :param partitions:
  :param working_dict:
  :return:
  """
  val = working_dict.get(var_k, None)
  if val is not None:  # You complete me
    if val[1 - file_no] is None:
      raise ValueError('Repeated identical entry: {}'.format(var_k))
    val[file_no] = {'cat': var_cat, 'type': var_type, 'v': v}
    cat_key = '{}-{}'.format(val[0]['cat'], val[1]['cat'])
    partitions[cat_key]['count'][var_type] += 1
    partitions[cat_key]['vcf'].write(val[0]['v'])
    del working_dict[var_k]
  elif var_k is not None:
    working_dict[var_k] = [None, None]
    working_dict[var_k][file_no] = {'cat': var_cat, 'type': var_type, 'v': v}

def main(fname_a, fname_b, out_prefix, high_confidence_region=None):
  partitions = create_partition_list()
  print(summary_table_text(partitions))

  vcf_a = pysam.VariantFile(fname_a, mode='rb' if fname_a.endswith('bcf') else 'r')
  vcf_b = pysam.VariantFile(fname_b, mode='rb' if fname_a.endswith('bcf') else 'r')
  for k, v in partitions.items():
    v['vcf'] = pysam.VariantFile(out_prefix + '-' + k + '.vcf', mode='w', header=vcf_a.header)

  logger.debug('Reading variants ...')
  t0 = time.time()
  ctr = 0
  working_dict = {}
  while 1:
    va, vb = next(vcf_a, None), next(vcf_b, None)
    if va is None and vb is None: break

    if high_confidence_region is not None:
      if high_confidence_region not in va.info.get('Regions', []):
        va = None
      if high_confidence_region not in vb.info.get('Regions', []):
        vb = None

    var_k_a, var_cat_a, var_sz_a = process_v(va)
    var_k_b, var_cat_b, var_sz_b = process_v(vb)

    update(va, var_k_a, var_cat_a, 'SNP' if var_sz_a == 0 else 'INDEL', 0, partitions, working_dict)
    update(vb, var_k_b, var_cat_b, 'SNP' if var_sz_a == 0 else 'INDEL', 1, partitions, working_dict)

    if var_k_a is not None: ctr += 1
    if var_k_b is not None: ctr += 1

    if 0 <= ctr % 100000 <= 1:
      t1 = time.time()
      logger.debug('Read {} variants ({:0.2f} v/s). Working dict size is {}'.format(ctr, ctr/(t1 - t0), len(working_dict)))

  t1 = time.time()
  logger.debug(
    'Finished reading {} variants ({:0.2f} v/s). Residual working dict size is {}'.format(ctr, ctr / (t1 - t0), len(working_dict)))

  # At this point, the working_dict better only contain N -> FP and FP -> N
  for k, v in working_dict.items():
    v_to_use, cat_k = (v[1], 'N-FP') if v[0] is None else (v[0], 'FP-N')
    assert v_to_use['cat'] == 'FP', '{}: Not a FP. There is a bug in the code'.format(k)
    partitions[cat_k]['count'][v_to_use['type']] += 1
    partitions[cat_k]['vcf'].write(v_to_use['v'])

  summary = summary_table_text(partitions)
  logger.debug(summary)

  with open(out_prefix + '-summary.txt', 'w') as fp:
    fp.write(summary)

def summary_table_text(partitions):
  def line(k):
    return ['{}:\t{}\t{}'.format(k.replace('-', ' -> '), partitions[k]['count']['SNP'], partitions[k]['count']['INDEL'])]

  res = []

  res += ['', 'Improvements\tSNP\tINDEL', '-' * 30]
  for key in ['FN-TP', 'FN-GT', 'GT-TP', 'FP-N']:
    res += line(key)

  res += ['', 'Unchanged\tSNP\tINDEL', '-' * 30]
  for key in ['TP-TP', 'FN-FN', 'GT-GT', 'FP-FP']:
    res += line(key)

  res += ['', 'Regression\tSNP\tINDEL', '-' * 30]
  for key in ['TP-FN', 'TP-GT', 'GT-FN', 'N-FP']:
    res += line(key)

  return '\n'.join(res)<commit_msg>refactoring: call-fate<commit_after>"""Takes the variants from two eVCF files and tracks the fate of each variant's category
between the two files."""
import time

import numpy as np
import pysam

from mitty.lib.evcfparse import parse_line

import logging
logger = logging.getLogger(__name__)

def gt_str(s):
  return '{}{}{}'.format(s['GT'][0], '|' if s.phased else '/', s['GT'][1])

def partition_list():
  return [
    'FN->TP',
    'FN->GT',
    'GT->TP',
    'FP->N',
    'TP->TP',
    'FN->FN',
    'GT->GT',
    'FP->FP',
    'TP->FN',
    'TP->GT',
    'GT->FN',
    'N->FP'
  ]

def write_output_header(out_fp, vcf_fp):
  """Write the header for the output file

  :param out_fp:
  :param vcf_fp:
  :param partitions:
  :return:
  """
  hdr = vcf_fp.header.copy()
  for k in hdr.info.keys():
    if k != 'Regions':
      hdr.info.remove_header(k)
  for k in hdr.formats.keys():
    if k != 'GT':
      hdr.formats.remove_header(k)

  hdr_lines = str(hdr).splitlines(keepends=True)
  hdr_lines[-1] = '\t'.join(hdr_lines[-1].split('\t')[:-2] + partition_list())

  out_fp.writelines(hdr_lines)

def partition_count_dict():
  return {
    k: {vtype: 0 for vtype in ['SNP', 'INDEL']}
    for k in partition_list()
  }

def variant_hash(v, alt):
  return '{}.{}.{}.{}'.format(v.chrom, v.pos, v.ref, alt)

def process_v(v):
  cat, sz, alt = parse_line(v) if v is not None else (None, None, None)
  k = variant_hash(v, alt) if sz is not None else None
  return k, cat, sz

def write_out_v(out_fp, v, var_change_cat):
  row = [str(v.chrom), str(v.pos), '.', v.ref, ','.join(v.alts)]
  gt = gt_str(v.samples['QUERY']) \
    if var_change_cat[:2] == 'FP' or var_change_cat[-2:] == 'FP' \
    else gt_str(v.samples['TRUTH'])
  row += ['0/0' if k != var_change_cat else gt for k in partition_list()]
  out_fp.write('\n' + '\t'.join(row))

def update(out_fp, v, var_k, var_cat, var_type, file_no, partitions, working_dict):
  """
  :param out_fp:
  :param v: the variant
  :param var_k:
  :param var_cat:
  :param var_type: 'SNP' or 'INDEL'
  :param file_no: 0 or 1 depending on which file the variant came from
  :param partitions:
  :param working_dict:
  :return:
  """
  val = working_dict.get(var_k, None)
  if val is not None:  # You complete me
    if val[1 - file_no] is None:
      raise ValueError('Repeated identical entry: {}'.format(var_k))
    val[file_no] = {'cat': var_cat, 'type': var_type, 'v': v}
    cat_key = '{}->{}'.format(val[0]['cat'], val[1]['cat'])
    partitions[cat_key][var_type] += 1
    write_out_v(out_fp, val[0]['v'], cat_key)  # We use val[0] because these are guaranteed not to be N->FP
    del working_dict[var_k]
  elif var_k is not None:
    working_dict[var_k] = [None, None]
    working_dict[var_k][file_no] = {'cat': var_cat, 'type': var_type, 'v': v}

def main(fname_a, fname_b, out_prefix, high_confidence_region=None):
  partitions = partition_count_dict()

  vcf_in = [
    pysam.VariantFile(fname_a, mode='rb' if fname_a.endswith('bcf') else 'r'),
    pysam.VariantFile(fname_b, mode='rb' if fname_a.endswith('bcf') else 'r')
  ]
  vcf_out = open(out_prefix + '.vcf', 'w')
  write_output_header(vcf_out, vcf_in[0])

  logger.debug('Reading variants ...')
  t0 = time.time()
  ctr, null_ctr = 0, 0
  working_dict = {}
  while null_ctr < 2:
    null_ctr = 0
    for file_no in [0, 1]:
      v = next(vcf_in[file_no], None)
      if v is None: null_ctr += 1
      if high_confidence_region is not None:
        if high_confidence_region not in v.info.get('Regions', []):
          v = None

      var_k, var_cat, var_sz = process_v(v)
      update(vcf_out, v, var_k, var_cat, 'SNP' if var_sz == 0 else 'INDEL', file_no, partitions, working_dict)
      if var_k is not None: ctr += 1

    if 0 <= ctr % 100000 <= 1:
      t1 = time.time()
      logger.debug('Read {} variants ({:0.2f} v/s). Working dict size is {}'.format(ctr, ctr/(t1 - t0), len(working_dict)))

  t1 = time.time()
  logger.debug(
    'Finished reading {} variants ({:0.2f} v/s). Residual working dict size is {}'.format(ctr, ctr / (t1 - t0), len(working_dict)))

  # At this point, the working_dict better only contain N -> FP and FP -> N
  for k, v in working_dict.items():
    v_to_use, cat_k = (v[1], 'N->FP') if v[0] is None else (v[0], 'FP->N')
    assert v_to_use['cat'] == 'FP', '{}: Not a FP. There is a bug in the code'.format(k)
    partitions[cat_k][v_to_use['type']] += 1
    write_out_v(vcf_out, v_to_use['v'], cat_k)

  summary = summary_table_text(partitions)
  logger.debug(summary)

  with open(out_prefix + '-summary.txt', 'w') as fp:
    fp.write(summary)

def summary_table_text(partitions):
  def line(k):
    return ['{}:\t\t{}\t{}'.format(k, partitions[k]['SNP'], partitions[k]['INDEL'])]

  res = []

  res += ['', 'Improvements\tSNP\tINDEL', '-' * 30]
  for key in partition_list()[:4]:
    res += line(key)

  res += ['', 'Unchanged\tSNP\tINDEL', '-' * 30]
  for key in partition_list()[4:8]:
    res += line(key)

  res += ['', 'Regression\tSNP\tINDEL', '-' * 30]
  for key in partition_list()[8:]:
    res += line(key)

  return '\n'.join(res)<|endoftext|>


<commit_before>import itertools
import sys

import blessings

from curtsies.fmtfuncs import red, bold, green, on_blue, yellow, on_red
from curtsies.window import FullscreenWindow
from curtsies.input import Input
from curtsies.fsarray import FSArray

class Entity(object):
    def __init__(self, display, x, y, speed=1):
        self.display = display
        self.x, self.y = x, y
        self.speed = speed

    def towards(self, entity):
        dx = entity.x - self.x
        dy = entity.y - self.y
        return vscale(self.speed, (sign(dx), sign(dy)))

    def die(self):
        self.speed = 0
        self.display = on_red(bold(yellow('o')))

def sign(n):
    return -1 if n < 0 else 0 if n == 0 else 1

def vscale(c, v):
    return tuple(c*x for x in v)

class World(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        n = 5
        self.player = Entity(on_blue(green(bold('5'))), width // 2, height // 2 - 2, speed=5)
        self.npcs = [Entity(on_blue(red('X')), i * width // (n * 2), j * height // (n * 2))
                     for i in range(1, 2*n, 2)
                     for j in range(1, 2*n, 2)]
        self.turn = 0

    entities = property(lambda self: self.npcs + [self.player])

    def move_entity(self, entity, dx, dy):
        entity.x = max(0, min(self.width-1, entity.x + dx))
        entity.y = max(0, min(self.height-1, entity.y + dy))

    def process_event(self, c):
        """Returns a message from tick() to be displayed if game is over"""
        if c == "":
            sys.exit()
        elif c in key_directions:
            self.move_entity(self.player, *vscale(self.player.speed, key_directions[c]))
        else:
            return "try arrow keys, w, a, s, d, or ctrl-D (you pressed %r)" % c
        return self.tick()

    def tick(self):
        """Returns a message to be displayed if game is over, else None"""
        for npc in self.npcs:
            self.move_entity(npc, *npc.towards(self.player))
        for entity1, entity2 in itertools.combinations(self.entities, 2):
            if (entity1.x, entity1.y) == (entity2.x, entity2.y):
                if self.player in (entity1, entity2):
                    return 'you lost on turn %d' % self.turn
                entity1.die()
                entity2.die()

        if all(npc.speed == 0 for npc in self.npcs):
            return 'you won on turn %d' % self.turn
        self.turn += 1
        if self.turn % 20 == 0:
            self.player.speed = max(1, self.player.speed - 1)
            self.player.display = on_blue(green(bold(str(self.player.speed))))

    def get_array(self):
        a = FSArray(self.height, self.width, bg='blue')
        for entity in self.entities:
            a[self.height - 1 - entity.y, entity.x] = entity.display
        return a

key_directions = {'<UP>':    (0, 1),
                  '<LEFT>': (-1, 0),
                  '<DOWN>':  (0,-1),
                  '<RIGHT>': (1, 0),
                  'w':       (0, 1),
                  'a':       (-1, 0),
                  's':       (0,-1),
                  'd':       (1, 0)}

def main():
    with FullscreenWindow(sys.stdout) as w:
        with Input(sys.stdin) as input_generator:
            rows, columns = w.t.height, w.t.width
            world = World(width=columns, height=rows) # note the row/column x/y swap!
            while True:
                w.render_to_terminal(world.get_array())
                c = input_generator.next()
                msg = world.process_event(c)
                if msg:
                    break
    print(msg)

if __name__ == '__main__':
    main()
<commit_msg>Use the generator interface<commit_after>import itertools
import sys

import blessings

from curtsies.fmtfuncs import red, bold, green, on_blue, yellow, on_red
from curtsies.window import FullscreenWindow
from curtsies.input import Input
from curtsies.fsarray import FSArray

class Entity(object):
    def __init__(self, display, x, y, speed=1):
        self.display = display
        self.x, self.y = x, y
        self.speed = speed

    def towards(self, entity):
        dx = entity.x - self.x
        dy = entity.y - self.y
        return vscale(self.speed, (sign(dx), sign(dy)))

    def die(self):
        self.speed = 0
        self.display = on_red(bold(yellow('o')))

def sign(n):
    return -1 if n < 0 else 0 if n == 0 else 1

def vscale(c, v):
    return tuple(c*x for x in v)

class World(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        n = 5
        self.player = Entity(on_blue(green(bold('5'))), width // 2, height // 2 - 2, speed=5)
        self.npcs = [Entity(on_blue(red('X')), i * width // (n * 2), j * height // (n * 2))
                     for i in range(1, 2*n, 2)
                     for j in range(1, 2*n, 2)]
        self.turn = 0

    entities = property(lambda self: self.npcs + [self.player])

    def move_entity(self, entity, dx, dy):
        entity.x = max(0, min(self.width-1, entity.x + dx))
        entity.y = max(0, min(self.height-1, entity.y + dy))

    def process_event(self, c):
        """Returns a message from tick() to be displayed if game is over"""
        if c == "":
            sys.exit()
        elif c in key_directions:
            self.move_entity(self.player, *vscale(self.player.speed, key_directions[c]))
        else:
            return "try arrow keys, w, a, s, d, or ctrl-D (you pressed %r)" % c
        return self.tick()

    def tick(self):
        """Returns a message to be displayed if game is over, else None"""
        for npc in self.npcs:
            self.move_entity(npc, *npc.towards(self.player))
        for entity1, entity2 in itertools.combinations(self.entities, 2):
            if (entity1.x, entity1.y) == (entity2.x, entity2.y):
                if self.player in (entity1, entity2):
                    return 'you lost on turn %d' % self.turn
                entity1.die()
                entity2.die()

        if all(npc.speed == 0 for npc in self.npcs):
            return 'you won on turn %d' % self.turn
        self.turn += 1
        if self.turn % 20 == 0:
            self.player.speed = max(1, self.player.speed - 1)
            self.player.display = on_blue(green(bold(str(self.player.speed))))

    def get_array(self):
        a = FSArray(self.height, self.width, bg='blue')
        for entity in self.entities:
            a[self.height - 1 - entity.y, entity.x] = entity.display
        return a

key_directions = {'<UP>':    (0, 1),
                  '<LEFT>': (-1, 0),
                  '<DOWN>':  (0,-1),
                  '<RIGHT>': (1, 0),
                  'w':       (0, 1),
                  'a':       (-1, 0),
                  's':       (0,-1),
                  'd':       (1, 0)}

def main():
    with FullscreenWindow(sys.stdout) as w:
        with Input(sys.stdin) as input_generator:
            rows, columns = w.t.height, w.t.width
            world = World(width=columns, height=rows) # note the row/column x/y swap!
            w.render_to_terminal(world.get_array())
            for c in input_generator:
                msg = world.process_event(c)
                if msg:
                    break
                w.render_to_terminal(world.get_array())
    print(msg)

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


<commit_before># Copyright 2014 hm authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

__version__ = '0.1.5'
<commit_msg>bump version<commit_after># Copyright 2014 hm authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

__version__ = '0.1.6'
<|endoftext|>