<commit_before>"""Representation of the SPL standard message."""
from struct import Struct
from math2d import Vector2, Pose2D
from math3d import Vector3
from naoth import Representations_pb2

class MixedTeamMessage(Struct):

    def __init__(self, data=None):
        """Constructor."""
        super(MixedTeamMessage, self).__init__('Q4B')
        # based on the given data the message is initialized with default values or parsed from the data string
        if data is not None:
            self.unpack(data)
        else:
            self.setDefaults()

    def setDefaults(self):
        self.timestamp = 0
        self.teamID = 4
        self.isPenalized = 0
        self.whistleDetected = 0
        self.dummy = 42

    def unpack(self, data):
        # check 'data' length
        if len(data) < self.size:
            raise Exception("Invalid data size for MixedTeamMessage")

        msg = Struct.unpack(self, data[:self.size])
        it = iter(msg)
        self.timestamp = next(it)
        self.teamID = next(it)
        self.isPenalized = next(it)
        self.whistleDetected = next(it)
        self.dummy = next(it)

    def pack(self):
        return Struct.pack(self,
                           self.timestamp,
                           self.teamID,
                           self.isPenalized,
                           self.whistleDetected,
                           self.dummy
        )

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

class SPLMessage(Struct):
    """Representation of the standard SPLMessage."""

    SPL_STANDARD_MESSAGE_STRUCT_HEADER = b'SPL '
    SPL_STANDARD_MESSAGE_STRUCT_VERSION = 6
    SPL_STANDARD_MESSAGE_DATA_SIZE = 780
    SPL_STANDARD_MESSAGE_MAX_NUM_OF_PLAYERS = 5

    def __init__(self, teamnumber:int=0, playernumber:int=0, data=None):
        """Constructor."""
        super(SPLMessage, self).__init__('4s3b?12f6B2h2bh')
        # based on the given data the message is initialized with default values or parsed from the data string
        if data is not None:
            self.unpack(data)
        else:
            self.setDefaults(teamnumber, playernumber)

    def setDefaults(self, teamnumber=0, playernumber=0):

        self.playerNumber = playernumber
        self.teamNumber = teamnumber
        self.fallen = False
        self.pose = Pose2D(Vector2(0.0, 0.0), 0.0)  # x, y, r | +/-4500, +/-3000
        self.walkingTo = Vector2(0.0, 0.0)
        self.shootingTo = Vector2(0.0, 0.0)
        self.ballAge = -1
        self.ballPosition = Vector2(0.0, 0.0)
        self.ballVelocity = Vector2(0.0, 0.0)
        self.suggestion = [0 for x in range(self.SPL_STANDARD_MESSAGE_MAX_NUM_OF_PLAYERS)]
        self.intention = 0
        self.averageWalkSpeed = 200  # see TeamCommSender
        self.maxKickDistance = 3000  # see TeamCommSender
        self.currentPositionConfidence = 100
        self.currentSideConfidence = 100

        self._mixed = MixedTeamMessage()

        self.data = Representations_pb2.BUUserTeamMessage()

        # set known default values of custom message part
        for field in self.data.DESCRIPTOR.fields:
            if field.has_default_value:
                setattr(self.data, field.name, field.default_value)

        self.numOfDataBytes = self.data.ByteSize() + self._mixed.size

    def pack(self):
        return Struct.pack(self,
                           self.SPL_STANDARD_MESSAGE_STRUCT_HEADER,
                           self.SPL_STANDARD_MESSAGE_STRUCT_VERSION,
                           self.playerNumber,
                           self.teamNumber,
                           self.fallen,
                           *self.pose.__dict__.values(),     # same as 'self.pose.x, self.pose.y, self.pose.r'
                           *self.walkingTo.__dict__.values(),
                           *self.shootingTo.__dict__.values(),
                           self.ballAge,
                           *self.ballPosition.__dict__.values(),
                           *self.ballVelocity.__dict__.values(),
                           *self.suggestion,
                           self.intention,
                           self.averageWalkSpeed,
                           self.maxKickDistance,
                           self.currentPositionConfidence,
                           self.currentSideConfidence,
                           (self.data.ByteSize() + self._mixed.size),
                           ) + self._mixed.pack()\
                             + self.data.SerializeToString()

    def unpack(self, data):
        # check 'data' length
        if len(data) < self.size:
            raise NotAnSplMessage("Not an SPL Message.")

        msg = Struct.unpack(self, data[:self.size])

        # check header
        if msg[0] != self.SPL_STANDARD_MESSAGE_STRUCT_HEADER:
            raise InvalidSplHeader("Invalid header! (" + msg[0].decode("utf-8") + ")")

        # check spl message version
        if msg[1] != self.SPL_STANDARD_MESSAGE_STRUCT_VERSION:
            raise WrongSplMessageVersion("Wrong version: received " + msg[1] + ", but expected " + self.SPL_STANDARD_MESSAGE_STRUCT_VERSION)

        # assign data
        it = iter(msg[2:])
        self.playerNumber = next(it)
        self.teamNumber = next(it)
        self.fallen = next(it)
        self.pose = Pose2D( Vector2(next(it), next(it)), next(it) )
        self.walkingTo = Vector2( next(it), next(it) )
        self.shootingTo = Vector2( next(it), next(it) )
        self.ballAge = next(it)
        self.ballPosition = Vector2( next(it), next(it) )
        self.ballVelocity = Vector2( next(it), next(it) )
        self.suggestion = [ next(it) for i in range(self.SPL_STANDARD_MESSAGE_MAX_NUM_OF_PLAYERS) ]
        self.intention = next(it)
        self.averageWalkSpeed = next(it)
        self.maxKickDistance = next(it)
        self.currentPositionConfidence = next(it)
        self.currentSideConfidence = next(it)
        self.numOfDataBytes = next(it)
        self.data = data[self.size:self.size+self.numOfDataBytes]
        # TODO: unpack MixedTeamData!?
        #'''
        #self._mixed = MixedTeamMessage()
        custom = None
        try:
            custom = Representations_pb2.BUUserTeamMessage()
            custom.ParseFromString(data[self.size:self.size+self.numOfDataBytes])
        except:
            # if we can't parse custom data - it is not our message
            pass
        finally:
            # if its our custom data ...
            if custom is not None and custom.ByteSize() > 0:
                self.data = custom

    def parseFromLogJson(self, data):
        self.__map_json_data('playerNum', 'playerNumber', data)
        self.__map_json_data('teamNum', 'teamNumber', data)
        self.__map_json_data('fallen', 'fallen', data)
        self.__map_json_data((self.pose,'x'), 'pose_x', data)
        self.__map_json_data((self.pose,'y'), 'pose_y', data)
        self.__map_json_data((self.pose,'r'), 'pose_a', data)
        self.__map_json_data((self.walkingTo,'x'), 'walkingTo_x', data)
        self.__map_json_data((self.walkingTo,'y'), 'walkingTo_y', data)
        self.__map_json_data((self.shootingTo,'x'), 'shootingTo_x', data)
        self.__map_json_data((self.shootingTo,'y'), 'shootingTo_y', data)
        self.__map_json_data('ballAge', 'ballAge', data)
        self.__map_json_data((self.ball,'x'), 'ball_x', data)
        self.__map_json_data((self.ball,'y'), 'ball_y', data)
        self.__map_json_data((self.ballVel,'x'), 'ballVel_x', data)
        self.__map_json_data((self.ballVel,'y'), 'ballVel_y', data)
        self.__map_json_data('suggestion', 'suggestion', data)
        self.__map_json_data('intention', 'intention', data)
        self.__map_json_data('averageWalkSpeed', 'averageWalkSpeed', data)
        self.__map_json_data('maxKickDistance', 'maxKickDistance', data)
        self.__map_json_data('currentPositionConfidence', 'currentPositionConfidence', data)
        self.__map_json_data('currentSideConfidence', 'currentSideConfidence', data)
        self.__map_json_data('numOfDataBytes', 'numOfDataBytes', data)
        self.__map_json_data('data', 'data', data, lambda x: bytes(list(map(lambda i: -128*(i//128)+(i%128),x))))

    def __map_json_data(self, var, key, data, func=lambda x: x):
        if key in data:
            if type(var) == tuple:
                var[0].__dict__[var[1]] = func(data[key])
            else:
                self.__dict__[var] = func(data[key])

    def __repr__(self):
        """Returns all 'active' message fields as string."""
        result = ""
        for attr in self.__dict__:
            #print(attr, isinstance(self.__dict__[attr], bytes), self.__dict__[attr])
            if attr == "data" and not isinstance(self.__dict__[attr], bytes):
                fields = self.__dict__[attr].DESCRIPTOR.fields_by_name
                for custom_attr in fields:
                    # do not show deprecated custom fields!
                    if fields[custom_attr].GetOptions().deprecated:
                        continue
                    result += "\t" + custom_attr + " = " + str(getattr(self.__dict__[attr], custom_attr)) + "\n"
            elif attr.startswith("_"):
                # skip attributes starting with '_' (private)
                continue
            else:
                result += "\t" + attr + " = " + str(self.__dict__[attr]) + "\n"
        return result

class NotAnSplMessage(Exception):
    """Exception, if given data is not an SPL message."""
    pass

class WrongSplMessageVersion(Exception):
    """Wrong spl message version exception."""
    pass

class InvalidSplHeader(Exception):
    """Wrong spl message header exception."""
    pass
<commit_msg>python adjustments for new proto files<commit_after>"""Representation of the SPL standard message."""
from struct import Struct
from math2d import Vector2, Pose2D
from math3d import Vector3
from naoth import TeamMessage_pb2

class MixedTeamMessage(Struct):

    def __init__(self, data=None):
        """Constructor."""
        super(MixedTeamMessage, self).__init__('Q4B')
        # based on the given data the message is initialized with default values or parsed from the data string
        if data is not None:
            self.unpack(data)
        else:
            self.setDefaults()

    def setDefaults(self):
        self.timestamp = 0
        self.teamID = 4
        self.isPenalized = 0
        self.whistleDetected = 0
        self.dummy = 42

    def unpack(self, data):
        # check 'data' length
        if len(data) < self.size:
            raise Exception("Invalid data size for MixedTeamMessage")

        msg = Struct.unpack(self, data[:self.size])
        it = iter(msg)
        self.timestamp = next(it)
        self.teamID = next(it)
        self.isPenalized = next(it)
        self.whistleDetected = next(it)
        self.dummy = next(it)

    def pack(self):
        return Struct.pack(self,
                           self.timestamp,
                           self.teamID,
                           self.isPenalized,
                           self.whistleDetected,
                           self.dummy
        )

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

class SPLMessage(Struct):
    """Representation of the standard SPLMessage."""

    SPL_STANDARD_MESSAGE_STRUCT_HEADER = b'SPL '
    SPL_STANDARD_MESSAGE_STRUCT_VERSION = 6
    SPL_STANDARD_MESSAGE_DATA_SIZE = 780
    SPL_STANDARD_MESSAGE_MAX_NUM_OF_PLAYERS = 5

    def __init__(self, teamnumber:int=0, playernumber:int=0, data=None):
        """Constructor."""
        super(SPLMessage, self).__init__('4s3b?12f6B2h2bh')
        # based on the given data the message is initialized with default values or parsed from the data string
        if data is not None:
            self.unpack(data)
        else:
            self.setDefaults(teamnumber, playernumber)

    def setDefaults(self, teamnumber=0, playernumber=0):

        self.playerNumber = playernumber
        self.teamNumber = teamnumber
        self.fallen = False
        self.pose = Pose2D(Vector2(0.0, 0.0), 0.0)  # x, y, r | +/-4500, +/-3000
        self.walkingTo = Vector2(0.0, 0.0)
        self.shootingTo = Vector2(0.0, 0.0)
        self.ballAge = -1
        self.ballPosition = Vector2(0.0, 0.0)
        self.ballVelocity = Vector2(0.0, 0.0)
        self.suggestion = [0 for x in range(self.SPL_STANDARD_MESSAGE_MAX_NUM_OF_PLAYERS)]
        self.intention = 0
        self.averageWalkSpeed = 200  # see TeamCommSender
        self.maxKickDistance = 3000  # see TeamCommSender
        self.currentPositionConfidence = 100
        self.currentSideConfidence = 100

        self._mixed = MixedTeamMessage()

        self.data = TeamMessage_pb2.BUUserTeamMessage()

        # set known default values of custom message part
        for field in self.data.DESCRIPTOR.fields:
            if field.has_default_value:
                setattr(self.data, field.name, field.default_value)

        self.numOfDataBytes = self.data.ByteSize() + self._mixed.size

    def pack(self):
        return Struct.pack(self,
                           self.SPL_STANDARD_MESSAGE_STRUCT_HEADER,
                           self.SPL_STANDARD_MESSAGE_STRUCT_VERSION,
                           self.playerNumber,
                           self.teamNumber,
                           self.fallen,
                           *self.pose.__dict__.values(),     # same as 'self.pose.x, self.pose.y, self.pose.r'
                           *self.walkingTo.__dict__.values(),
                           *self.shootingTo.__dict__.values(),
                           self.ballAge,
                           *self.ballPosition.__dict__.values(),
                           *self.ballVelocity.__dict__.values(),
                           *self.suggestion,
                           self.intention,
                           self.averageWalkSpeed,
                           self.maxKickDistance,
                           self.currentPositionConfidence,
                           self.currentSideConfidence,
                           (self.data.ByteSize() + self._mixed.size),
                           ) + self._mixed.pack()\
                             + self.data.SerializeToString()

    def unpack(self, data):
        # check 'data' length
        if len(data) < self.size:
            raise NotAnSplMessage("Not an SPL Message.")

        msg = Struct.unpack(self, data[:self.size])

        # check header
        if msg[0] != self.SPL_STANDARD_MESSAGE_STRUCT_HEADER:
            raise InvalidSplHeader("Invalid header! (" + msg[0].decode("utf-8") + ")")

        # check spl message version
        if msg[1] != self.SPL_STANDARD_MESSAGE_STRUCT_VERSION:
            raise WrongSplMessageVersion("Wrong version: received " + msg[1] + ", but expected " + self.SPL_STANDARD_MESSAGE_STRUCT_VERSION)

        # assign data
        it = iter(msg[2:])
        self.playerNumber = next(it)
        self.teamNumber = next(it)
        self.fallen = next(it)
        self.pose = Pose2D( Vector2(next(it), next(it)), next(it) )
        self.walkingTo = Vector2( next(it), next(it) )
        self.shootingTo = Vector2( next(it), next(it) )
        self.ballAge = next(it)
        self.ballPosition = Vector2( next(it), next(it) )
        self.ballVelocity = Vector2( next(it), next(it) )
        self.suggestion = [ next(it) for i in range(self.SPL_STANDARD_MESSAGE_MAX_NUM_OF_PLAYERS) ]
        self.intention = next(it)
        self.averageWalkSpeed = next(it)
        self.maxKickDistance = next(it)
        self.currentPositionConfidence = next(it)
        self.currentSideConfidence = next(it)
        self.numOfDataBytes = next(it)
        self.data = data[self.size:self.size+self.numOfDataBytes]
        # TODO: unpack MixedTeamData!?
        #'''
        #self._mixed = MixedTeamMessage()
        custom = None
        try:
            custom = TeamMessage_pb2.BUUserTeamMessage()
            custom.ParseFromString(data[self.size:self.size+self.numOfDataBytes])
        except:
            # if we can't parse custom data - it is not our message
            pass
        finally:
            # if its our custom data ...
            if custom is not None and custom.ByteSize() > 0:
                self.data = custom

    def parseFromLogJson(self, data):
        self.__map_json_data('playerNum', 'playerNumber', data)
        self.__map_json_data('teamNum', 'teamNumber', data)
        self.__map_json_data('fallen', 'fallen', data)
        self.__map_json_data((self.pose,'x'), 'pose_x', data)
        self.__map_json_data((self.pose,'y'), 'pose_y', data)
        self.__map_json_data((self.pose,'r'), 'pose_a', data)
        self.__map_json_data((self.walkingTo,'x'), 'walkingTo_x', data)
        self.__map_json_data((self.walkingTo,'y'), 'walkingTo_y', data)
        self.__map_json_data((self.shootingTo,'x'), 'shootingTo_x', data)
        self.__map_json_data((self.shootingTo,'y'), 'shootingTo_y', data)
        self.__map_json_data('ballAge', 'ballAge', data)
        self.__map_json_data((self.ball,'x'), 'ball_x', data)
        self.__map_json_data((self.ball,'y'), 'ball_y', data)
        self.__map_json_data((self.ballVel,'x'), 'ballVel_x', data)
        self.__map_json_data((self.ballVel,'y'), 'ballVel_y', data)
        self.__map_json_data('suggestion', 'suggestion', data)
        self.__map_json_data('intention', 'intention', data)
        self.__map_json_data('averageWalkSpeed', 'averageWalkSpeed', data)
        self.__map_json_data('maxKickDistance', 'maxKickDistance', data)
        self.__map_json_data('currentPositionConfidence', 'currentPositionConfidence', data)
        self.__map_json_data('currentSideConfidence', 'currentSideConfidence', data)
        self.__map_json_data('numOfDataBytes', 'numOfDataBytes', data)
        self.__map_json_data('data', 'data', data, lambda x: bytes(list(map(lambda i: -128*(i//128)+(i%128),x))))

    def __map_json_data(self, var, key, data, func=lambda x: x):
        if key in data:
            if type(var) == tuple:
                var[0].__dict__[var[1]] = func(data[key])
            else:
                self.__dict__[var] = func(data[key])

    def __repr__(self):
        """Returns all 'active' message fields as string."""
        result = ""
        for attr in self.__dict__:
            #print(attr, isinstance(self.__dict__[attr], bytes), self.__dict__[attr])
            if attr == "data" and not isinstance(self.__dict__[attr], bytes):
                fields = self.__dict__[attr].DESCRIPTOR.fields_by_name
                for custom_attr in fields:
                    # do not show deprecated custom fields!
                    if fields[custom_attr].GetOptions().deprecated:
                        continue
                    result += "\t" + custom_attr + " = " + str(getattr(self.__dict__[attr], custom_attr)) + "\n"
            elif attr.startswith("_"):
                # skip attributes starting with '_' (private)
                continue
            else:
                result += "\t" + attr + " = " + str(self.__dict__[attr]) + "\n"
        return result

class NotAnSplMessage(Exception):
    """Exception, if given data is not an SPL message."""
    pass

class WrongSplMessageVersion(Exception):
    """Wrong spl message version exception."""
    pass

class InvalidSplHeader(Exception):
    """Wrong spl message header exception."""
    pass
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from openerp import fields, api, models

class account_voucher(models.Model):
    _inherit = 'account.voucher'

    tax_move_line_ids = fields.One2many(
        'account.move.line',
        compute='_get_tax_move_lines',
        string='Tax Journal Items',
        # related='move_id.line_id',
        # domain=[('tax_code_id', '!=', False)],
        )
    # tax_move_line_ids = fields.One2many(
        # 'account.move.line',
        # domain=[('tax_code_id', '!=', False)],
        # )

    @api.one
    @api.depends(
        'move_id.line_id.tax_code_id',
        'move_id.line_id.tax_amount',
        )
    def _get_tax_move_lines(self):
        self.tax_move_line_ids = self.move_id.line_id.filtered(
            lambda r: r.tax_code_id and r.tax_amount)
<commit_msg>IMP tax settlement integration with voucher (cancelation)<commit_after># -*- coding: utf-8 -*-
from openerp import fields, api, models, _
from openerp.exceptions import Warning

class account_voucher(models.Model):
    _inherit = 'account.voucher'

    tax_move_line_ids = fields.One2many(
        'account.move.line',
        compute='_get_tax_move_lines',
        string='Tax Journal Items',
        )

    @api.one
    @api.depends(
        'move_id.line_id.tax_code_id',
        'move_id.line_id.tax_amount',
        )
    def _get_tax_move_lines(self):
        self.tax_move_line_ids = self.move_id.line_id.filtered(
            lambda r: r.tax_code_id and r.tax_amount)

    @api.multi
    def cancel_voucher(self):
        """
        We search all move lines that has been settled for vouchers and, after
        unreconcile, we try to unlink them. If one tax settlement has been paid
        or has been settled on a settlement, then won't allow you to cancel the
        voucher.
        """
        tax_settlement_moves = self.mapped('move_ids.tax_settlement_move_id')
        res = super(account_voucher, self).cancel_voucher()
        tax_settlement_moves.unlink()
        return res
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
###############################################################################
#
# ODOO (ex OpenERP)
# Open Source Management Solution
# Copyright (C) 2001-2015 Micronaet S.r.l. (<http://www.micronaet.it>)
# Developer: Nicola Riolini @thebrush (<https://it.linkedin.com/in/thebrush>)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# 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 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/>.
#
###############################################################################

import os
import sys
import logging
import openerp
import openerp.addons.decimal_precision as dp
from openerp.osv import fields, osv, expression, orm
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.tools.translate import _
from openerp.tools import (DEFAULT_SERVER_DATE_FORMAT,
    DEFAULT_SERVER_DATETIME_FORMAT,
    DATETIME_FORMATS_MAP,
    float_compare)

_logger = logging.getLogger(__name__)

class EdiLoadDdtLineWizard(orm.TransientModel):
    ''' Wizard for load DDT report
    '''
    _name = 'edi.load.ddt.line.wizard'

    # --------------------
    # Wizard button event:
    # --------------------
    def action_reload(self, cr, uid, ids, context=None):
        ''' Reload DDT file
            Update Order touched
        '''
        if context is None:
            context = {}

        # Launch opeation scheduled for reimport DDT (previous exported)
        folder_pool = self.pool.get('edi.order.folder')
        _logger.info('Reimport DDT and check lines')
        return folder_pool.load_scheduled_folder_selected(cr, uid, load_file=False,
            generate_line=False, import_invoice=False, import_ddt=True,
            context=context)

    def action_export_report(self, cr, uid, ids, context=None):
        ''' Extract DDT report with parameters
        '''
        _logger.info('Start export order VS DDT')
        tollerance = 0.0001

        if context is None:
            context = {}

        # Pool used:
        excel_pool = self.pool.get('excel.writer')
        order_pool = self.pool.get('edi.order')
        ddt_line_pool = self.pool.get('edi.ddt.line')

        # ---------------------------------------------------------------------
        # Parameters and domain filter:
        # ---------------------------------------------------------------------
        wiz_proxy = self.browse(cr, uid, ids, context=context)[0]

        mode = wiz_proxy.mode

        domain = [('has_ddt', '=', True)]
        domain_text = _('Order VS DDT (%s)') % mode

        # DDT filter:
        domain_ddt = []
        if wiz_proxy.from_date:
            domain_ddt.append(
                ('ddt_id.date', '>=', wiz_proxy.from_date))
            domain_text += _(' - Dalla date DDT %s') % excel_pool.format_date(
                wiz_proxy.from_date)
        if wiz_proxy.to_date:
            domain_ddt.append(
                ('ddt_id.date', '<=', wiz_proxy.from_date))
            domain_text += _(' - Alla data DDT %s') % excel_pool.format_date(
                wiz_proxy.to_date)
        if domain_ddt:
            ddt_ids = ddt_line_pool.search(
                cr, uid, domain_ddt, context=context)
            find_order_ids = [
                line.order_id.id for line in ddt_line_pool.browse(
                    cr, uid, ddt_ids, context=context)]
            domain.append(('id', 'in', tuple(set(find_order_ids))))

        # ---------------------------------------------------------------------
        # Create XSXL file:
        # ---------------------------------------------------------------------
        ws_name = _('Order')
        row = 0

        excel_pool.create_worksheet(ws_name)

        # ---------------------------------------------------------------------
        # Format elements:
        # ---------------------------------------------------------------------
        excel_pool.set_format()

        f_title = excel_pool.get_format('title')
        f_header = excel_pool.get_format('header')

        f_text = excel_pool.get_format('text')

        # Color setup:
        f_bg_white = excel_pool.get_format('bg_white')
        f_bg_red = excel_pool.get_format('bg_red')
        f_bg_green = excel_pool.get_format('bg_green')
        f_bg_yellow = excel_pool.get_format('bg_yellow')
        f_bg_blue = excel_pool.get_format('bg_blue')

        f_bg_white_number = excel_pool.get_format('bg_white_number')
        f_bg_red_number = excel_pool.get_format('bg_red_number')
        f_bg_green_number = excel_pool.get_format('bg_green_number')
        f_bg_yellow_number = excel_pool.get_format('bg_yellow_number')
        f_bg_blue_number = excel_pool.get_format('bg_blue_number')

        f_number = excel_pool.get_format('number')

        # ---------------------------------------------------------------------
        # Write title and header:
        # ---------------------------------------------------------------------
        # Title:
        excel_pool.write_xls_line(ws_name, row, [
            _('Filter:'),
            domain_text,
            ], default_format=f_title)

        # Header:
        row += 2
        if mode == 'ddt':
            excel_pool.column_width(ws_name, [22, 22, 35, 10])
            header = [
                _('DDT'),
                _('Ordine'),
                _('Data'),
                _('Differenza'),
                ]
        else:
            excel_pool.column_width(ws_name, [
                22, 22,
                20, 20,
                10, 10, 10, 10, 10, 10, 10,
                ])

            header = [
                _('DDT'),
                _('Ordine'),
                _('Ns Codice'),
                _('Vs Codice'),
                _('OC: Pdv'),
                _('DDT: Pdv'),
                _('OC: Q.'),
                _('DDT: Q.'),
                _('OC: Totale'),
                _('DDT: Totale'),
                _('Differenza'),
                ]

        excel_pool.write_xls_line(
            ws_name, row, header, default_format=f_header)

        # ---------------------------------------------------------------------
        # Extract data:
        # ---------------------------------------------------------------------
        # Database for DDT and order:
        ddt_db = {}
        ddt_ids = ddt_line_pool.search(cr, uid, [], context=context)
        for line in ddt_line_pool.browse(cr, uid, ddt_ids, context=context):
            if line.order_id.id not in ddt_db:
                ddt_db[line.order_id.id] = []
            if line.ddt_id not in ddt_db[line.order_id.id]:
                ddt_db[line.order_id.id].append(line.ddt_id)

        # Read order:
        res = {}
        order_ids = order_pool.search(cr, uid, domain, context=context)
        for order in order_pool.browse(cr, uid, order_ids, context=context):
            if order not in res:
                # Order ref: (gap, DDT check, Invoice check)
                res[order] = [0.0, [], []]

            # -----------------------------------------------------------------
            # DDT and invoice check:
            # -----------------------------------------------------------------
            for level, list_ids in (
                    (1, order.check_ddt_ids),
                    (2, order.check_invoice_ids)):
                for check in list_ids:
                    if abs(check.difference) >= tollerance:
                        res[order][0] += check.difference

                    if mode != 'ddt': # add line only if not ddt mode
                        res[order][level].append(check)

        # Print sorted order:
        total = 0.0
        for order in sorted(res, key=lambda x: x.name):

            # DDT Line color depend on difference: (red - green - white)
            difference = res[order][0]
            total += difference
            if abs(difference) <= tollerance:
                f_text_default = f_bg_white
                f_number_default = f_bg_white_number
                if mode == 'difference':
                    continue # jump line that write DDT header without diff.
                difference = 0.0 # Reset unused decimal
            elif difference > 0:
                f_text_default = f_bg_red
                f_number_default = f_bg_red_number
            else: # >0
                f_text_default = f_bg_green
                f_number_default = f_bg_green_number

            row += 1
            ddt_text = ''
            for item in ddt_db.get(order.id, ()):
                ddt_text += '%s%s [%s]' % (
                    ', ' if ddt_text else '',
                    item.name,
                    excel_pool.format_date(item.date),
                    )

            # Only DDT:
            if mode == 'ddt':
                excel_pool.write_xls_line(ws_name, row, [
                    ddt_text,
                    '%s%s' % (
                        order.name,
                        '(FATT.)' if order.has_invoice else '',
                        ),
                    order.date,
                    (difference, f_number_default),
                    ], default_format=f_text_default)
            else:
                excel_pool.write_xls_line(ws_name, row, [
                    ddt_text,
                    '%s%s [%s]' % (
                        order.name,
                        '(FATT.)' if order.has_invoice else '',
                        order.date,
                        ),
                    '', '', '', '', '', '', '', '',
                    (res[order][0], f_number_default),
                    ], default_format=f_text_default)

                # Detailed check line:
                for level in (1, 2):
                    for check in res[order][1]:
                        difference = check.difference or 0.0
                        if mode == 'difference' and abs(difference) <= tollerance:
                            continue # only case to jump

                        row += 1
                        # DDT Line color depend on status:
                        if check.state == 'correct':
                            f_text_default = f_bg_white
                            f_number_default = f_bg_white_number
                        elif check.state == 'difference':
                            f_text_default = f_bg_red
                            f_number_default = f_bg_red_number
                        elif check.state == 'order':
                            f_text_default = f_bg_blue
                            f_number_default = f_bg_blue_number
                        elif check.state == 'invoice':
                            f_text_default = f_bg_yellow
                            f_number_default = f_bg_yellow_number

                        excel_pool.write_xls_line(ws_name, row, [
                            '(DDT)' if level == 1 else '(FATT)',
                            check.name,
                            check.article,

                            (check.order_price, f_number_default),
                            (check.invoice_price, f_number_default),

                            (check.order_qty, f_number_default),
                            (check.invoice_qty, f_number_default),

                            (check.order_total, f_number_default),
                            (check.invoice_total, f_number_default),

                            (difference, f_number_default),
                            ], default_format=f_text_default, col=1)

        row += 1
        if mode=='ddt':
            col = 2
        else:
            col = 9
        if total > 0:
            excel_pool.write_xls_line(ws_name, row, ['Totale', total, ],
                default_format=f_bg_green_number, col=col)
        else:
            excel_pool.write_xls_line(ws_name, row, ['Totale', total, ],
                default_format=f_bg_red_number, col=col)

        return excel_pool.return_attachment(cr, uid, ws_name,
            version='7.0', php=False, context=context)

    _columns = {
        'mode': fields.selection([
            ('ddt', 'Only DDT'),
            ('difference', 'With difference'),
            ('all', 'All details'),
            ], 'Mode', required=True),

        'from_date': fields.date('From date'),
        'to_date': fields.date('To date'),

        'note': fields.text('Procedure', readonly=True),
        }

    _defaults = {
        'mode': lambda *x: 'ddt',

        'note': lambda *x: _('''
            DDT compare procedure:

            Launch sprix from Mexal Service menu:
            # 780 (Export DDT)

            When the exprot is finished press DONE

            '''),
        }

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>generic bugfix (fast message)<commit_after># -*- coding: utf-8 -*-
###############################################################################
#
# ODOO (ex OpenERP)
# Open Source Management Solution
# Copyright (C) 2001-2015 Micronaet S.r.l. (<http://www.micronaet.it>)
# Developer: Nicola Riolini @thebrush (<https://it.linkedin.com/in/thebrush>)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# 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 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/>.
#
###############################################################################

import os
import sys
import logging
import openerp
import openerp.addons.decimal_precision as dp
from openerp.osv import fields, osv, expression, orm
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.tools.translate import _
from openerp.tools import (DEFAULT_SERVER_DATE_FORMAT,
    DEFAULT_SERVER_DATETIME_FORMAT,
    DATETIME_FORMATS_MAP,
    float_compare)

_logger = logging.getLogger(__name__)

class EdiLoadDdtLineWizard(orm.TransientModel):
    ''' Wizard for load DDT report
    '''
    _name = 'edi.load.ddt.line.wizard'

    # --------------------
    # Wizard button event:
    # --------------------
    def action_reload(self, cr, uid, ids, context=None):
        ''' Reload DDT file
            Update Order touched
        '''
        if context is None:
            context = {}

        # Launch opeation scheduled for reimport DDT (previous exported)
        folder_pool = self.pool.get('edi.order.folder')
        _logger.info('Reimport DDT and check lines')
        return folder_pool.load_scheduled_folder_selected(cr, uid, load_file=False,
            generate_line=False, import_invoice=False, import_ddt=True,
            context=context)

    def action_export_report(self, cr, uid, ids, context=None):
        ''' Extract DDT report with parameters
        '''
        _logger.info('Start export order VS DDT')
        tollerance = 0.0001

        if context is None:
            context = {}

        # Pool used:
        excel_pool = self.pool.get('excel.writer')
        order_pool = self.pool.get('edi.order')
        ddt_line_pool = self.pool.get('edi.ddt.line')

        # ---------------------------------------------------------------------
        # Parameters and domain filter:
        # ---------------------------------------------------------------------
        wiz_proxy = self.browse(cr, uid, ids, context=context)[0]

        mode = wiz_proxy.mode

        domain = [('has_ddt', '=', True)]
        domain_text = _('Order VS DDT (%s)') % mode

        # DDT filter:
        domain_ddt = []
        if wiz_proxy.from_date:
            domain_ddt.append(
                ('ddt_id.date', '>=', wiz_proxy.from_date))
            domain_text += _(' - Dalla date DDT %s') % excel_pool.format_date(
                wiz_proxy.from_date)
        if wiz_proxy.to_date:
            domain_ddt.append(
                ('ddt_id.date', '<=', wiz_proxy.from_date))
            domain_text += _(' - Alla data DDT %s') % excel_pool.format_date(
                wiz_proxy.to_date)
        if domain_ddt:
            ddt_ids = ddt_line_pool.search(
                cr, uid, domain_ddt, context=context)
            find_order_ids = [
                line.order_id.id for line in ddt_line_pool.browse(
                    cr, uid, ddt_ids, context=context)]
            domain.append(('id', 'in', tuple(set(find_order_ids))))

        # ---------------------------------------------------------------------
        # Create XSXL file:
        # ---------------------------------------------------------------------
        ws_name = _('Order')
        row = 0

        excel_pool.create_worksheet(ws_name)

        # ---------------------------------------------------------------------
        # Format elements:
        # ---------------------------------------------------------------------
        excel_pool.set_format()

        f_title = excel_pool.get_format('title')
        f_header = excel_pool.get_format('header')

        f_text = excel_pool.get_format('text')

        # Color setup:
        f_bg_white = excel_pool.get_format('bg_white')
        f_bg_red = excel_pool.get_format('bg_red')
        f_bg_green = excel_pool.get_format('bg_green')
        f_bg_yellow = excel_pool.get_format('bg_yellow')
        f_bg_blue = excel_pool.get_format('bg_blue')

        f_bg_white_number = excel_pool.get_format('bg_white_number')
        f_bg_red_number = excel_pool.get_format('bg_red_number')
        f_bg_green_number = excel_pool.get_format('bg_green_number')
        f_bg_yellow_number = excel_pool.get_format('bg_yellow_number')
        f_bg_blue_number = excel_pool.get_format('bg_blue_number')

        f_number = excel_pool.get_format('number')

        # ---------------------------------------------------------------------
        # Write title and header:
        # ---------------------------------------------------------------------
        # Title:
        excel_pool.write_xls_line(ws_name, row, [
            _('Filter:'),
            domain_text,
            ], default_format=f_title)

        # Header:
        row += 2
        if mode == 'ddt':
            excel_pool.column_width(ws_name, [22, 22, 35, 10])
            header = [
                _('DDT'),
                _('Ordine'),
                _('Data'),
                _('Differenza'),
                ]
        else:
            excel_pool.column_width(ws_name, [
                22, 22,
                20, 20,
                10, 10, 10, 10, 10, 10, 10,
                ])

            header = [
                _('DDT'),
                _('Ordine'),
                _('Ns Codice'),
                _('Vs Codice'),
                _('OC: Pdv'),
                _('DDT: Pdv'),
                _('OC: Q.'),
                _('DDT: Q.'),
                _('OC: Totale'),
                _('DDT: Totale'),
                _('Differenza'),
                ]

        excel_pool.write_xls_line(
            ws_name, row, header, default_format=f_header)

        # ---------------------------------------------------------------------
        # Extract data:
        # ---------------------------------------------------------------------
        # Database for DDT and order:
        ddt_db = {}
        ddt_ids = ddt_line_pool.search(cr, uid, [], context=context)
        for line in ddt_line_pool.browse(cr, uid, ddt_ids, context=context):
            if line.order_id.id not in ddt_db:
                ddt_db[line.order_id.id] = []
            if line.ddt_id not in ddt_db[line.order_id.id]:
                ddt_db[line.order_id.id].append(line.ddt_id)

        # Read order:
        res = {}
        order_ids = order_pool.search(cr, uid, domain, context=context)
        for order in order_pool.browse(cr, uid, order_ids, context=context):
            if order not in res:
                # Order ref: (gap, DDT check, Invoice check)
                res[order] = [0.0, [], []]

            # -----------------------------------------------------------------
            # DDT and invoice check:
            # -----------------------------------------------------------------
            for level, list_ids in (
                    (1, order.check_ddt_ids),
                    (2, order.check_invoice_ids)):
                for check in list_ids:
                    if abs(check.difference) >= tollerance:
                        res[order][0] += check.difference

                    if mode != 'ddt': # add line only if not ddt mode
                        res[order][level].append(check)

        # Print sorted order:
        total = 0.0
        for order in sorted(res, key=lambda x: x.name):

            # DDT Line color depend on difference: (red - green - white)
            difference = res[order][0]
            total += difference
            if abs(difference) <= tollerance:
                f_text_default = f_bg_white
                f_number_default = f_bg_white_number
                if mode == 'difference':
                    continue # jump line that write DDT header without diff.
                difference = 0.0 # Reset unused decimal
            elif difference > 0:
                f_text_default = f_bg_red
                f_number_default = f_bg_red_number
            else: # >0
                f_text_default = f_bg_green
                f_number_default = f_bg_green_number

            row += 1
            ddt_text = ''
            for item in ddt_db.get(order.id, ()):
                ddt_text += '%s%s [%s]' % (
                    ', ' if ddt_text else '',
                    item.name,
                    excel_pool.format_date(item.date),
                    )

            # Only DDT:
            if mode == 'ddt':
                excel_pool.write_xls_line(ws_name, row, [
                    ddt_text,
                    '%s%s' % (
                        order.name,
                        '(FATT.)' if order.has_invoice else '',
                        ),
                    order.date,
                    (difference, f_number_default),
                    ], default_format=f_text_default)
            else:
                excel_pool.write_xls_line(ws_name, row, [
                    ddt_text,
                    '%s%s [%s]' % (
                        order.name,
                        '(FATT.)' if order.has_invoice else '',
                        order.date,
                        ),
                    '', '', '', '', '', '', '', '',
                    (res[order][0], f_number_default),
                    ], default_format=f_text_default)

                # Detailed check line:
                for level in (1, 2):
                    for check in res[order][level]:
                        difference = check.difference or 0.0
                        if mode == 'difference' and abs(difference) <= tollerance:
                            continue # only case to jump

                        row += 1
                        # DDT Line color depend on status:
                        if check.state == 'correct':
                            f_text_default = f_bg_white
                            f_number_default = f_bg_white_number
                        elif check.state == 'difference':
                            f_text_default = f_bg_red
                            f_number_default = f_bg_red_number
                        elif check.state == 'order':
                            f_text_default = f_bg_blue
                            f_number_default = f_bg_blue_number
                        elif check.state == 'invoice':
                            f_text_default = f_bg_yellow
                            f_number_default = f_bg_yellow_number

                        excel_pool.write_xls_line(ws_name, row, [
                            '(DDT)' if level == 1 else '(FATT)',
                            check.name,
                            check.article,

                            (check.order_price, f_number_default),
                            (check.invoice_price, f_number_default),

                            (check.order_qty, f_number_default),
                            (check.invoice_qty, f_number_default),

                            (check.order_total, f_number_default),
                            (check.invoice_total, f_number_default),

                            (difference, f_number_default),
                            ], default_format=f_text_default, col=1)

        row += 1
        if mode=='ddt':
            col = 2
        else:
            col = 9
        if total > 0:
            excel_pool.write_xls_line(ws_name, row, ['Totale', total, ],
                default_format=f_bg_green_number, col=col)
        else:
            excel_pool.write_xls_line(ws_name, row, ['Totale', total, ],
                default_format=f_bg_red_number, col=col)

        return excel_pool.return_attachment(cr, uid, ws_name,
            version='7.0', php=False, context=context)

    _columns = {
        'mode': fields.selection([
            ('ddt', 'Only DDT'),
            ('difference', 'With difference'),
            ('all', 'All details'),
            ], 'Mode', required=True),

        'from_date': fields.date('From date'),
        'to_date': fields.date('To date'),

        'note': fields.text('Procedure', readonly=True),
        }

    _defaults = {
        'mode': lambda *x: 'ddt',

        'note': lambda *x: _('''
            DDT compare procedure:

            Launch sprix from Mexal Service menu:
            # 780 (Export DDT)

            When the exprot is finished press DONE

            '''),
        }

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, _
from odoo.exceptions import UserError

class StockPickingBatch(models.Model):
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _name = "stock.picking.batch"
    _description = "Batch Transfer"
    _order = "name desc"

    name = fields.Char(
        string='Batch Transfer', default='New',
        copy=False, required=True, readonly=True,
        help='Name of the batch transfer')
    user_id = fields.Many2one(
        'res.users', string='Responsible', tracking=True, check_company=True,
        readonly=True, states={'draft': [('readonly', False)], 'in_progress': [('readonly', False)]},
        help='Person responsible for this batch transfer')
    company_id = fields.Many2one(
        'res.company', string="Company", required=True, readonly=True,
        index=True, default=lambda self: self.env.company)
    picking_ids = fields.One2many(
        'stock.picking', 'batch_id', string='Transfers', readonly=True,
        domain="[('id', 'in', allowed_picking_ids)]", check_company=True,
        states={'draft': [('readonly', False)], 'in_progress': [('readonly', False)]},
        help='List of transfers associated to this batch')
    show_check_availability = fields.Boolean(
        compute='_compute_move_ids',
        help='Technical field used to compute whether the check availability button should be shown.')
    allowed_picking_ids = fields.One2many('stock.picking', compute='_compute_allowed_picking_ids')
    move_ids = fields.One2many(
        'stock.move', string="Stock moves", compute='_compute_move_ids')
    move_line_ids = fields.One2many(
        'stock.move.line', string='Stock move lines',
        compute='_compute_move_ids', inverse='_set_move_line_ids', readonly=True,
        states={'draft': [('readonly', False)], 'in_progress': [('readonly', False)]})
    state = fields.Selection([
        ('draft', 'Draft'),
        ('in_progress', 'In progress'),
        ('done', 'Done'),
        ('cancel', 'Cancelled')], default='draft',
        copy=False, tracking=True, required=True, readonly=True)
    picking_type_id = fields.Many2one(
        'stock.picking.type', 'Operation Type', check_company=True, copy=False,
        readonly=True, states={'draft': [('readonly', False)]})

    @api.depends('company_id', 'picking_type_id', 'state')
    def _compute_allowed_picking_ids(self):
        allowed_picking_states = ['waiting', 'confirmed', 'assigned']
        cancelled_batchs = self.env['stock.picking.batch'].search_read(
            [('state', '=', 'cancel')], ['id']
        )
        cancelled_batch_ids = [batch['id'] for batch in cancelled_batchs]

        for batch in self:
            domain_states = list(allowed_picking_states)
            # Allows to add draft pickings only if batch is in draft as well.
            if batch.state == 'draft':
                domain_states.append('draft')
            domain = [
                ('company_id', '=', batch.company_id.id),
                ('immediate_transfer', '=', False),
                ('state', 'in', domain_states),
                '|',
                '|',
                ('batch_id', '=', False),
                ('batch_id', '=', batch.id),
                ('batch_id', 'in', cancelled_batch_ids),
            ]
            if batch.picking_type_id:
                domain += [('picking_type_id', '=', batch.picking_type_id.id)]
            batch.allowed_picking_ids = self.env['stock.picking'].search(domain)

    @api.depends('picking_ids', 'picking_ids.move_line_ids', 'picking_ids.move_lines', 'picking_ids.move_lines.state')
    def _compute_move_ids(self):
        for batch in self:
            batch.move_ids = batch.picking_ids.move_lines
            batch.move_line_ids = batch.picking_ids.move_line_ids
            batch.show_check_availability = any(m.state != 'assigned' for m in batch.move_ids)

    def _set_move_line_ids(self):
        new_move_lines = self[0].move_line_ids
        for picking in self.picking_ids:
            old_move_lines = picking.move_line_ids
            picking.move_line_ids = new_move_lines.filtered(lambda ml: ml.picking_id.id == picking.id)
            move_lines_to_unlink = old_move_lines - new_move_lines
            if move_lines_to_unlink:
                move_lines_to_unlink.unlink()

    # -------------------------------------------------------------------------
    # CRUD
    # -------------------------------------------------------------------------
    @api.model
    def create(self, vals):
        if vals.get('name', '/') == '/':
            vals['name'] = self.env['ir.sequence'].next_by_code('picking.batch') or '/'
        return super().create(vals)

    def write(self, vals):
        res = super().write(vals)
        if vals.get('picking_type_id'):
            self._sanity_check()
        if vals.get('picking_ids'):
            batch_without_picking_type = self.filtered(lambda batch: not batch.picking_type_id)
            if batch_without_picking_type:
                picking = self.picking_ids and self.picking_ids[0]
                batch_without_picking_type.picking_type_id = picking.picking_type_id.id
        return res

    def unlink(self):
        if any(batch.state != 'draft' for batch in self):
            raise UserError(_("You can only delete draft batch transfers."))
        return super().unlink()

    # -------------------------------------------------------------------------
    # Action methods
    # -------------------------------------------------------------------------
    def action_confirm(self):
        """Sanity checks, confirm the pickings and mark the batch as confirmed."""
        self.ensure_one()
        if not self.picking_ids:
            raise UserError(_("You have to set some pickings to batch."))
        self.picking_ids.action_confirm()
        self._check_company()
        self.state = 'in_progress'
        return True

    def action_cancel(self):
        self.ensure_one()
        self.state = 'cancel'
        return True

    def action_print(self):
        self.ensure_one()
        return self.env.ref('stock_picking_batch.action_report_picking_batch').report_action(self)

    def action_done(self):
        self.ensure_one()
        self._check_company()
        pickings = self.mapped('picking_ids').filtered(lambda picking: picking.state not in ('cancel', 'done'))
        if any(picking.state not in ('assigned') for picking in pickings):
            raise UserError(_('Some transfers are still waiting for goods. Please check or force their availability before setting this batch to done.'))

        for picking in pickings:
            picking.message_post(
                body="%s: %s %s" % (
                    _("Transferred by"),
                    _("Batch Transfer"),
                    picking.batch_id.id,
                    picking.batch_id.name))

        self.write({'state': 'done'})
        return self.picking_ids.button_validate()

    def action_assign(self):
        self.ensure_one()
        self.picking_ids.action_assign()

    # -------------------------------------------------------------------------
    # Miscellaneous
    # -------------------------------------------------------------------------
    def _sanity_check(self):
        for batch in self:
            if not batch.picking_ids <= batch.allowed_picking_ids:
                erroneous_pickings = batch.picking_ids - batch.allowed_picking_ids
                raise UserError(_(
                    "The following transfers cannot be added to batch transfer %s. "
                    "Please check their states and operation types, if they aren't immediate " +
                    "transfers or if they're not already part of another batch transfer.\n\n"
                    "Incompatibilities: %s") % (batch.name, ', '.join(erroneous_pickings.mapped('name'))))

    def _track_subtype(self, init_values):
        if 'state' in init_values:
            return self.env.ref('stock_picking_batch.mt_batch_state')
        return super()._track_subtype(init_values)

<commit_msg>[IMP] stock_picking_batch: compute state<commit_after># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, _
from odoo.exceptions import UserError

class StockPickingBatch(models.Model):
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _name = "stock.picking.batch"
    _description = "Batch Transfer"
    _order = "name desc"

    name = fields.Char(
        string='Batch Transfer', default='New',
        copy=False, required=True, readonly=True,
        help='Name of the batch transfer')
    user_id = fields.Many2one(
        'res.users', string='Responsible', tracking=True, check_company=True,
        readonly=True, states={'draft': [('readonly', False)], 'in_progress': [('readonly', False)]},
        help='Person responsible for this batch transfer')
    company_id = fields.Many2one(
        'res.company', string="Company", required=True, readonly=True,
        index=True, default=lambda self: self.env.company)
    picking_ids = fields.One2many(
        'stock.picking', 'batch_id', string='Transfers', readonly=True,
        domain="[('id', 'in', allowed_picking_ids)]", check_company=True,
        states={'draft': [('readonly', False)], 'in_progress': [('readonly', False)]},
        help='List of transfers associated to this batch')
    show_check_availability = fields.Boolean(
        compute='_compute_move_ids',
        help='Technical field used to compute whether the check availability button should be shown.')
    allowed_picking_ids = fields.One2many('stock.picking', compute='_compute_allowed_picking_ids')
    move_ids = fields.One2many(
        'stock.move', string="Stock moves", compute='_compute_move_ids')
    move_line_ids = fields.One2many(
        'stock.move.line', string='Stock move lines',
        compute='_compute_move_ids', inverse='_set_move_line_ids', readonly=True,
        states={'draft': [('readonly', False)], 'in_progress': [('readonly', False)]})
    state = fields.Selection([
        ('draft', 'Draft'),
        ('in_progress', 'In progress'),
        ('done', 'Done'),
        ('cancel', 'Cancelled')], default='draft',
        store=True, compute='_compute_state',
        copy=False, tracking=True, required=True, readonly=True)
    picking_type_id = fields.Many2one(
        'stock.picking.type', 'Operation Type', check_company=True, copy=False,
        readonly=True, states={'draft': [('readonly', False)]})

    @api.depends('company_id', 'picking_type_id', 'state')
    def _compute_allowed_picking_ids(self):
        allowed_picking_states = ['waiting', 'confirmed', 'assigned']
        cancelled_batchs = self.env['stock.picking.batch'].search_read(
            [('state', '=', 'cancel')], ['id']
        )
        cancelled_batch_ids = [batch['id'] for batch in cancelled_batchs]

        for batch in self:
            domain_states = list(allowed_picking_states)
            # Allows to add draft pickings only if batch is in draft as well.
            if batch.state == 'draft':
                domain_states.append('draft')
            domain = [
                ('company_id', '=', batch.company_id.id),
                ('immediate_transfer', '=', False),
                ('state', 'in', domain_states),
                '|',
                '|',
                ('batch_id', '=', False),
                ('batch_id', '=', batch.id),
                ('batch_id', 'in', cancelled_batch_ids),
            ]
            if batch.picking_type_id:
                domain += [('picking_type_id', '=', batch.picking_type_id.id)]
            batch.allowed_picking_ids = self.env['stock.picking'].search(domain)

    @api.depends('picking_ids', 'picking_ids.move_line_ids', 'picking_ids.move_lines', 'picking_ids.move_lines.state')
    def _compute_move_ids(self):
        for batch in self:
            batch.move_ids = batch.picking_ids.move_lines
            batch.move_line_ids = batch.picking_ids.move_line_ids
            batch.show_check_availability = any(m.state != 'assigned' for m in batch.move_ids)

    @api.depends('picking_ids', 'picking_ids.state')
    def _compute_state(self):
        batchs = self.filtered(lambda batch: batch.state not in ['cancel', 'done'])
        for batch in batchs:
            if not batch.picking_ids:
                return
            # Cancels automatically the batch picking if all its transfers are cancelled.
            if all(picking.state == 'cancel' for picking in batch.picking_ids):
                batch.state = 'cancel'
            # Batch picking is marked as done if all its not canceled transfers are done.
            elif all(picking.state in ['cancel', 'done'] for picking in batch.picking_ids):
                batch.state = 'done'

    def _set_move_line_ids(self):
        new_move_lines = self[0].move_line_ids
        for picking in self.picking_ids:
            old_move_lines = picking.move_line_ids
            picking.move_line_ids = new_move_lines.filtered(lambda ml: ml.picking_id.id == picking.id)
            move_lines_to_unlink = old_move_lines - new_move_lines
            if move_lines_to_unlink:
                move_lines_to_unlink.unlink()

    # -------------------------------------------------------------------------
    # CRUD
    # -------------------------------------------------------------------------
    @api.model
    def create(self, vals):
        if vals.get('name', '/') == '/':
            vals['name'] = self.env['ir.sequence'].next_by_code('picking.batch') or '/'
        return super().create(vals)

    def write(self, vals):
        res = super().write(vals)
        if vals.get('picking_type_id'):
            self._sanity_check()
        if vals.get('picking_ids'):
            batch_without_picking_type = self.filtered(lambda batch: not batch.picking_type_id)
            if batch_without_picking_type:
                picking = self.picking_ids and self.picking_ids[0]
                batch_without_picking_type.picking_type_id = picking.picking_type_id.id
        return res

    def unlink(self):
        if any(batch.state != 'draft' for batch in self):
            raise UserError(_("You can only delete draft batch transfers."))
        return super().unlink()

    # -------------------------------------------------------------------------
    # Action methods
    # -------------------------------------------------------------------------
    def action_confirm(self):
        """Sanity checks, confirm the pickings and mark the batch as confirmed."""
        self.ensure_one()
        if not self.picking_ids:
            raise UserError(_("You have to set some pickings to batch."))
        self.picking_ids.action_confirm()
        self._check_company()
        self.state = 'in_progress'
        return True

    def action_cancel(self):
        self.ensure_one()
        self.state = 'cancel'
        return True

    def action_print(self):
        self.ensure_one()
        return self.env.ref('stock_picking_batch.action_report_picking_batch').report_action(self)

    def action_done(self):
        self.ensure_one()
        self._check_company()
        pickings = self.mapped('picking_ids').filtered(lambda picking: picking.state not in ('cancel', 'done'))
        if any(picking.state not in ('assigned') for picking in pickings):
            raise UserError(_('Some transfers are still waiting for goods. Please check or force their availability before setting this batch to done.'))

        for picking in pickings:
            picking.message_post(
                body="%s: %s %s" % (
                    _("Transferred by"),
                    _("Batch Transfer"),
                    picking.batch_id.id,
                    picking.batch_id.name))
        return self.picking_ids.button_validate()

    def action_assign(self):
        self.ensure_one()
        self.picking_ids.action_assign()

    # -------------------------------------------------------------------------
    # Miscellaneous
    # -------------------------------------------------------------------------
    def _sanity_check(self):
        for batch in self:
            if not batch.picking_ids <= batch.allowed_picking_ids:
                erroneous_pickings = batch.picking_ids - batch.allowed_picking_ids
                raise UserError(_(
                    "The following transfers cannot be added to batch transfer %s. "
                    "Please check their states and operation types, if they aren't immediate " +
                    "transfers or if they're not already part of another batch transfer.\n\n"
                    "Incompatibilities: %s") % (batch.name, ', '.join(erroneous_pickings.mapped('name'))))

    def _track_subtype(self, init_values):
        if 'state' in init_values:
            return self.env.ref('stock_picking_batch.mt_batch_state')
        return super()._track_subtype(init_values)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models

class AccountInvoiceSend(models.TransientModel):
    _name = 'account.invoice.send'
    _inherit = 'account.invoice.send'

    partner_id = fields.Many2one('res.partner', compute='_get_partner', string='Partner')
    snailmail_is_letter = fields.Boolean('Send by Post', help='Allows to send the document by snail mail (coventional posting delivery service)', default=lambda self: self.env.user.company_id.snailmail_is_letter)
    snailmail_cost = fields.Float(string='Credits', compute='_snailmail_estimate', store=True, readonly=True)
    currency_id = fields.Many2one('res.currency', compute='_get_invoice_currency', string="Currency")
    letter_ids = fields.Many2many('snailmail.letter', 'snailmail_letter_account_invoice_send_rel', ondelete='cascade')

    @api.multi
    @api.onchange('invoice_ids')
    def _get_invoice_currency(self):
        for wizard in self:
            if wizard.invoice_ids:
                wizard.currency_id = wizard.invoice_ids.mapped('currency_id')[0].id

    @api.multi
    @api.onchange('invoice_ids')
    def _get_partner(self):
        for wizard in self:
            if wizard.invoice_ids and len(wizard.invoice_ids) == 1:
                wizard.partner_id = wizard.invoice_ids.partner_id.id

    @api.multi
    def unlink(self):
        for agc in self:
            agc.letter_ids.filtered(lambda l: l.state == 'draft').unlink()
        return super(AccountInvoiceSend, self).unlink()

    @api.multi
    def _fetch_letters(self):
        self.ensure_one()
        if not self.letter_ids:

            letters = self.env['snailmail.letter']
            for invoice in self.invoice_ids:
                letter = letters.create({
                    'partner_id': invoice.partner_id.id,
                    'model': 'account.invoice',
                    'res_id': invoice.id,
                    'user_id': self.env.user.id,
                    'company_id': invoice.company_id.id,
                    'report_template': self.env.ref('account.account_invoices').id
                })
                letters |= letter
            self.letter_ids = [(4, l.id) for l in letters]
        return self.letter_ids

    @api.onchange('snailmail_is_letter')
    def _snailmail_estimate(self):
        for wizard in self:
            if wizard.snailmail_is_letter:
                letters = wizard._fetch_letters()
                wizard.snailmail_cost = letters._snailmail_estimate()

    @api.multi
    def snailmail_print_action(self):
        for wizard in self:
            letters = wizard._fetch_letters()
            letters.write({'state': 'pending'})
            if len(letters) == 1:
                letters._snailmail_print()

    @api.multi
    def send_and_print_action(self):
        res = super(AccountInvoiceSend, self).send_and_print_action()
        if self.snailmail_is_letter:
            self.snailmail_print_action()
        return res
<commit_msg>[FIX] snailmail_account: updating field default value<commit_after># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models

class AccountInvoiceSend(models.TransientModel):
    _name = 'account.invoice.send'
    _inherit = 'account.invoice.send'

    partner_id = fields.Many2one('res.partner', compute='_get_partner', string='Partner')
    snailmail_is_letter = fields.Boolean('Send by Post', help='Allows to send the document by snail mail (coventional posting delivery service)', default=lambda self: self.env.user.company_id.invoice_is_snailmail)
    snailmail_cost = fields.Float(string='Credits', compute='_snailmail_estimate', store=True, readonly=True)
    currency_id = fields.Many2one('res.currency', compute='_get_invoice_currency', string="Currency")
    letter_ids = fields.Many2many('snailmail.letter', 'snailmail_letter_account_invoice_send_rel', ondelete='cascade')

    @api.multi
    @api.onchange('invoice_ids')
    def _get_invoice_currency(self):
        for wizard in self:
            if wizard.invoice_ids:
                wizard.currency_id = wizard.invoice_ids.mapped('currency_id')[0].id

    @api.multi
    @api.onchange('invoice_ids')
    def _get_partner(self):
        for wizard in self:
            if wizard.invoice_ids and len(wizard.invoice_ids) == 1:
                wizard.partner_id = wizard.invoice_ids.partner_id.id

    @api.multi
    def unlink(self):
        for agc in self:
            agc.letter_ids.filtered(lambda l: l.state == 'draft').unlink()
        return super(AccountInvoiceSend, self).unlink()

    @api.multi
    def _fetch_letters(self):
        self.ensure_one()
        if not self.letter_ids:

            letters = self.env['snailmail.letter']
            for invoice in self.invoice_ids:
                letter = letters.create({
                    'partner_id': invoice.partner_id.id,
                    'model': 'account.invoice',
                    'res_id': invoice.id,
                    'user_id': self.env.user.id,
                    'company_id': invoice.company_id.id,
                    'report_template': self.env.ref('account.account_invoices').id
                })
                letters |= letter
            self.letter_ids = [(4, l.id) for l in letters]
        return self.letter_ids

    @api.onchange('snailmail_is_letter')
    def _snailmail_estimate(self):
        for wizard in self:
            if wizard.snailmail_is_letter:
                letters = wizard._fetch_letters()
                wizard.snailmail_cost = letters._snailmail_estimate()

    @api.multi
    def snailmail_print_action(self):
        for wizard in self:
            letters = wizard._fetch_letters()
            letters.write({'state': 'pending'})
            if len(letters) == 1:
                letters._snailmail_print()

    @api.multi
    def send_and_print_action(self):
        res = super(AccountInvoiceSend, self).send_and_print_action()
        if self.snailmail_is_letter:
            self.snailmail_print_action()
        return res
<|endoftext|>


<commit_before>import os
import time
from multiprocessing import Pool
from django.conf import settings
import requests
import logging
import json

log=logging.getLogger(__name__)

from django.core.management.base import NoArgsCommand

log_files = [ f for f in os.listdir(settings.LOG_READ_DIRECTORY) if os.path.isfile(os.path.join(settings.LOG_READ_DIRECTORY,f)) ]

class Command(NoArgsCommand):
    """
    Run registered cron jobs
    """

    def handle_noargs(self, **options):
        length = len(log_files)
        p = Pool(processes=length)
        p.map_async(handle_single_log_file,[os.path.join(settings.LOG_READ_DIRECTORY,log_files[i]) for i in xrange(0,length)]).get(9999999)

def _http_post(session, url, data, timeout):
    '''
    Contact grading controller, but fail gently.
    Takes following arguments:
    session - requests.session object
    url - url to post to
    data - dictionary with data to post
    timeout - timeout in settings

    Returns (success, msg), where:
        success: Flag indicating successful exchange (Boolean)
        msg: Accompanying message; Controller reply when successful (string)
    '''

    try:
        r = session.post(url, data=data, timeout=timeout, verify=False)
    except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
        log.error('Could not connect to server at %s in timeout=%f' % (url, timeout))
        return (False, 'Cannot connect to server.')

    if r.status_code not in [200]:
        log.error('Server %s returned status_code=%d' % (url, r.status_code))
        return (False, 'Unexpected HTTP status code [%d]' % r.status_code)
    return (True, r.text)

def handle_single_log_file(args):
    filename = args
    session = requests.session()
    file = open(filename,'r')

    #Find the size of the file and move to the end
    st_results = os.stat(filename)
    st_size = st_results[6]
    file.seek(st_size)

    while True:
        where = file.tell()
        line = file.readline()
        if not line:
            time.sleep(1)
            file.seek(where)
        else:
            log.debug("Posting {0}".format(line))
            json_dict= json.dumps({'message' : line})
            _http_post(session,settings.LOG_POST_URL,line,10)

<commit_msg>Make event log streaming work<commit_after>import os
import time
from multiprocessing import Pool
from django.conf import settings
import requests
import logging
import json

log=logging.getLogger(__name__)

from django.core.management.base import NoArgsCommand

log_files = [ f for f in os.listdir(settings.LOG_READ_DIRECTORY) if os.path.isfile(os.path.join(settings.LOG_READ_DIRECTORY,f)) ]

class Command(NoArgsCommand):
    """
    Run registered cron jobs
    """

    def handle_noargs(self, **options):
        length = len(log_files)
        p = Pool(processes=length)
        p.map_async(handle_single_log_file,[os.path.join(settings.LOG_READ_DIRECTORY,log_files[i]) for i in xrange(0,length)]).get(9999999)

def _http_post(session, url, data, timeout):
    '''
    Contact grading controller, but fail gently.
    Takes following arguments:
    session - requests.session object
    url - url to post to
    data - dictionary with data to post
    timeout - timeout in settings

    Returns (success, msg), where:
        success: Flag indicating successful exchange (Boolean)
        msg: Accompanying message; Controller reply when successful (string)
    '''

    try:
        r = session.post(url, data=data, timeout=timeout, verify=False)
    except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
        log.error('Could not connect to server at %s in timeout=%f' % (url, timeout))
        return (False, 'Cannot connect to server.')

    if r.status_code not in [200]:
        log.error('Server %s returned status_code=%d' % (url, r.status_code))
        return (False, 'Unexpected HTTP status code [%d]' % r.status_code)
    return (True, r.text)

def _http_get(session, url, data={}):
    """
    Send an HTTP get request:
    session: requests.session object.
    url : url to send request to
    data: optional dictionary to send
    """
    try:
        r = session.get(url, params=data)
    except requests.exceptions.ConnectionError, err:
        log.error(err)
        return (False, 'Cannot connect to server.')

    if r.status_code not in [200]:
        return (False, 'Unexpected HTTP status code [%d]' % r.status_code)
    return r.text

def handle_single_log_file(args):
    filename = args
    session = requests.session()
    file = open(filename,'r')

    #Find the size of the file and move to the end
    st_results = os.stat(filename)
    st_size = st_results[6]
    file.seek(st_size)

    while True:
        where = file.tell()
        line = file.readline()
        if not line:
            time.sleep(1)
            file.seek(where)
        else:
            log.debug("Posting {0}".format(line))
            json_dict= {'message' : json.dumps(line)}
            response_text = _http_get(session,settings.LOG_POST_URL,json_dict)

<|endoftext|>


<commit_before>#!/usr/bin/env python
# Created by: Lee Bergstrand
# Descript: A program that extracts the protein annotations from a fasta file and searches these
#           annotations using HMMsearch and an HMM file. It then stores hits along with organism
#           information (gathered from a csv file) in a sqlite3 database.
#
# Requirements: - This script requires the Biopython module: http://biopython.org/wiki/Download
#               - This script requires HMMER 3.1 or later.
#               - This script requires sqlLite3 or later.
#
# Usage: HMMExtract.py <organism.faa> <organisms.csv> <hmm.hmm> <sqldb.sqlite>
# Example: HMMExtract.py ecoli.faa bacteria.csv helicase.hmm helicasedb.sqlite
#----------------------------------------------------------------------------------------
#===========================================================================================================
#Imports & Setup:

import sys
import subprocess
import sqlite3
import csv
import re
import hashlib
from os import path
from Bio import SeqIO
from multiprocessing import cpu_count

import time # Dev. Import

processors = cpu_count() # Gets number of processor cores for HMMER.

# Regex's
LocusRegex = re.compile("\(Locus:\s\S*\)")
LocationRegex = re.compile("\(Location:\s\[(\S*)\:(\S*)\]\((\S)\)\)")
#===========================================================================================================
# Functions:

# 1: Checks if in proper number of arguments are passed gives instructions on proper use.
def argsCheck(numArgs):
	if len(sys.argv) < numArgs or len(sys.argv) > numArgs:
		print "Hmmer hit cataloguer."
		print "By Lee Bergstrand\n"
		print "Usage: " + sys.argv[0] + "  <organism.faa> <organisms.csv> <hmm.hmm> <sqldb.sqlite>"
		print "Examples: " + sys.argv[0] + "  <organism.faa> <organisms.csv> <hmm.hmm> <sqldb.sqlite>"
		sys.exit(1) # Aborts program. (exit(1) indicates that an error occurred)
#------------------------------------------------------------------------------------------------------------
# 2: Runs HMMER with settings specific for extracting subject sequences.
def runHMMSearch(FASTA, HMMERDBFile):
	Found16S = True
	process = subprocess.Popen(["hmmsearch", "--acc", "--cpu", str(processors), HMMERDBFile, "-"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, bufsize = 1)
	stdout, error = process.communicate(FASTA) # This returns a list with both stderr and stdout. Only want stdout which is first element.
	if error:
		print error
		sys.exit(1)
	else:
		return stdout
#------------------------------------------------------------------------------------------------------------
# 3: Creates a python dictionary (hash table) that contains the the fasta for each protien in the proteome.
def createProteomeHash(ProteomeFile):
	ProteomeHash = dict()
	try:
		handle = open(ProteomeFile, "rU")
		proteome = SeqIO.parse(handle, "fasta")
		for record in proteome:
			ProteomeHash.update({ record.id : record.format("fasta") })
		handle.close()
	except IOError:
		print "Failed to open " + ProteomeFile
		exit(1)
	return ProteomeHash
#------------------------------------------------------------------------------------------------------------
# 4: Parses HMM searches text output and generates a two dementional array of the domain alignments results.
def parseHmmsearchResults(HMMResults, HMMName, HMMLength):
	HitRowRegex = re.compile("^\s*\d\s*((\?)|(\!))\s*")
	HMMResults = HMMResults.split(">>") # Splits output at domain alignments.
	del HMMResults[0] # Deletes stuff at top of text output which would be the first element after the split.
	HMMResults = [x.split("Alignments")[0] for x in HMMResults] # Removes detailed per alignment info.
	HMMResultsCleaned = []
	for proteinResult in HMMResults:
		proteinResult = proteinResult.splitlines()
		TargetProtein = proteinResult[0].split()[0] # Records protein accession from first line
		for row in proteinResult:
			if HitRowRegex.match(row): # If row is a domain table line.
				row = row.split()
				score   = float(row[2])
				evalue  = float(row[5])
				hmmfrom = int(row[6])
				hmmto   = int(row[7])
				alifrom = int(row[9])
				alito   = int(row[10])
				hmmCoverage = ((float((hmmto - hmmfrom)))/float((HMMLength)))
				DomainRow = [TargetProtein, HMMName, score, evalue, hmmfrom, hmmto, alifrom, alito, hmmCoverage]
				HMMResultsCleaned.append(DomainRow)
	return HMMResultsCleaned
#------------------------------------------------------------------------------------------------------------
# 6: Fitres HMM Hits.
def filterHMMHitTable(HMMHitTable):

	HMMHitTable = [row for row in HMMHitTable if row[3] < float("1e-30")] # Filtres by E-value.

	i = 0
	while i < (len(HMMHitTable)-1):
		RowOne = HMMHitTable[i]    # Current Row in hit table.
		RowTwo = HMMHitTable[i+1]  # Row below.
		if(RowOne[0] == RowTwo[0]): # If they have the same targe protein.
			AlignmentOneLength = RowOne[-2] - RowOne[-3] # RowOne AliTo - AliFrom
			AlignmentTwoLength = RowTwo[-2] - RowTwo[-3] # RowTwo AliTo - AliFrom
			Overlap = RowOne[-2] - RowTwo[-3]  # RowOne AliTo -  RowTwo AliFrom
			if (Overlap > 0): # If there is overlap...
				# If the overlap is greater than 50% of either alignment.
				if((((float(Overlap)/float(AlignmentOneLength)) > 0.5) or ((float(Overlap)/float(AlignmentTwoLength)) > 0.5))):
					if RowOne[3] < RowTwo[3]: # If row one has a lower E-value than row two remove row two else remove row one.
						HMMHitTable.remove(RowTwo)
					else:
						HMMHitTable.remove(RowOne)
					i = i - 1 # Resets list index.
		i += 1

	HMMHitTable = [row for row in HMMHitTable if row[-1] > 0.3] # Filtres by Query Coverage.
	return	HMMHitTable
#------------------------------------------------------------------------------------------------------------
# 6: Creates list of hits protien FASTAs.
def getHitProteins(HMMHitTable, AnnotationFASTADict, OrganismName):
	HitProteins = []
	for row in HMMHitTable:
		ProteinAccession = row[0]
		ProteinFASTA = AnnotationFASTADict[ProteinAccession]

		Locus = LocusRegex.search(ProteinFASTA).group(0)
		Locus = Locus.split()[1].rstrip(")")
	try:
		LocationData = LocationRegex.search(ProteinFASTA)
		Start  = LocationData.group(1)
		End    = LocationData.group(2)
		Strand = LocationData.group(3)
		ProteinData = [ProteinAccession, OrganismName, Locus, Start, End, Strand, ProteinFASTA]
		HitProteins.append(ProteinData)
	except AttributeError as Error:
		print LocationData
		print "This is the organism: ", OrganismName
		print "The AttributeError was ", str(Error)
		sys.exit(1)
	return HitProteins
#-----------------------------------------------------------------------------------------------------------
# 7: Inserts organism info into DB.
def insertOrganismInfo(cursor, OrganismInfo):
	cursor.execute('''INSERT OR REPLACE INTO Organisms(Organism_Accession, Accession_Type, Organism_Description, Source, Organism_Phylogeny)
			          VALUES(?,?,?,?,?)''', OrganismInfo)
#-----------------------------------------------------------------------------------------------------------
# 8: Inserts protein info into DB.
def insertProteins(cursor, HitProteins):
	for protein in HitProteins:
		cursor.execute('''INSERT OR REPLACE INTO Proteins(Protein_Accession,Organism_Accession,Locus,Start,End,Strand,FASTA_Sequence)
		                  VALUES(?,?,?,?,?,?,?)''', protein)
#-----------------------------------------------------------------------------------------------------------
# 9: Inserts hits into DB and creates md5 hash for primary key.
def insertHits(cursor, HMMHitTable):
	HitHash = hashlib.md5()
	for hit in HMMHitTable:
		HitHash.update("".join([str(i) for i in HMMHitTable])) # Converts every list element to a string, joins them and updates Hash.
		hit = [HitHash.hexdigest()] + hit
		cursor.execute('''INSERT OR REPLACE INTO HMM_Hits(Hit_HASH, Protein_Accession, HMM_Model, HMM_Score, HMM_E_Value, Ali_From, Ali_To, HMM_From, HMM_To, HMM_Coverage)
			              VALUES(?,?,?,?,?,?,?,?,?,?)''', hit)
#===========================================================================================================
# Main program code:

# House keeping...
argsCheck(5) # Checks if the number of arguments are correct.

# Stores file one for input checking.
print ">> Starting up..."
OrganismFile = sys.argv[1]
PhyloCSVFile = sys.argv[2]
HMMFile      = sys.argv[3]
sqlFile      = sys.argv[4]

HMMName = path.split(HMMFile)[1].rstrip(".hmm")
OrganismName = path.split(OrganismFile)[1].rstrip(".faa")

# File extension checks
print ">> Performing file extention checks..."
if not OrganismFile.endswith(".faa"):
	print "[Warning] " + OrganismFile + " may not be a fasta file!"
if not PhyloCSVFile.endswith(".csv"):
	print "[Warning] " + PhyloCSVFile + " may not be a csv file!"
if not HMMFile.endswith(".hmm"):
	print "[Warning] " + HMMFile + " may not be a HMM file!"
if not sqlFile.endswith(".sqlite"):
	print "[Warning] " + sqlFile + " may not be a sqlite file!"

# Read in genbank file as a sequence record object.
try:
	print ">> Opening FASTA File: " + OrganismFile
	handle = open(OrganismFile, "rU")
	records = SeqIO.parse(handle, "fasta")
	handle.close()
except IOError:
	print "Failed to open " + OrganismFile
	sys.exit(1)

# Opens OrganismDB CSV file for reading and stores as hash table.
OrganismHash = {}
try:
	print ">> Opening CSV File: " + PhyloCSVFile
	readFile = open(PhyloCSVFile, "r")
	reader  = csv.reader(readFile) # Opens file with csv module which takes into account verying csv formats and parses correctly.
	print ">> Good CSV file."
	for row in reader:
		OrganismHash[row[0]] = row
	readFile.close()
except IOError:
	print "Failed to open " + PhyloCSVFile
	sys.exit(1)

# Gets HMM Length.
try:
	HMMLengthRegex = re.compile("^LENG\s*\d*$")
	print ">> Opening HMM File: " + HMMFile
	with open(HMMFile, "rU") as inFile:
		currentLine = inFile.readline()
		while not HMMLengthRegex.match(currentLine):
			currentLine = inFile.readline()
		HMMLength = int(currentLine.split()[1])
except IOError:
	print "Failed to open " + HMMFile
	sys.exit(1)

print ">> Extracting Protein Annotations..."
AnnotationFASTADict = createProteomeHash(OrganismFile) # Creates a dictionary containing all protein annotations in the gbk file.

print ">> Extracting Organism Info..."
OrganismInfo = OrganismHash[OrganismName]

print ">> Running Hmmsearch..."
FASTAString = "".join(AnnotationFASTADict.values()) # Saves these annotations to a string.
HMMResults  = runHMMSearch(FASTAString, HMMFile) # Runs hmmsearch.

if (len(HMMResults) < 1):
	print "Hmmearch found no hits for " + HMMFile + " in " + OrganismName
	print "Aborting..."
	sys.exit(1)

print ">> Parsing and filtering hmmsearch results..."
HMMHitTable = parseHmmsearchResults(HMMResults, HMMName, HMMLength) # Parses hmmsearch results into a two dimensional array.
HMMHitTable = filterHMMHitTable(HMMHitTable)

if (len(HMMHitTable) < 1):
	print "We found no quality hits for " + HMMFile + " in " + OrganismName
	print "Aborting..."
	sys.exit(1)

print ">> Extracting Hit Proteins."
HitProteins = getHitProteins(HMMHitTable, AnnotationFASTADict, OrganismName) # Gets hit protein FASTAs.

if path.isfile(sqlFile):
	try:
		HMMDB = sqlite3.connect(sqlFile, timeout = 60)
		print ">> Opened database successfully."
		cursor = HMMDB.cursor()

		print ">> Inserting Organism Info."
		insertOrganismInfo(cursor, OrganismInfo)
		print ">> Inserting Proteins."
		insertProteins(cursor, HitProteins)
		print ">> Inserting Hits."
		insertHits(cursor, HMMHitTable)

		HMMDB.commit()
		HMMDB.close()
	except sqlite3.Error as Error:
		print "sqlite3 Error: " + str(Error)
		print "The program will be aborted."
		sys.exit(1)
else:
	print "Failed to open " + sqlFile
	sys.exit(1)
print ">> Done!"<commit_msg>Debugging updates.<commit_after>#!/usr/bin/env python
# Created by: Lee Bergstrand
# Descript: A program that extracts the protein annotations from a fasta file and searches these
#           annotations using HMMsearch and an HMM file. It then stores hits along with organism
#           information (gathered from a csv file) in a sqlite3 database.
#
# Requirements: - This script requires the Biopython module: http://biopython.org/wiki/Download
#               - This script requires HMMER 3.1 or later.
#               - This script requires sqlLite3 or later.
#
# Usage: HMMExtract.py <organism.faa> <organisms.csv> <hmm.hmm> <sqldb.sqlite>
# Example: HMMExtract.py ecoli.faa bacteria.csv helicase.hmm helicasedb.sqlite
#----------------------------------------------------------------------------------------
#===========================================================================================================
#Imports & Setup:

import sys
import subprocess
import sqlite3
import csv
import re
import hashlib
from os import path
from Bio import SeqIO
from multiprocessing import cpu_count

import time # Dev. Import

processors = cpu_count() # Gets number of processor cores for HMMER.

# Regex's
LocusRegex = re.compile("\(Locus:\s\S*\)")
LocationRegex = re.compile("\(Location:\s\[(\S*)\:(\S*)\]\((\S)\)\)")
#===========================================================================================================
# Functions:

# 1: Checks if in proper number of arguments are passed gives instructions on proper use.
def argsCheck(numArgs):
	if len(sys.argv) < numArgs or len(sys.argv) > numArgs:
		print "Hmmer hit cataloguer."
		print "By Lee Bergstrand\n"
		print "Usage: " + sys.argv[0] + "  <organism.faa> <organisms.csv> <hmm.hmm> <sqldb.sqlite>"
		print "Examples: " + sys.argv[0] + "  <organism.faa> <organisms.csv> <hmm.hmm> <sqldb.sqlite>"
		sys.exit(1) # Aborts program. (exit(1) indicates that an error occurred)
#------------------------------------------------------------------------------------------------------------
# 2: Runs HMMER with settings specific for extracting subject sequences.
def runHMMSearch(FASTA, HMMERDBFile):
	Found16S = True
	process = subprocess.Popen(["hmmsearch", "--acc", "--cpu", str(processors), HMMERDBFile, "-"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, bufsize = 1)
	stdout, error = process.communicate(FASTA) # This returns a list with both stderr and stdout. Only want stdout which is first element.
	if error:
		print error
		sys.exit(1)
	else:
		return stdout
#------------------------------------------------------------------------------------------------------------
# 3: Creates a python dictionary (hash table) that contains the the fasta for each protien in the proteome.
def createProteomeHash(ProteomeFile):
	ProteomeHash = dict()
	try:
		handle = open(ProteomeFile, "rU")
		proteome = SeqIO.parse(handle, "fasta")
		for record in proteome:
			ProteomeHash.update({ record.id : record.format("fasta") })
		handle.close()
	except IOError:
		print "Failed to open " + ProteomeFile
		exit(1)
	return ProteomeHash
#------------------------------------------------------------------------------------------------------------
# 4: Parses HMM searches text output and generates a two dementional array of the domain alignments results.
def parseHmmsearchResults(HMMResults, HMMName, HMMLength):
	HitRowRegex = re.compile("^\s*\d\s*((\?)|(\!))\s*")
	HMMResults = HMMResults.split(">>") # Splits output at domain alignments.
	del HMMResults[0] # Deletes stuff at top of text output which would be the first element after the split.
	HMMResults = [x.split("Alignments")[0] for x in HMMResults] # Removes detailed per alignment info.
	HMMResultsCleaned = []
	for proteinResult in HMMResults:
		proteinResult = proteinResult.splitlines()
		TargetProtein = proteinResult[0].split()[0] # Records protein accession from first line
		for row in proteinResult:
			if HitRowRegex.match(row): # If row is a domain table line.
				row = row.split()
				score   = float(row[2])
				evalue  = float(row[5])
				hmmfrom = int(row[6])
				hmmto   = int(row[7])
				alifrom = int(row[9])
				alito   = int(row[10])
				hmmCoverage = ((float((hmmto - hmmfrom)))/float((HMMLength)))
				DomainRow = [TargetProtein, HMMName, score, evalue, hmmfrom, hmmto, alifrom, alito, hmmCoverage]
				HMMResultsCleaned.append(DomainRow)
	return HMMResultsCleaned
#------------------------------------------------------------------------------------------------------------
# 6: Fitres HMM Hits.
def filterHMMHitTable(HMMHitTable):

	HMMHitTable = [row for row in HMMHitTable if row[3] < float("1e-30")] # Filtres by E-value.

	i = 0
	while i < (len(HMMHitTable)-1):
		RowOne = HMMHitTable[i]    # Current Row in hit table.
		RowTwo = HMMHitTable[i+1]  # Row below.
		if(RowOne[0] == RowTwo[0]): # If they have the same targe protein.
			AlignmentOneLength = RowOne[-2] - RowOne[-3] # RowOne AliTo - AliFrom
			AlignmentTwoLength = RowTwo[-2] - RowTwo[-3] # RowTwo AliTo - AliFrom
			Overlap = RowOne[-2] - RowTwo[-3]  # RowOne AliTo -  RowTwo AliFrom
			if (Overlap > 0): # If there is overlap...
				# If the overlap is greater than 50% of either alignment.
				if((((float(Overlap)/float(AlignmentOneLength)) > 0.5) or ((float(Overlap)/float(AlignmentTwoLength)) > 0.5))):
					if RowOne[3] < RowTwo[3]: # If row one has a lower E-value than row two remove row two else remove row one.
						HMMHitTable.remove(RowTwo)
					else:
						HMMHitTable.remove(RowOne)
					i = i - 1 # Resets list index.
		i += 1

	HMMHitTable = [row for row in HMMHitTable if row[-1] > 0.3] # Filtres by Query Coverage.
	return	HMMHitTable
#------------------------------------------------------------------------------------------------------------
# 6: Creates list of hits protien FASTAs.
def getHitProteins(HMMHitTable, AnnotationFASTADict, OrganismName):
	HitProteins = []
	for row in HMMHitTable:
		ProteinAccession = row[0]
		ProteinFASTA = AnnotationFASTADict[ProteinAccession]

		Locus = LocusRegex.search(ProteinFASTA).group(0)
		Locus = Locus.split()[1].rstrip(")")
	try:
		LocationData = LocationRegex.search(ProteinFASTA)
		Start  = LocationData.group(1)
		End    = LocationData.group(2)
		Strand = LocationData.group(3)
		ProteinData = [ProteinAccession, OrganismName, Locus, Start, End, Strand, ProteinFASTA]
		HitProteins.append(ProteinData)
	except AttributeError as Error:
		print row
		print ProteinFASTA
		print LocationData
		print "This is the organism: ", OrganismName
		print "The AttributeError was ", str(Error)
		sys.exit(1)
	return HitProteins.
#-----------------------------------------------------------------------------------------------------------
# 7: Inserts organism info into DB.
def insertOrganismInfo(cursor, OrganismInfo):
	cursor.execute('''INSERT OR REPLACE INTO Organisms(Organism_Accession, Accession_Type, Organism_Description, Source, Organism_Phylogeny)
			          VALUES(?,?,?,?,?)''', OrganismInfo)
#-----------------------------------------------------------------------------------------------------------
# 8: Inserts protein info into DB.
def insertProteins(cursor, HitProteins):
	for protein in HitProteins:
		cursor.execute('''INSERT OR REPLACE INTO Proteins(Protein_Accession,Organism_Accession,Locus,Start,End,Strand,FASTA_Sequence)
		                  VALUES(?,?,?,?,?,?,?)''', protein)
#-----------------------------------------------------------------------------------------------------------
# 9: Inserts hits into DB and creates md5 hash for primary key.
def insertHits(cursor, HMMHitTable):
	HitHash = hashlib.md5()
	for hit in HMMHitTable:
		HitHash.update("".join([str(i) for i in HMMHitTable])) # Converts every list element to a string, joins them and updates Hash.
		hit = [HitHash.hexdigest()] + hit
		cursor.execute('''INSERT OR REPLACE INTO HMM_Hits(Hit_HASH, Protein_Accession, HMM_Model, HMM_Score, HMM_E_Value, Ali_From, Ali_To, HMM_From, HMM_To, HMM_Coverage)
			              VALUES(?,?,?,?,?,?,?,?,?,?)''', hit)
#===========================================================================================================
# Main program code:

# House keeping...
argsCheck(5) # Checks if the number of arguments are correct.

# Stores file one for input checking.
print ">> Starting up..."
OrganismFile = sys.argv[1]
PhyloCSVFile = sys.argv[2]
HMMFile      = sys.argv[3]
sqlFile      = sys.argv[4]

HMMName = path.split(HMMFile)[1].rstrip(".hmm")
OrganismName = path.split(OrganismFile)[1].rstrip(".faa")

# File extension checks
print ">> Performing file extention checks..."
if not OrganismFile.endswith(".faa"):
	print "[Warning] " + OrganismFile + " may not be a fasta file!"
if not PhyloCSVFile.endswith(".csv"):
	print "[Warning] " + PhyloCSVFile + " may not be a csv file!"
if not HMMFile.endswith(".hmm"):
	print "[Warning] " + HMMFile + " may not be a HMM file!"
if not sqlFile.endswith(".sqlite"):
	print "[Warning] " + sqlFile + " may not be a sqlite file!"

# Read in genbank file as a sequence record object.
try:
	print ">> Opening FASTA File: " + OrganismFile
	handle = open(OrganismFile, "rU")
	records = SeqIO.parse(handle, "fasta")
	handle.close()
except IOError:
	print "Failed to open " + OrganismFile
	sys.exit(1)

# Opens OrganismDB CSV file for reading and stores as hash table.
OrganismHash = {}
try:
	print ">> Opening CSV File: " + PhyloCSVFile
	readFile = open(PhyloCSVFile, "r")
	reader  = csv.reader(readFile) # Opens file with csv module which takes into account verying csv formats and parses correctly.
	print ">> Good CSV file."
	for row in reader:
		OrganismHash[row[0]] = row
	readFile.close()
except IOError:
	print "Failed to open " + PhyloCSVFile
	sys.exit(1)

# Gets HMM Length.
try:
	HMMLengthRegex = re.compile("^LENG\s*\d*$")
	print ">> Opening HMM File: " + HMMFile
	with open(HMMFile, "rU") as inFile:
		currentLine = inFile.readline()
		while not HMMLengthRegex.match(currentLine):
			currentLine = inFile.readline()
		HMMLength = int(currentLine.split()[1])
except IOError:
	print "Failed to open " + HMMFile
	sys.exit(1)

print ">> Extracting Protein Annotations..."
AnnotationFASTADict = createProteomeHash(OrganismFile) # Creates a dictionary containing all protein annotations in the gbk file.

print ">> Extracting Organism Info..."
OrganismInfo = OrganismHash[OrganismName]

print ">> Running Hmmsearch..."
FASTAString = "".join(AnnotationFASTADict.values()) # Saves these annotations to a string.
HMMResults  = runHMMSearch(FASTAString, HMMFile) # Runs hmmsearch.

if (len(HMMResults) < 1):
	print "Hmmearch found no hits for " + HMMFile + " in " + OrganismName
	print "Aborting..."
	sys.exit(1)

print ">> Parsing and filtering hmmsearch results..."
HMMHitTable = parseHmmsearchResults(HMMResults, HMMName, HMMLength) # Parses hmmsearch results into a two dimensional array.
HMMHitTable = filterHMMHitTable(HMMHitTable)

if (len(HMMHitTable) < 1):
	print "We found no quality hits for " + HMMFile + " in " + OrganismName
	print "Aborting..."
	sys.exit(1)

print ">> Extracting Hit Proteins."
HitProteins = getHitProteins(HMMHitTable, AnnotationFASTADict, OrganismName) # Gets hit protein FASTAs.

if path.isfile(sqlFile):
	try:
		HMMDB = sqlite3.connect(sqlFile, timeout = 60)
		print ">> Opened database successfully."
		cursor = HMMDB.cursor()

		print ">> Inserting Organism Info."
		insertOrganismInfo(cursor, OrganismInfo)
		print ">> Inserting Proteins."
		insertProteins(cursor, HitProteins)
		print ">> Inserting Hits."
		insertHits(cursor, HMMHitTable)

		HMMDB.commit()
		HMMDB.close()
	except sqlite3.Error as Error:
		print "sqlite3 Error: " + str(Error)
		print "The program will be aborted."
		sys.exit(1)
else:
	print "Failed to open " + sqlFile
	sys.exit(1)
print ">> Done!"<|endoftext|>