<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
# Author: <NAME>
# Copyright 2015, TODAY Clouder SASU
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License with Attribution
# clause 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License with
# Attribution clause along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp import models, fields, api, _
from openerp.exceptions import except_orm
from dateutil.relativedelta import relativedelta
import logging

_logger = logging.getLogger(__name__)


class ClouderInvoicingPricegridLine(models.Model):
    """
    Defines a pricegrid line
    """
    _name = 'clouder.invoicing.pricegrid.line'

    application_metadata = fields.Many2one('clouder.application.metadata', 'Invoicing Unit', required=True)
    threshold = fields.Integer('Threshold', required=True)
    price = fields.Float('Price', required=True)
    type = fields.Selection(
        string="Type",
        selection=[
            ('fixed', 'Fixed Price'),
            ('mult', 'Value Multiplier')
        ],
        required=True
    )
    link_application = fields.Many2one('clouder.application', 'Application')
    link_container = fields.Many2one('clouder.container', 'Container')
    link_base = fields.Many2one('clouder.base', 'Base')

    @api.one
    @api.constrains('link_application', 'link_container', 'link_base')
    def _check_links(self):
        """
        Checks that at least one - and only one - of the three links is defined
        """
        # At least one should be defined
        if not self.link:
            raise except_orm(
                _('Pricegrid error!'),
                _("You cannot define a pricegrid line without linking it to a base or container or application.")
            )
        # No more than one should be defined
        if (self.link_base and self.link_application or
                self.link_base and self.link_container or
                self.link_application and self.link_container):
            raise except_orm(
                _('Pricegrid error!'),
                _("Pricegrid links to application/container/base are exclusive to one another.")
            )

    @property
    def link(self):
        """
        Returns the link defined
        """
        return self.link_application or self.link_container or self.link_base

    @property
    def link_type(self):
        """
        Returns a string that gives the type of the link
            Example: "clouder.base"
        """
        return self.link._name

    @property
    def invoicing_unit(self):
        """
        Returns the invoicing unit of a pricegrid line
        """
        class_to_search = self.link_type + ".metadata"
        class_link = self.link_type.split('.')[-1] + "_id"

        # Search for the metadata
        metadata = self.env[class_to_search].search([
            (class_link, '=', self.link.id),
            ('name', '=', self.application_metadata.id)
        ])
        if not metadata:
            raise except_orm(
                _('Pricegrid invoicing_unit error!'),
                _("No linked metadata found for {0} '{1}'".format(self.link_type, self.link.name))
            )

        return metadata[0].value

    @api.multi
    def invoice_amount(self):
        """
        Given pricegrid lines for a single container/base: computes the amount to invoice
        """
        # Check that all lines are linked to the same object
        linked_recs = []
        for pgl in self:
            if pgl.link not in linked_recs:
                linked_recs.append(pgl.link)
                if len(linked_recs) > 1:
                    raise except_orm(
                        _('Pricegrid invoice_amount error!'),
                        _("This function should only be called from recordsets linked to the same container/base.")
                    )
        # Grouping lines by invoicing unit
        invoicing_data = {}
        for pgl in self:
            if pgl.application_metadata.id not in invoicing_data:
                invoicing_data[pgl.application_metadata.id] = {}
            if pgl.type not in invoicing_data[pgl.application_metadata.id]:
                invoicing_data[pgl.application_metadata.id][pgl.type] = []
            invoicing_data[pgl.application_metadata.id][pgl.type].append(pgl)

        # Sorting resulting lists by threshold
        for k, v in invoicing_data.iteritems():
            for table in v:
                v[table].sort(key=lambda x: x.threshold)

        # Computing final value*
        amount = 0.0
        for tables in invoicing_data.values():
            for lines in tables:
                compare_unit = lines[0].invoicing_unit
                index = 0

                # No amount to add if the first threshold is above current compare value
                if lines[index].threshold > compare_unit:
                    continue

                # Searching for the right line
                while (index+1) < len(lines) and compare_unit <= lines[index+1].threshold:
                    index += 1

                # Computing and adding price
                if lines[index].type == 'fixed':
                    amount += lines[index].price
                elif lines[index].type == 'mult':
                    amount += lines[index].price * compare_unit
                else:
                    # Preventing possible future type errors
                    raise except_orm(
                        _('Pricegrid invoice_amount error!'),
                        _(
                            "Unknown type '{0}' in pricegrid line for {1} '{2}'.".format(
                                lines[index].type,
                                lines[index].link_type,
                                lines[index].link.name
                            )
                        )
                    )
        return amount


class ClouderApplication(models.Model):
    """
    Defines invoicing settings for an application
    """
    _inherit = 'clouder.application'

    pricegrid_ids = fields.One2many(
        'clouder.invoicing.pricegrid.line',<commit_msg>Fixed bad condition for selecting pricegrid line. Fixed a bug with a for loop<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
# Author: <NAME>
# Copyright 2015, TODAY Clouder SASU
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License with Attribution
# clause 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License with
# Attribution clause along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp import models, fields, api, _
from openerp.exceptions import except_orm
from dateutil.relativedelta import relativedelta
import logging

_logger = logging.getLogger(__name__)


class ClouderInvoicingPricegridLine(models.Model):
    """
    Defines a pricegrid line
    """
    _name = 'clouder.invoicing.pricegrid.line'

    application_metadata = fields.Many2one('clouder.application.metadata', 'Invoicing Unit', required=True)
    threshold = fields.Integer('Threshold', required=True)
    price = fields.Float('Price', required=True)
    type = fields.Selection(
        string="Type",
        selection=[
            ('fixed', 'Fixed Price'),
            ('mult', 'Value Multiplier')
        ],
        required=True
    )
    link_application = fields.Many2one('clouder.application', 'Application')
    link_container = fields.Many2one('clouder.container', 'Container')
    link_base = fields.Many2one('clouder.base', 'Base')

    @api.one
    @api.constrains('link_application', 'link_container', 'link_base')
    def _check_links(self):
        """
        Checks that at least one - and only one - of the three links is defined
        """
        # At least one should be defined
        if not self.link:
            raise except_orm(
                _('Pricegrid error!'),
                _("You cannot define a pricegrid line without linking it to a base or container or application.")
            )
        # No more than one should be defined
        if (self.link_base and self.link_application or
                self.link_base and self.link_container or
                self.link_application and self.link_container):
            raise except_orm(
                _('Pricegrid error!'),
                _("Pricegrid links to application/container/base are exclusive to one another.")
            )

    @property
    def link(self):
        """
        Returns the link defined
        """
        return self.link_application or self.link_container or self.link_base

    @property
    def link_type(self):
        """
        Returns a string that gives the type of the link
            Example: "clouder.base"
        """
        return self.link._name

    @property
    def invoicing_unit(self):
        """
        Returns the invoicing unit of a pricegrid line
        """
        class_to_search = self.link_type + ".metadata"
        class_link = self.link_type.split('.')[-1] + "_id"

        # Search for the metadata
        metadata = self.env[class_to_search].search([
            (class_link, '=', self.link.id),
            ('name', '=', self.application_metadata.id)
        ])
        if not metadata:
            raise except_orm(
                _('Pricegrid invoicing_unit error!'),
                _("No linked metadata found for {0} '{1}'".format(self.link_type, self.link.name))
            )

        return metadata[0].value

    @api.multi
    def invoice_amount(self):
        """
        Given pricegrid lines for a single container/base: computes the amount to invoice
        """
        # Check that all lines are linked to the same object
        linked_recs = []
        for pgl in self:
            if pgl.link not in linked_recs:
                linked_recs.append(pgl.link)
                if len(linked_recs) > 1:
                    raise except_orm(
                        _('Pricegrid invoice_amount error!'),
                        _("This function should only be called from recordsets linked to the same container/base.")
                    )
        # Grouping lines by invoicing unit
        invoicing_data = {}
        for pgl in self:
            if pgl.application_metadata.id not in invoicing_data:
                invoicing_data[pgl.application_metadata.id] = {}
            if pgl.type not in invoicing_data[pgl.application_metadata.id]:
                invoicing_data[pgl.application_metadata.id][pgl.type] = []
            invoicing_data[pgl.application_metadata.id][pgl.type].append(pgl)

        # Sorting resulting lists by threshold
        for k, v in invoicing_data.iteritems():
            for table in v:
                v[table].sort(key=lambda x: x.threshold)

        # Computing final value*
        amount = 0.0
        for tables in invoicing_data.values():
            for k, lines in tables.iteritems():
                compare_unit = lines[0].invoicing_unit
                index = 0

                # No amount to add if the first threshold is above current compare value
                if lines[index].threshold > compare_unit:
                    continue

                # Searching for the right line
                while (index+1) < len(lines) and lines[index+1].threshold <= compare_unit:
                    index += 1

                # Computing and adding price
                if lines[index].type == 'fixed':
                    amount += lines[index].price
                elif lines[index].type == 'mult':
                    amount += lines[index].price * compare_unit
                else:
                    # Preventing possible future type errors
                    raise except_orm(
                        _('Pricegrid invoice_amount error!'),
                        _(
                            "Unknown type '{0}' in pricegrid line for {1} '{2}'.".format(
                                lines[index].type,
                                lines[index].link_type,
                                lines[index].link.name
                            )
                        )
                    )
        return amount


class ClouderApplication(models.Model):
    """
    Defines invoicing settings for an application
    """
    _inherit = 'clouder.application'

    pricegrid_ids = fields.One2many(
        'clouder.invoicing.pricegrid.line',
{'fraction_non_alphanumeric': 0.10879988197978904, 'fraction_numerical': 0.0038356568562366304, 'mean_word_length': 1.9856859722528077, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5895820', 'n_tokens_mistral': 3562, 'n_tokens_neox': 3368, 'n_words': 1071}