# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import itertools
import json
from odoo import models, fields, api, _
from odoo.addons.sale.models.sale import SaleOrderLine as SOL
from odoo.addons.sale.models.sale import SaleOrder as SO
from odoo.tools import float_compare, float_is_zero, DEFAULT_SERVER_DATE_FORMAT
from odoo.exceptions import UserError
from odoo.models import regex_order
from odoo.addons.of_utils.models.of_utils import get_selection_label

NEGATIVE_TERM_OPERATORS = ('!=', 'not like', 'not ilike', 'not in')

@api.onchange('product_uom', 'product_uom_qty')
def product_uom_change(self):
    u"""Copie de la fonction parente avec retrait de l'affectation du prix unitaire"""
    if not self.product_uom or not self.product_id:
        self.price_unit = 0.0
        return
    if self.order_id.pricelist_id.of_is_quantity_dependent(self.product_id.id, self.order_id.date_order) \
            and self.order_id.partner_id \
            and (not self.price_unit or float_compare(self.price_unit, self.product_id.list_price, 2) != 0):
        self.price_unit = self.of_get_price_unit()

SOL.product_uom_change = product_uom_change

@api.onchange('fiscal_position_id')
def _compute_tax_id(self):
    """
    La fonction est appelée compute mais est en réalité un onchange. Surcharge pour ne pas réaffecter les taxes
    sur des lignes ayant déjà été facturées
    """
    for order in self:
        order.order_line.filtered(lambda l: not l.invoice_lines)._compute_tax_id()

SO._compute_tax_id = _compute_tax_id

class SaleOrder(models.Model):
    _name = 'sale.order'
    _inherit = ['sale.order', 'of.documents.joints']

    def pdf_payment_schedule(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_payment_schedule')

    def pdf_address_contact_parent_name(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_address_contact_parent_name')

    def pdf_address_contact_titles(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_address_contact_titles')

    def pdf_address_contact_name(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_address_contact_name')

    def pdf_address_contact_phone(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_address_contact_phone') or False

    def pdf_address_contact_mobile(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_address_contact_mobile') or False

    def pdf_address_contact_fax(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_address_contact_fax') or False

    def pdf_address_contact_email(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_address_contact_email') or False

    def pdf_technical_visit_insert(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_technical_visit_insert')

    def pdf_validity_insert(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_validity_insert')

    def pdf_address_title(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_address_title')

    def pdf_shipping_address_specific_title(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_shipping_address_specific_title') or False

    def pdf_commercial_insert(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_commercial_insert')

    def pdf_commercial_contact(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_commercial_contact')

    def pdf_commercial_email(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_commercial_email')

    def pdf_customer_insert(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_customer_insert')

    def pdf_customer_phone(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_customer_phone')

    def pdf_customer_mobile(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_customer_mobile')

    def pdf_customer_fax(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_customer_fax')

    def pdf_customer_email(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_customer_email')

    def pdf_payment_term_insert(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_payment_term_insert')

    def pdf_customer_ref_insert(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_customer_ref_insert')

    def pdf_taxes_detail(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_taxes_detail')

    def pdf_signatures_insert(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_signatures_insert')

    def pdf_vendor_signature(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_vendor_signature')

    def pdf_prefill_vendor_signature(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_prefill_vendor_signature')

    def pdf_customer_signature(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_customer_signature')

    def pdf_signature_text(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_signature_text')

    def get_color_section(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_section_bg_color') or '#FFFFFF'

    def get_color_font(self):
        return self.env['ir.values'].get_default('sale.config.settings', 'pdf_section_font_color') or "#000000"

    def _search_of_to_invoice(self, operator, value):
        # Récupération des bons de commande non entièrement livrés
        self._cr.execute("SELECT DISTINCT order_id\n"
                         "FROM sale_order_line\n"
                         "WHERE qty_to_invoice + qty_invoiced < product_uom_qty")
        order_ids = self._cr.fetchall()

        domain = ['&', '&',
                  ('of_force_invoice_status', 'not in', ('invoiced', 'no')),
                  ('state', 'in', ('sale', 'done')),
                  ('order_line.qty_to_invoice', '>', 0)]
        if order_ids:
            domain = ['&'] + domain + [('id', 'not in', zip(*order_ids)[0])]
        return domain

    @api.depends('order_line.price_total')
    def _amount_all(self):
        """Compute the total amounts of the SO."""
        # Le calcul standard diffère du calcul utilisé dans les factures, cela peut mener à des écarts dans certains cas
        # (quand l'option d'arrondi global de la tva est utilisée
        # et que la commande contient plusieurs lignes avec des taxes différentes).
        # On uniformise le calcul du montant des devis/commandes avec celui des factures.
        for order in self:
            order.amount_untaxed = sum(line.price_subtotal for line in order.order_line)
            order.amount_tax = sum(tax['amount'] for tax in order.of_get_taxes_values().itervalues())
            order.amount_total = order.amount_untaxed + order.amount_tax

    of_to_invoice = fields.Boolean(
        u"Entièrement facturable", compute='_compute_of_to_invoice', search='_search_of_to_invoice'
    )
    of_notes_facture = fields.Html(string="Notes facture", oldname="of_notes_factures")
    of_notes_intervention = fields.Html(string="Notes intervention")
    of_notes_client = fields.Text(related='partner_id.comment', string="Notes client", readonly=True)

    of_total_cout = fields.Monetary(compute='_compute_of_marge', string='Prix de revient')
    of_marge_pc = fields.Float(compute='_compute_of_marge', string=u"Marge %", search='_search_of_marge_pc')

    of_etiquette_partenaire_ids = fields.Many2many(
        'res.partner.category', related='partner_id.category_id', string=u"Étiquettes client")
    of_client_view = fields.Boolean(string='Vue client/vendeur')

    of_date_vt = fields.Date(
        string="Date visite technique", help=u"Si renseignée apparaîtra sur le devis / Bon de commande"
    )
    of_echeance_line_ids = fields.One2many('of.sale.echeance', 'order_id', string=u"Échéances")

    of_echeances_modified = fields.Boolean(
        u"Les échéances ont besoin d'être recalculées", compute="_compute_of_echeances_modified")
    of_force_invoice_status = fields.Selection([
        ('invoiced', 'Fully Invoiced'),
        ('no', 'Nothing to Invoice')], string=u"Forcer état de facturation",
        help=u"Permet de forcer l'état de facturation de la commande.\n"
             u"Utile pour les commandes facturées qui refusent de changer d'état "
             u"(e.g. une ligne a été supprimée dans la facture).", copy=False
    )
    of_invoice_policy = fields.Selection(
        [('order', u'Quantités commandées'), ('delivery', u'Quantités livrées')], string="Politique de facturation"
    )
    of_fixed_invoice_date = fields.Date(string="Date de facturation fixe")
    of_invoice_date_prev = fields.Date(
        string=u"Date de facturation prévisonnelle", compute="_compute_of_invoice_date_prev",
        inverse="_inverse_of_invoice_date_prev", store=True, compute_sudo=True)
    of_delivered = fields.Boolean(string=u"Livrée", compute="_compute_delivered", store=True)
    of_allow_quote_addition = fields.Boolean(
        string=u"Permet l'ajout de devis complémentaires", compute='_compute_of_allow_quote_addition')
    of_price_printing = fields.Selection([
        ('order_line', u'Prix par ligne de commande'),
    ], string=u"Impressions des prix", default='order_line', required=True)
    of_apply_on_invoice = fields.Boolean(string=u"Appliquer aux factures", default=True)
    of_partner_phone = fields.Char(related='partner_id.phone', string=u"Téléphone du partenaire", readonly=True)
    of_partner_mobile = fields.Char(related='partner_id.mobile', string=u"Mobile du partenaire", readonly=True)
    of_partner_email = fields.Char(related='partner_id.email', string=u"Courriel du partenaire", readonly=True)

    @api.multi
    @api.depends('name', 'date', 'state')
    def name_get(self):
        if not self._context.get('extended_display'):
            return super(SaleOrder, self).name_get()
        result = []
        date_format = '%d/%m/%Y' if self.env.user.lang == 'fr_FR' else DEFAULT_SERVER_DATE_FORMAT
        for record in self:
            date_order = fields.Date.from_string(record.date_order).strftime(date_format)
            order_state = get_selection_label(self, record._name, 'state', record.state)
            record_name = "%s - %s - %s" % (
                record.name, order_state, date_order
            )
            result.append((record.id, record_name))
        return result

    @api.depends('company_id')
    def _compute_of_allow_quote_addition(self):
        option = self.env['ir.values'].get_default('sale.config.settings', 'of_allow_quote_addition')
        for order in self:
            order.of_allow_quote_addition = option

    @api.depends('of_echeance_line_ids', 'amount_total')
    def _compute_of_echeances_modified(self):
        for order in self:
            order.of_echeances_modified = bool(order.of_echeance_line_ids
                                               and float_compare(order.amount_total,
                                                                 sum(order.of_echeance_line_ids.mapped('amount')),
                                                                 precision_rounding=.01))

    @api.depends('order_line', 'order_line.qty_delivered', 'order_line.product_uom_qty')
    def _compute_delivered(self):
        for order in self:
            for line in order.order_line:
                if float_compare(line.qty_delivered, line.product_uom_qty, 2) < 0:
                    order.of_delivered = False
                    break
            else:
                order.of_delivered = True

    @api.depends('of_fixed_invoice_date', 'of_invoice_policy',
                 'order_line', 'order_line.of_invoice_date_prev',
                 'order_line.procurement_ids', 'order_line.procurement_ids.move_ids',
                 'order_line.procurement_ids.move_ids.picking_id.min_date')
    def _compute_of_invoice_date_prev(self):
        for order in self:
            if order.of_fixed_invoice_date or order.of_invoice_policy == 'order':
                order.of_invoice_date_prev = order.of_fixed_invoice_date
            elif order.of_invoice_policy == 'delivery':
                pickings = order.order_line.mapped('procurement_ids')\
                                           .mapped('move_ids')\
                                           .mapped('picking_id')\
                                           .filtered(lambda p: p.state != 'cancel')\
                                           .sorted('min_date')
                if pickings:
                    to_process_pickings = pickings.filtered(lambda p: p.state != 'done')
                    if to_process_pickings:
                        order.of_invoice_date_prev = fields.Date.to_string(
                            fields.Date.from_string(to_process_pickings[0].min_date))
                    else:
                        order.of_invoice_date_prev = fields.Date.to_string(
                            fields.Date.from_string(pickings[-1].min_date))

    def _inverse_of_invoice_date_prev(self):
        for order in self:
            order.of_fixed_invoice_date = order.of_invoice_date_prev

    def _of_get_max_or_min_seq_by_layout(self, what='max'):
        self.ensure_one()
        lines_with_layout = self.order_line.filtered(lambda l: l.layout_category_id)
        seq_by_layout = {}.fromkeys(lines_with_layout.mapped('layout_category_id').ids, 0)
        for layout_id in seq_by_layout:
            if what == 'max':
                seq = max(lines_with_layout.filtered(lambda l: l.layout_category_id.id == layout_id).mapped('sequence'))
            else:
                seq = min(lines_with_layout.filtered(lambda l: l.layout_category_id.id == layout_id).mapped('sequence'))
            seq_by_layout[layout_id] = seq
        return seq_by_layout

    @api.multi
    def of_get_taxes_values(self):
        tax_grouped = {}
        round_curr = self.currency_id.round
        for line in self.order_line:
            price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)

            taxes = line.tax_id.compute_all(price_unit, self.currency_id, line.product_uom_qty,
                                            product=line.product_id, partner=self.partner_shipping_id)['taxes']
            for val in taxes:
                key = val['account_id']

                val['amount'] += val['base'] - round_curr(val['base'])
                if key not in tax_grouped:
                    tax_grouped[key] = {
                        'tax_id': val['id'],
                        'amount': val['amount'],
                        'base': round_curr(val['base'])
                    }
                else:
                    tax_grouped[key]['amount'] += val['amount']
                    tax_grouped[key]['base'] += round_curr(val['base'])

        for values in tax_grouped.itervalues():
            values['base'] = round_curr(values['base'])
            values['amount'] = round_curr(values['amount'])
        return tax_grouped

    @api.multi
    def _of_compute_echeances(self):
        self.ensure_one()
        if not self.payment_term_id:
            return False
        dates = {
            'order': self.state not in ('draft', 'sent', 'cancel') and self.confirmation_date,
            'invoice': self.invoice_status == 'invoiced' and self.invoice_ids[0].date_invoice,
            'default': False,
        }
        amounts = self.payment_term_id.compute(self.amount_total, dates=dates)[0]

        amount_total = self.amount_total
        pct_left = 100.0
        pct = 0
        result = [(5, )]
        for term, (date, amount) in itertools.izip(self.payment_term_id.line_ids, amounts):
            pct_left -= pct
            pct = round(100 * amount / amount_total, 2) if amount_total else 0

            line_vals = {
                'name': term.name,
                'percent': pct,
                'amount': amount,
                'date': date,
            }
            result.append((0, 0, line_vals))
        if len(result) > 1:
            result[-1][2]['percent'] = pct_left
        return result

    @api.depends('state', 'order_line.invoice_status', 'of_force_invoice_status')
    def _get_invoiced(self):
        # Appel du super dans tous les cas pour le calcul de invoice_count et invoice_ids
        super(SaleOrder, self)._get_invoiced()
        for order in self:
            if order.of_force_invoice_status:
                order.invoice_status = order.of_force_invoice_status

    @api.onchange('partner_id')
    def onchange_partner_id(self):
        fiscal_position = self.fiscal_position_id
        payment_term = self.payment_term_id

        super(SaleOrder, self).onchange_partner_id()
        self.of_invoice_policy = self.partner_id and self.partner_id.of_invoice_policy or False

        # Si la nouvelle valeur est vide, on remet l'ancienne
        if fiscal_position != self.fiscal_position_id and not self.fiscal_position_id:
            self.fiscal_position_id = fiscal_position.id
        if payment_term != self.payment_term_id and not self.payment_term_id:
            self.payment_term_id = payment_term.id

        if self.partner_id:
            # Référence client
            ref = self.partner_id.ref
            if not ref and self.partner_id.parent_id:
                ref = self.partner_id.parent_id.ref
            self.client_order_ref = ref

            # Adresses par défaut
            if not self.partner_invoice_id.of_default_address:
                default_invoice_address = self.partner_id.child_ids.filtered(
                    lambda child: child.type == 'invoice' and child.of_default_address)
                if default_invoice_address:
                    if len(default_invoice_address) > 1:
                        default_invoice_address = default_invoice_address[0]
                    self.partner_invoice_id = default_invoice_address
            if not self.partner_shipping_id.of_default_address:
                default_shipping_address = self.partner_id.child_ids.filtered(
                    lambda child: child.type == 'delivery' and child.of_default_address)
                if default_shipping_address:
                    if len(default_shipping_address) > 1:
                        default_shipping_address = default_shipping_address[0]
                    self.partner_shipping_id = default_shipping_address

    @api.multi
    @api.onchange('partner_shipping_id', 'partner_id')
    def onchange_partner_shipping_id(self):
        fiscal_position = self.fiscal_position_id
        super(SaleOrder, self).onchange_partner_shipping_id()
        # Si la nouvelle valeur est vide, on remet l'ancienne
        if fiscal_position != self.fiscal_position_id and not self.fiscal_position_id:
            self.fiscal_position_id = fiscal_position.id
        return {}

    @api.onchange('partner_id')
    def onchange_partner_id_warning(self):
        if not self.partner_id:
            return
        partner = self.partner_id

        # If partner has no warning, check its parents
        # invoice_warn is shared between different objects
        if not partner.of_is_sale_warn and partner.parent_id:
            partner = partner.parent_id

        if partner.of_is_sale_warn and partner.invoice_warn != 'no-message':
            return super(SaleOrder, self).onchange_partner_id_warning()
        return

    @api.onchange('payment_term_id')
    def _onchange_payment_term_id(self):
        if self.payment_term_id:
            self.of_echeance_line_ids = self._of_compute_echeances()

    @api.onchange('amount_total')
    def _onchange_amount_total(self):
        self._onchange_payment_term_id()

    @api.multi
    def of_update_dates_echeancier(self):
        for order in self:
            if not order.payment_term_id:
                continue

            date_invoice = order.invoice_status == 'invoiced' and order.invoice_ids and \
                order.invoice_ids[0].date_invoice or False
            dates = {
                'order': order.confirmation_date,
                'invoice': date_invoice,
                'default': False,
            }
            force_dates = [echeance.date for echeance in order.of_echeance_line_ids]
            echeances = order.payment_term_id.compute(order.amount_total, dates=dates, force_dates=force_dates)[0]

            if len(echeances) != len(order.of_echeance_line_ids):
                continue

            for echeance, ech_calc in itertools.izip(order.of_echeance_line_ids, echeances):
                if ech_calc[0] and not echeance.date:
                    echeance.date = ech_calc[0]

    @api.multi
    def action_verification_confirm(self):
        """
        Permet de faire les vérification avant de démarrer la confirmation de la commande.
        Comme il n'y a pas de raise si on veut une vérification qui bloque la confirmation il faut le faire hors de
        action_confirm, autrement certaines surcharge qui seraient passées avant/après seront tout de même réalisées
        """

        action = False
        for order in self:
            action, interrupt = self.env['of.sale.order.verification'].do_verification(order)
            if interrupt:
                return action
        res = self.action_confirm()
        if action:
            return action
        return res

    @api.multi
    def action_confirm(self):
        res = super(SaleOrder, self).action_confirm()
        self.of_update_dates_echeancier()
        return res

    @api.multi
    def of_recompute_echeance_last(self):
        for order in self:
            if not order.of_echeance_line_ids:
                continue

            percent = 100.0
            amount = order.amount_total
            for echeance in order.of_echeance_line_ids:
                if echeance.last:
                    echeance.write({
                        'percent': percent,
                        'amount': amount,
                    })
                else:
                    percent -= echeance.percent
                    amount -= echeance.amount

    @api.model
    def create(self, vals):
        mail_subtype = self.env.ref('of_base.mail_message_subtype_mail', raise_if_not_found=False)
        record = super(SaleOrder, self).create(vals)
        if mail_subtype:
            record.message_subscribe(partner_ids=[vals['partner_id']], subtype_ids=[mail_subtype.id], force=False)
        return record

    @api.multi
    def write(self, vals):
        mail_subtype = self.env.ref('of_base.mail_message_subtype_mail', raise_if_not_found=False)
        if mail_subtype and vals.get('partner_id'):
            old_partner_ids = self.mapped('partner_id')._ids
        res = super(SaleOrder, self).write(vals)
        if mail_subtype and vals.get('partner_id'):
            # subscribe new partner and unsunscribe the old ones
            self.message_subscribe(partner_ids=[vals['partner_id']], subtype_ids=[mail_subtype.id], force=False)
            message_followers = self.mapped('message_follower_ids')
            message_followers.filtered(lambda r: r.partner_id.id in old_partner_ids)\
                             .write({'subtype_ids': [(3, mail_subtype.id)]})
        # Recalcul de la dernière échéance si besoin
        self.filtered('of_echeances_modified').of_recompute_echeance_last()
        return res

    def _search_of_marge_pc(self, operator, value):
        top = value + 0.004
        down = value - 0.005
        params = []
        request = "SELECT id FROM sale_order WHERE "
        if operator == '=':
            request += "(100 * (margin / NULLIF(amount_untaxed, 0))) >= %s AND " \
                "(100 * (margin / NULLIF(amount_untaxed, 0))) <= %s;"
            params = (down, top)
        elif operator == '!=':
            request += "(100 * (margin / NULLIF(amount_untaxed, 0))) <= %s OR " \
                "(100 * (margin / NULLIF(amount_untaxed, 0))) >= %s;"
            params = (down, top)
        elif operator == '>=':
            request += "(100 * (margin / NULLIF(amount_untaxed, 0))) >= %s;"
            params = (down,)
        elif operator == '>':
            request += "(100 * (margin / NULLIF(amount_untaxed, 0))) > %s;"
            params = (top,)
        elif operator == '<=':
            request += "(100 * (margin / NULLIF(amount_untaxed, 0))) <= %s;"
            params = (top,)
        elif operator == '<':
            request += "(100 * (margin / NULLIF(amount_untaxed, 0))) < %s;"
            params = (down,)
        else:
            raise NotImplementedError(_("Search operator %s not implemented for value %s") % (operator, value))
        self.env.cr.execute(request, params)
        ids = [r[0] for r in self.env.cr.fetchall()]
        return [('id', 'in', ids)]

    @api.depends('state', 'order_line', 'order_line.qty_to_invoice', 'order_line.product_uom_qty')
    def _compute_of_to_invoice(self):
        for order in self:
            if order.state not in ('sale', 'done') or order.of_force_invoice_status in ('invoiced', 'no'):
                order.of_to_invoice = False
                continue
            for line in order.order_line:
                if line.qty_to_invoice + line.qty_invoiced < line.product_uom_qty:
                    order.of_to_invoice = False
                    break
            else:
                order.of_to_invoice = True

    @api.depends('margin', 'amount_untaxed')
    def _compute_of_marge(self):
        for order in self:
            cout = order.amount_untaxed - order.margin
            order.of_total_cout = cout
            order.of_marge_pc = 100 * (1 - cout / order.amount_untaxed) if order.amount_untaxed else -100

    def toggle_view(self):
        """ Permet de basculer entre la vue vendeur/client
        """
        self.of_client_view = not self.of_client_view

    @api.multi
    def _of_get_total_lines_by_group(self):
        """
        Retourne les lignes de la commande, séparées en fonction du groupe dans lequel les afficher.
        Les groupes sont ceux définis par l'objet of.invoice.report.total, permettant de déplacer le rendu des
          lignes de commande sous le total hors taxe ou TTC.
        Les groupes sont affichés dans leur ordre propre, puis les lignes dans l'ordre d'apparition dans la commande.
        @param return: Liste de couples (groupe, lignes de commande). Le 1er élément vaut (False, Lignes non groupées).
        """
        self.ensure_one()
        group_obj = self.env['of.invoice.report.total.group']

        lines = self.order_line
        products = lines.mapped('product_id')
        product_ids = list(products._ids)
        categ_ids = list(products.mapped('categ_id')._ids)
        groups = group_obj.search([('order', '=', True),
                                   '|', ('id', '=', group_obj.get_group_paiements().id),
                                   '|', ('product_ids', 'in', product_ids), ('categ_ids', 'in', categ_ids)])

        result = []
        for group in groups:
            if group.is_group_paiements():
                group_paiement_lines = group.filter_lines(lines)
                if group_paiement_lines is not False:
                    lines -= group_paiement_lines
                break
        for group in groups:
            if group.is_group_paiements():
                result.append((group, group_paiement_lines))
            else:
                group_lines = group.filter_lines(lines)
                if group_lines is not False:
                    # On ajoute cette vérification pour ne pas afficher des lignes à 0 dans les paiements et
                    # ne pas afficher le groupe si toutes les lignes sont à 0.
                    group_lines_2 = group_lines.filtered(lambda l: l.price_subtotal)
                    if group_lines_2:
                        result.append((group, group_lines_2))
                    # On enlève quand même toutes les lignes du groupe pour ne pas qu'elle s'affichent
                    lines -= group_lines
        if lines:
            result = [(False, lines)] + result
        else:
            result = [(False, self.order_line.mapped('invoice_lines'))]
            # On ajoute quand-même les paiements
            for group in groups:
                if group.is_group_paiements():
                    result.append((group, lines))  # lines est vide
        return result

    @api.multi
    def _of_get_printable_lines(self):
        """ [IMPRESSION]
        Renvoie les lignes à afficher
        """
        return self._of_get_total_lines_by_group()[0][1]

    def _prepare_tax_line_vals(self, line, tax):
        """ Emulation de la fonction du même nom du modèle 'account.invoice'
            Permet de récupérer la clé de groupement dans _of_get_printable_totals
        """
        vals = {
            'name': tax['name'],
            'tax_id': tax['id'],
            'amount': tax['amount'],
            'base': tax['base'],
            'manual': False,
            'sequence': tax['sequence'],
            'account_analytic_id': tax['analytic'] or False,
            'account_id': tax['account_id'] or tax['refund_account_id'] or False,

        }
        return vals

    @api.multi
    def _of_get_printable_totals(self):
        """ [IMPRESSION]
        Retourne un dictionnaire contenant les valeurs à afficher dans les totaux de la commande pdf.
        Dictionnaire de la forme :
        {
            'subtotal' : Total HT des lignes affichées,
            'untaxed' : [[('libellé', montant),...], ('libellé total': montant_total)]
            'taxes' : idem,
            'total' : idem,
        }
        Les listes untaxed, taxes et total pourraient être regroupés en une seule.
        Ce format pourra aider aux héritages (?).
        """
        self.ensure_one()
        tax_obj = self.env['account.tax']
        round_curr = self.currency_id.round

        group_lines = self._of_get_total_lines_by_group()

        result = {}
        result['subtotal'] = sum(group_lines[0][1].mapped('price_subtotal'))
        total_amount = result['subtotal']

        i = 1
        untaxed_lines = group_lines[0][1]
        # --- Sous-totaux hors taxes ---
        result_untaxed = []
        while i < len(group_lines) and group_lines[i][0].position == '0-ht':
            group, lines = group_lines[i]
            i += 1
            untaxed_lines |= lines
            lines_vals = []
            for line in lines:
                lines_vals.append((line.of_get_line_name()[0], line.price_subtotal))
                total_amount += line.price_subtotal
            total_vals = (group.subtotal_name, round_curr(total_amount))
            result_untaxed.append([lines_vals, total_vals])
        result['untaxed'] = result_untaxed

        # --- Ajout des taxes ---
        # Code copié depuis account.invoice.get_taxes_values()
        tax_grouped = {}
        for line in untaxed_lines:
            price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
            taxes = line.tax_id.compute_all(price_unit, self.currency_id, line.product_uom_qty, line.product_id,
                                            self.partner_id)['taxes']
            for tax_val in taxes:
                val = self._prepare_tax_line_vals(line, tax_val)
                tax = tax_obj.browse(tax_val['id'])
                key = tax.get_grouping_key(val)

                val['amount'] += val['base'] - round_curr(val['base'])
                if key not in tax_grouped:
                    tax_grouped[key] = val
                    tax_grouped[key]['name'] = tax.description or tax.name
                    tax_grouped[key]['group'] = tax.tax_group_id
                else:
                    tax_grouped[key]['amount'] += val['amount']
        # Taxes groupées par groupe de taxes (cf account.invoice._get_tax_amount_by_group())
        tax_vals_dict = {}
        for tax in sorted(tax_grouped.values(), key=lambda t: t['name']):
            amount = round_curr(tax['amount'])
            tax_vals_dict.setdefault(tax['group'], [tax['group'].name, 0])
            tax_vals_dict[tax['group']][1] += amount
            total_amount += amount
        result['taxes'] = [[tax_vals_dict.values(), (_("Total TTC"), round_curr(total_amount))]]

        # --- Sous-totaux TTC ---
        result_total = []
        while i < len(group_lines):
            # Tri des paiements par date
            group, lines = group_lines[i]
            i += 1
            if group.is_group_paiements():
                lines_vals = self._of_get_printable_payments(lines)
                if not lines_vals:
                    continue
                for line in lines_vals:
                    total_amount -= line[1]
            else:
                lines_vals = []
                for line in lines:
                    lines_vals.append((line.of_get_line_name()[0], line.price_total))
                    total_amount += line.price_total
            total_vals = (group.subtotal_name, round_curr(total_amount))
            if group.hide_amount_total and len(result['taxes'][0]) == 2:
                result['taxes'][0].pop(1)
            result_total.append([lines_vals, total_vals])
        result['total'] = result_total

        return result

    @api.multi
    def order_lines_layouted(self):
        """
        Retire les lignes de commande qui doivent êtres affichées dans les totaux.
        """
        report_pages_full = super(SaleOrder, self).order_lines_layouted()
        report_lines = self._of_get_printable_lines()
        report_pages = []
        for page_full in report_pages_full:
            page = []
            for group in page_full:
                lines = [line for line in group['lines'] if line in report_lines]
                if lines:
                    group['lines'] = lines
                    page.append(group)
            if page:
                report_pages.append(page)
        return report_pages

    @api.multi
    def _of_get_printable_payments(self, order_lines):
        """ [IMPRESSION]
        Renvoie les lignes à afficher.
        Permet l'affichage des paiements dans une commande.
        On ne va pas chercher les paiements affectés à la commande car le lien est ajouté dans of_sale_payment
        """
        invoice_obj = self.env['account.invoice']
        account_move_line_obj = self.env['account.move.line']
        # Liste des factures et factures d'acompte
        invoices = self.mapped('order_line').mapped('invoice_lines').mapped('invoice_id')

        # Retour de tous les paiements des factures
        # On distingue les paiements de la facture principale de ceux des factures liées
        result = []
        for invoice in invoices:
            widget = json.loads(invoice.payments_widget.replace("'", "\'"))
            if not widget:
                continue
            for payment in widget.get('content', []):
                # Les paiements sont classés dans l'ordre chronologique
                move_line = account_move_line_obj.browse(payment['payment_id'])
                name = invoice_obj._of_get_payment_display(move_line)
                result.append((name, payment['amount']))
        return result

    @api.multi
    def _prepare_invoice(self):
        """ Rajout date visite technique. Attention en cas de facturation de plusieurs bons de commande à la fois"""
        self.ensure_one()
        if self.company_id:
            self = self.with_context(company_id=self.company_id.id)
        invoice_vals = super(SaleOrder, self)._prepare_invoice()
        invoice_vals["of_date_vt"] = self.of_date_vt
        if self.of_apply_on_invoice:
            invoice_vals["of_price_printing"] = self.of_price_printing
        if not self.env['ir.values'].get_default('sale.config.settings', 'of_propagate_payment_term'):
            invoice_vals['payment_term_id'] = False
        return invoice_vals

    @api.multi
    def copy(self, default=None):
        res = super(SaleOrder, self).copy(default=default)
        res._onchange_payment_term_id()
        return res

    @api.multi
    def action_invoice_create(self, grouped=False, final=False):
        grouped = self.env['ir.values'].get_default('sale.config.settings', 'of_invoice_grouped')
        invoice_ids = super(SaleOrder, self).action_invoice_create(grouped=grouped, final=final)
        invoices = self.env['account.invoice'].browse(invoice_ids)

        if self._context.get('of_include_null_qty_lines', False) and invoices:
            for order in self:
                # On récupère la facture générée correspondant à cette commande
                invoice = invoices.filtered(lambda inv: inv.origin == order.name)
                if invoice:
                    # On ajoute dans la facture les lignes correspondantes aux lignes de commande en quantité 0
                    # et qui n'ont pas de lignes de facture associées
                    for order_line in order.order_line.filtered(
                            lambda l: l.product_uom_qty == 0.0 and not l.invoice_lines):
                        vals = order_line._prepare_invoice_line(qty=0.0)
                        vals.update({'invoice_id': invoice.id, 'sale_line_ids': [(6, 0, [order_line.id])]})
                        self.env['account.invoice.line'].create(vals)

        # Pour les factures groupées, on indique pour chaque ligne de facture sa commande d'origine
        for inv in invoices:
            if len(inv.invoice_line_ids.mapped('sale_line_ids').mapped('order_id')) > 1:
                for line in inv.invoice_line_ids:
                    order_line = line.sale_line_ids[:1]
                    line.name = "%s %s\n%s" % (
                        order_line.order_id.name, order_line.order_id.client_order_ref or "", line.name)

        return invoice_ids

    @api.multi
    def action_add_quote(self):
        self.ensure_one()

        if self.state != 'sale':
            raise UserError(u"Vous ne pouvez pas ajouter un devis complémentaire à une commande non validée.")

        wizard = self.env['of.sale.order.add.quote.wizard'].create({
            'order_id': self.id,
        })

        return {
            'type': 'ir.actions.act_window',
            'name': "Ajouter un devis complémentaire",
            'view_mode': 'form',
            'res_model': 'of.sale.order.add.quote.wizard',
            'res_id': wizard.id,
            'target': 'new',
        }

    @api.multi
    def of_get_taxes_display(self):
        tax_obj = self.env['account.tax']
        tax_grouped = []
        round_curr = self.currency_id.round
        for line in self.order_line:
            price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)

            taxes = line.tax_id.compute_all(price_unit, self.currency_id, line.product_uom_qty,
                                            product=line.product_id, partner=self.partner_shipping_id)['taxes']
            for val in taxes:
                key = val['id']
                tax = tax_obj.browse(key)
                for values in tax_grouped:
                    if values['id'] == key:
                        values['amount'] += val['amount']
                        values['base'] += round_curr(val['base'])
                        break
                else:
                    tax_grouped.append({
                        'id': key,
                        'name': tax.description,
                        'amount': val['amount'],
                        'base': round_curr(val['base'])
                    })
        for values in tax_grouped:
            values['base'] = round_curr(values['base'])
            values['amount'] = round_curr(values['amount'])
        return tax_grouped

    @api.multi
    def action_quotation_send(self):
        mail_subtype = self.env.ref('of_base.mail_message_subtype_mail', raise_if_not_found=False)
        action = super(SaleOrder, self).action_quotation_send()
        if mail_subtype:
            action['ctx'].update({'default_subtype_id': mail_subtype.id})
        return action

class SaleOrderLine(models.Model):
    _name = 'sale.order.line'
    _inherit = ['sale.order.line', 'of.readgroup']

    price_unit = fields.Float(digits=False, help="""
    Prix unitaire de l'article.
    À entrer HT ou TTC suivant la TVA de la ligne de commande.
    """)
    of_client_view = fields.Boolean(string="Vue client/vendeur", related="order_id.of_client_view")
    of_article_principal = fields.Boolean(
        string="Article principal", help="Cet article est l'article principal de la commande"
    )
    of_product_categ_id = fields.Many2one(
        'product.category', related='product_id.categ_id', string=u"Catégorie d'article", store=True, index=True)
    date_order = fields.Datetime(related='order_id.date_order', string="Date de commande", store=True, index=True)
    confirmation_date_order = fields.Datetime(
        related='order_id.confirmation_date', string="Date de confirmation de commande", store=True, index=True)
    of_gb_partner_tag_id = fields.Many2one(
        'res.partner.category', compute=lambda *a, **k: {}, search='_search_of_gb_partner_tag_id',
        string="Étiquette client", of_custom_groupby=True
    )
    of_price_unit_display = fields.Float(related='price_unit', string=u"Prix unitaire", readonly=True)
    of_product_forbidden_discount = fields.Boolean(string=u"Remise interdite pour cet article")

    of_price_unit_ht = fields.Float(
        string='Unit Price excl', compute='_compute_of_price_unit', help="Unit price without taxes", store=True
    )
    of_price_unit_ttc = fields.Float(
        string='Unit Price incl', compute='_compute_of_price_unit', help="Unit price with taxes", store=True
    )
    of_marge_pc = fields.Float(
        compute='_compute_of_marge', string=u"Marge %", store=True)

    of_product_default_code = fields.Char(related='product_id.default_code', string=u"Référence article", readonly=True)
    of_order_line_option_id = fields.Many2one(comodel_name='of.order.line.option', string=u"Option")
    of_reset_option = fields.Boolean(string=u"Réinitialiser l'option ?")

    of_confirmation_date = fields.Datetime(
        string="Date de confirmation", related="order_id.confirmation_date", store=True)
    of_invoice_policy = fields.Selection([('order', u'Quantités commandées'), ('delivery', u'Quantités livrées')],
                                         string="Politique de facturation",
                                         compute="_compute_of_invoice_policy",
                                         store=True)
    of_invoice_date_prev = fields.Date(
        string=u"Date de facturation prévisionnelle", compute="_compute_of_invoice_date_prev", store=True,
        compute_sudo=True)
    of_seller_price = fields.Float(string=u"Prix d'achat")

    of_date_tarif = fields.Date(string="Date du tarif", related="product_id.date_tarif", readonly=True)
    of_obsolete = fields.Boolean(string=u"Article obsolète", related="product_id.of_obsolete", readonly=True)
    of_product_image_ids = fields.Many2many('of.product.image', string='Images')
    of_product_attachment_ids = fields.Many2many("ir.attachment", string="Documents joints")
    # Champ servant au calcul du domain de of_product_attachment_ids
    of_product_attachment_computed_ids = fields.Many2many(
        "ir.attachment", string="Documents joints",
        compute='_compute_of_product_attachment_computed_ids')
    # A supprimer après la prochaine màj
    of_product_attachment_computed = fields.Boolean(compute=lambda s: None)

    @api.model_cr_context
    def _auto_init(self):
        """
        Modification du nom du champ 'of_product_seller_price' en 'of_seller_price' dans les vues xml.
        TODO: A SUPPRIMER APRES INSTALLATION !
        """
        cr = self._cr
        cr.execute(
            "SELECT 1 FROM information_schema.columns WHERE table_name = %s AND column_name = 'of_seller_price'",
            (self._table,))
        exists = bool(cr.fetchall())
        res = super(SaleOrderLine, self)._auto_init()
        if not exists:
            cr.execute(
                """ UPDATE  ir_ui_view
                    SET     arch_db     = REPLACE(arch_db, 'of_product_seller_price', 'of_seller_price')
                    WHERE   arch_db     LIKE '%of_product_seller_price%'
                """)
        return res

    of_price_management_variation = fields.Float(
        string=u"Montant unitaire de la variation de prix liée à la gestion de prix")
    of_unit_price_variation = fields.Float(string=u"Montant unitaire de la variation de prix")

    @api.depends('price_subtotal', 'margin')
    def _compute_of_marge(self):
        for line in self:
            if line.price_subtotal:
                line.of_marge_pc = line.margin * 100.0 / line.price_subtotal
            else:
                line.of_marge_pc = 0.0

    @api.depends('product_id')
    def _compute_of_product_attachment_computed_ids(self):
        product_obj = self.env['product.product']
        attachment_obj = self.env['ir.attachment']
        for line in self:
            # On récupère toutes les variantes du modèle d'article
            product_ids = product_obj.search([('product_tmpl_id', '=', line.product_id.product_tmpl_id.id)])

            # On récupère toutes les PJ pdf du modèle d'article et de ses variantes
            domain = [
                '&',
                '|',
                '&',
                ('res_model', '=', 'product.template'),
                ('res_id', '=', line.product_id.product_tmpl_id.id),
                '&',
                ('res_model', '=', 'product.product'),
                ('res_id', 'in', product_ids.ids),
                ('mimetype', '=', 'application/pdf')
            ]
            attachment_ids = attachment_obj.search(domain)
            line.of_product_attachment_computed_ids = attachment_ids

    @api.depends('price_unit', 'order_id.currency_id', 'order_id.partner_shipping_id', 'product_id',
                 'price_subtotal', 'product_uom_qty')
    def _compute_of_price_unit(self):
        """
        @ TODO: à fusionner avec _compute_amount
        :return:
        """
        for line in self:
            taxes = line.tax_id.compute_all(line.price_unit, line.order_id.currency_id, 1,
                                            product=line.product_id, partner=line.order_id.partner_shipping_id)
            line.of_price_unit_ht = taxes['total_excluded']
            line.of_price_unit_ttc = taxes['total_included']

    @api.depends('product_id', 'product_id.invoice_policy',
                 'order_id', 'order_id.of_invoice_policy',
                 'order_partner_id', 'order_partner_id.of_invoice_policy')
    def _compute_of_invoice_policy(self):
        for line in self:
            line.of_invoice_policy = line.order_id.of_invoice_policy \
                or line.order_partner_id.of_invoice_policy or line.product_id.invoice_policy \
                or self.env['ir.values'].get_default('product_template', 'invoice_policy')

    @api.depends('of_invoice_policy',
                 'order_id', 'order_id.of_fixed_invoice_date',
                 'procurement_ids', 'procurement_ids.move_ids', 'procurement_ids.move_ids')
    def _compute_of_invoice_date_prev(self):
        for line in self:
            if line.of_invoice_policy == 'order':
                line.of_invoice_date_prev = line.order_id.of_invoice_date_prev
            elif line.of_invoice_policy == 'delivery':
                moves = line.procurement_ids.mapped('move_ids').sorted('date_expected')
                if moves:
                    line.of_invoice_date_prev = fields.Date.to_string(fields.Date.from_string(moves[0].date_expected))

    @api.model
    def _search_of_gb_partner_tag_id(self, operator, value):
        return [('order_partner_id.category_id', operator, value)]

    @api.model
    def _read_group_process_groupby(self, gb, query):
        # Ajout de la possibilité de regrouper par employé
        if gb != 'of_gb_partner_tag_id':
            return super(SaleOrderLine, self)._read_group_process_groupby(gb, query)

        alias, _ = query.add_join(
            (self._table, 'res_partner_res_partner_category_rel', 'order_partner_id', 'partner_id', 'partner_category'),
            implicit=False, outer=True,
        )

        return {
            'field': gb,
            'groupby': gb,
            'type': 'many2one',
            'display_format': None,
            'interval': None,
            'tz_convert': False,
            'qualified_field': '"%s".category_id' % (alias,)
        }

    @api.model
    def of_custom_groupby_generate_order(self, alias, order_field, query, reverse_direction, seen):
        if order_field == 'of_gb_partner_tag_id':
            dest_model = self.env['res.partner.category']
            m2o_order = dest_model._order
            if not regex_order.match(m2o_order):
                # _order is complex, can't use it here, so we default to _rec_name
                m2o_order = dest_model._rec_name

            rel_alias, _ = query.add_join(
                (alias, 'res_partner_res_partner_category_rel',
                 'order_partner_id', 'partner_id', 'partner_category_rel'),
                implicit=False, outer=True)
            dest_alias, _ = query.add_join(
                (rel_alias, 'res_partner_category', 'category_id', 'id', 'partner_category'),
                implicit=False, outer=True)
            return dest_model._generate_order_by_inner(dest_alias, m2o_order, query,
                                                       reverse_direction, seen)
        return []

    def _compute_margin(self, order_id, product_id, product_uom_id):
        """Override to use the theoretical cost instead of the standard cost price when the settings is set to True"""
        frm_cur = self.env.user.company_id.currency_id
        to_cur = order_id.pricelist_id.currency_id
        purchase_price = product_id.get_cost()
        if product_uom_id != product_id.uom_id:
            purchase_price = product_id.uom_id._compute_price(purchase_price, product_uom_id)
        price = frm_cur.with_context(date=order_id.date_order).compute(purchase_price, to_cur, round=False)
        return price

    @api.multi
    @api.onchange('product_id')
    def product_id_change(self):
        if not self.product_id:
            return
        if not self.order_id.partner_id:
            self.product_id = False
            warning = {
                'title': (_("Warning!")),
                'message': (_("You must fill in the Customer field to go further."))
            }
            return {'warning': warning}

        res = super(SaleOrderLine, self).product_id_change()
        afficher_descr_fab = self.env.user.company_id.afficher_descr_fab
        afficher = afficher_descr_fab == 'devis' or afficher_descr_fab == 'devis_factures'
        product = self.product_id.with_context(
            lang=self.order_id.partner_id.lang,
            partner=self.order_id.partner_id.id,
        )
        if product and product.description_fabricant and afficher:
            name = self.name
            name += '\n' + product.description_fabricant
            self.update({'name': name})

        # Remise interdite

        if self.product_id:
            self.of_product_forbidden_discount = self.product_id.of_forbidden_discount or not self.env.user.has_group(
                'of_sale.group_of_can_modify_sale_price_unit')
            if self.product_id.of_forbidden_discount and self.of_discount_formula:
                self.of_discount_formula = False
            if self.product_id.categ_id:
                self.of_article_principal = self.product_id.categ_id.of_article_principal
            if self.env.user.has_group('sale.group_sale_layout'):
                if self.product_id.of_layout_category_id:
                    self.layout_category_id = product.of_layout_category_id
                elif self.product_id.categ_id.of_layout_id:
                    self.layout_category_id = self.product_id.categ_id.of_layout_id
            if self.env.user.has_group('of_sale.group_of_sale_multiimage'):
                if self.product_id.product_tmpl_id.of_product_image_ids:
                    of_product_image_ids = self.product_id.product_tmpl_id.of_product_image_ids
                    self.of_product_image_ids = self.product_id.product_tmpl_id.of_product_image_ids
                    res.setdefault('domain', {})
                    res['domain']['of_product_image_ids'] = [('id', 'in', of_product_image_ids.ids)]
            if self.env.user.has_group('of_sale.group_of_sale_print_attachment'):
                attachment_ids = self.env['ir.attachment'].search(
                    [('id', 'in', self.of_product_attachment_computed_ids.ids)])
                self.of_product_attachment_ids = attachment_ids

        return res

    @api.onchange('product_id', 'product_uom')
    def product_id_change_margin(self):
        super(SaleOrderLine, self).product_id_change_margin()

        if not self.order_id.pricelist_id or not self.product_id or not self.product_uom:
            return

        frm_cur = self.env.user.company_id.currency_id
        to_cur = self.order_id.pricelist_id.currency_id
        seller_price = self.product_id.of_seller_price
        if self.product_uom != self.product_id.uom_id:
            seller_price = self.product_id.uom_id._compute_price(seller_price, self.product_uom)
        ctx = self.env.context.copy()
        ctx['date'] = self.order_id.date_order
        self.of_seller_price = frm_cur.with_context(ctx).compute(seller_price, to_cur, round=False)

    @api.model
    def _get_purchase_price(self, pricelist, product, product_uom, date):
        """Override to use the theoretical cost instead of the standard cost price when the settings is set to True"""
        frm_cur = self.env.user.company_id.currency_id
        to_cur = pricelist.currency_id
        purchase_price = product.get_cost()
        if product_uom != product.uom_id:
            purchase_price = product.uom_id._compute_price(purchase_price, product_uom)
        price = frm_cur.with_context(date=date).compute(purchase_price, to_cur, round=False)
        return {'purchase_price': price}

    @api.model
    def _get_of_seller_price(self, pricelist, product, product_uom, date):
        frm_cur = self.env.user.company_id.currency_id
        to_cur = pricelist.currency_id
        seller_price = product.of_seller_price
        if product_uom != product.uom_id:
            seller_price = product.uom_id._compute_price(seller_price, product_uom)
        ctx = self.env.context.copy()
        ctx['date'] = date
        price = frm_cur.with_context(ctx).compute(seller_price, to_cur, round=False)
        return {'of_seller_price': price}

    @api.onchange('of_order_line_option_id')
    def _onchange_of_order_line_option_id(self):
        if self.of_order_line_option_id and self.product_id:
            option = self.of_order_line_option_id
            if option.sale_price_update and self.price_unit:
                if option.sale_price_update_type == 'fixed':
                    self.price_unit = self.price_unit + option.sale_price_update_value
                elif option.sale_price_update_type == 'percent':
                    self.price_unit = self.price_unit + self.price_unit * (option.sale_price_update_value / 100)
                self.price_unit = self.order_id.currency_id.round(self.price_unit)
            if option.purchase_price_update and self.purchase_price:
                if option.purchase_price_update_type == 'fixed':
                    self.purchase_price = self.purchase_price + option.purchase_price_update_value
                elif option.purchase_price_update_type == 'percent':
                    self.purchase_price = \
                        self.purchase_price + self.purchase_price * (option.purchase_price_update_value / 100)
                self.purchase_price = self.order_id.currency_id.round(self.purchase_price)
            if option.description_update:
                self.name = self.name + "\n%s" % option.description_update

    @api.onchange('of_reset_option')
    def _onchange_of_reset_option(self):
        if self.of_reset_option:
            product = self.product_id.with_context(
                lang=self.order_id.partner_id.lang,
                partner=self.order_id.partner_id.id,
                quantity=self.product_uom_qty,
                date=self.order_id.date_order,
                pricelist=self.order_id.pricelist_id.id,
                uom=self.product_uom.id
            )

            if self.order_id.pricelist_id and self.order_id.partner_id:
                self.price_unit = self.env['account.tax']._fix_tax_included_price_company(
                    self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)
            self.purchase_price = product.get_cost()
            if self.of_order_line_option_id.description_update:
                self.name = self.name.replace(self.of_order_line_option_id.description_update, '')
            self.of_order_line_option_id = False
            self.of_reset_option = False

    @api.onchange('of_product_forbidden_discount')
    def _onchange_of_product_forbidden_discount(self):
        if self.of_product_forbidden_discount and self.product_id:
            self.price_unit = self.product_id.list_price

    def of_get_line_name(self):
        self.ensure_one()
        # inhiber l'affichage de la référence
        afficher_ref = self.env['ir.values'].get_default('sale.config.settings', 'pdf_product_reference')
        le_self = self.with_context(
            lang=self.order_id.partner_id.lang,
            partner=self.order_id.partner_id.id,
        )
        name = le_self.name
        if not afficher_ref:
            if name.startswith("["):
                splitted = name.split("]")
                if len(splitted) > 1:
                    splitted.pop(0)
                    name = ']'.join(splitted).strip()
        return name.split("\n")  # utilisation t-foreach dans template qweb

    def _write(self, vals):
        for field in vals:
            if field != 'of_product_categ_id':
                break
        else:
            self = self.sudo()

        if 'price_reduce' in vals and len(self) == 1:
            vals['of_unit_price_variation'] = \
                self.of_price_management_variation + vals.get('price_reduce', 0) - self.price_unit

        return super(SaleOrderLine, self)._write(vals)

    @api.multi
    def unlink(self):
        """
        Ne pas autoriser la suppression de ligne de commandes si la ligne est déjà présente sur une facture qui n'est
        pas une facture annulée n'ayant jamais été validée.
        """
        locked_invoice_lines = self.mapped('invoice_lines').filtered(
            lambda l: l.invoice_id.state != 'cancel' or l.invoice_id.move_name)
        if locked_invoice_lines:
            raise UserError(u"""Vous ne pouvez supprimer une ligne d'article liée à une facture.\n"""
                            u"""Veuillez annuler vos modifications.""")
        return super(SaleOrderLine, self).unlink()

    @api.model
    def create(self, vals):
        """
        Au moment de la sauvegarde de la commande, les images articles ne sont pas toujours sauvegardées
        car renseignées par un onchange et affichage en vue en kanban, du coup on surcharge le create
        """
        if vals.get('layout_category_id') and 'sequence' not in vals:
            order = self.env['sale.order'].browse(vals['order_id'])
            max_sequence = order._of_get_max_or_min_seq_by_layout().get(vals['layout_category_id'], 0)
            vals['sequence'] = max_sequence + 1
        res = super(SaleOrderLine, self).create(vals)
        if 'of_product_image_ids' in vals.keys() and vals['of_product_image_ids'] and not res.of_product_image_ids:
            res.with_context(already_tried=True).of_product_image_ids = vals['of_product_image_ids']
        return res

    @api.multi
    def write(self, vals):
        """
        Si un des champ de blocked est présent ET une ligne modifiée ne doit pas avoir de modification alors renvoi une
        erreur. Le champ of_discount_formula est dans le module of_sale_discount, la façon dont on vérifie la présence
        des champs dans vals ne provoque pas d'erreur si le module n'est pas installé.
        TODO: Permettre de modifier le montant si modification viens de la facture d'acompte
        """
        force = self._context.get('force_price')
        blocked = [x for x in ('price_unit', 'product_uom_qty', 'product_uom', 'discount', 'of_discount_formula')
                   if x in vals.keys()]
        for line in self:
            locked_invoice_lines = line.mapped('invoice_lines').filtered(lambda l: l.of_is_locked)
            if locked_invoice_lines and blocked and not force:
                raise UserError(u"""Cette ligne ne peut être modifiée : %s""" % line.name)

        # Au moment de la sauvegarde de la commande, les images articles ne sont pas toujours sauvegardées, car
        # renseignées par un onchange et affichage en vue en kanban. Du coup, on surcharge le write
        if 'already_tried' not in self._context:
            if 'of_product_image_ids' in vals.keys() and vals['of_product_image_ids'] and not self.of_product_image_ids:
                self.with_context(already_tried=True).of_product_image_ids = vals['of_product_image_ids']

        if vals.get('layout_category_id') and 'sequence' not in vals:
            new_layout = self.env['sale.layout_category'].browse(vals['layout_category_id'])
            for line in self:
                old_layout = line.layout_category_id
                order = line.order_id
                if old_layout.sequence < new_layout.sequence:
                    sequence = order._of_get_max_or_min_seq_by_layout('min').get(vals['layout_category_id'], 0)
                    vals['sequence'] = sequence - 1
                else:
                    sequence = order._of_get_max_or_min_seq_by_layout().get(vals['layout_category_id'], 0)
                    vals['sequence'] = sequence + 1

        return super(SaleOrderLine, self).write(vals)

    @api.multi
    def _additionnal_tax_verifications(self):
        invoice_line_obj = self.env['account.invoice.line']
        if self.product_id and self.product_id.id in invoice_line_obj.get_locked_product_ids():
            return True
        if self.product_id and self.product_id.categ_id and self.product_id.categ_id.id in invoice_line_obj.\
                get_locked_category_ids():
            return True
        return False

    @api.multi
    def _compute_tax_id(self):
        return super(SaleOrderLine, self.filtered(lambda line: not line._additionnal_tax_verifications())).\
            _compute_tax_id()

    @api.depends(
        'state', 'product_uom_qty', 'qty_delivered', 'qty_to_invoice', 'qty_invoiced', 'order_id.of_invoice_policy',
        'order_id.partner_id.of_invoice_policy', 'order_id.of_force_invoice_status')
    def _compute_invoice_status(self):
        """
        Compute the invoice status of a SO line. Possible statuses:
        - no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to
          invoice. This is also hte default value if the conditions of no other status is met.
        - to invoice: we refer to the quantity to invoice of the line. Refer to method
          `_get_to_invoice_qty()` for more information on how this quantity is calculated.
        - upselling: this is possible only for a product invoiced on ordered quantities for which
          we delivered more than expected. The could arise if, for example, a project took more
          time than expected but we decided not to invoice the extra cost to the client. This
          occurs only in state 'sale', so that when a SO is set to done, the upselling opportunity
          is removed from the list.
        - invoiced: the quantity invoiced is larger or equal to the quantity ordered.
        """
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        for line in self:
            if line.order_id.of_force_invoice_status:
                line.invoice_status = line.order_id.of_force_invoice_status
            else:
                invoice_policy = line.of_invoice_policy
                if line.state not in ('sale', 'done'):
                    line.invoice_status = 'no'
                elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
                    line.invoice_status = 'to invoice'
                elif line.state == 'sale' and invoice_policy == 'order' and \
                        float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1:
                    line.invoice_status = 'upselling'
                elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0:
                    line.invoice_status = 'invoiced'
                else:
                    line.invoice_status = 'no'

    @api.depends('qty_invoiced', 'qty_delivered', 'product_uom_qty', 'order_id.state',
                 'order_id.of_invoice_policy', 'order_id.partner_id.of_invoice_policy')
    def _get_to_invoice_qty(self):
        """
        Compute the quantity to invoice. If the invoice policy is order, the quantity to invoice is
        calculated from the ordered quantity. Otherwise, the quantity delivered is used.
        """
        for line in self:
            invoice_policy = line.of_invoice_policy
            if line.order_id.state in ['sale', 'done']:
                if invoice_policy == 'order':
                    line.qty_to_invoice = line.product_uom_qty - line.qty_invoiced
                elif invoice_policy == 'delivery':
                    line.qty_to_invoice = line.qty_delivered - line.qty_invoiced
            else:
                line.qty_to_invoice = 0

    def of_get_price_unit(self):
        """Renvoi le prix unitaire type."""
        self.ensure_one()
        product = self.product_id.with_context(
            lang=self.order_id.partner_id.lang,
            partner=self.order_id.partner_id.id,
            quantity=self.product_uom_qty,
            date=self.order_id.date_order,
            pricelist=self.order_id.pricelist_id.id,
            uom=self.product_uom.id,
            fiscal_position=self.env.context.get('fiscal_position')
        )
        return self.env['account.tax']._fix_tax_included_price_company(
            self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)

    @api.model
    def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
        if 'of_marge_pc' in fields and 'margin' not in fields:
            fields.append('margin')
        if 'of_marge_pc' in fields and 'price_subtotal' not in fields:
            fields.append('price_subtotal')
        res = super(SaleOrderLine, self).read_group(
            domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
        for line in res:
            if 'of_marge_pc' in fields:
                if 'margin' in line and line['margin'] is not None and \
                        'price_subtotal' in line and line['price_subtotal']:
                    line['of_marge_pc'] = round(100.0 * line['margin'] / line['price_subtotal'], 2)
                else:
                    line['of_marge_pc'] = 0.0
        return res

class SaleLayoutCategory(models.Model):
    _inherit = 'sale.layout_category'

    active = fields.Boolean(string="Active", default=True)

class OFOrderLineOption(models.Model):
    _name = 'of.order.line.option'
    _description = u"Option pour les lignes de commande (Achat et Vente)"

    name = fields.Char(string=u"Nom", required=True)
    purchase_price_update = fields.Boolean(string=u"Modification du prix d'achat")
    purchase_price_update_type = fields.Selection(
        selection=[('fixed', u"Montant fixe"),
                   ('percent', u"Pourcentage")], string=u"Type de modification du prix d'achat")
    purchase_price_update_value = fields.Float(string=u"Valeur de modification du prix d'achat")
    sale_price_update = fields.Boolean(string=u"Modification du prix de vente")
    sale_price_update_type = fields.Selection(
        selection=[('fixed', u"Montant fixe"),
                   ('percent', u"Pourcentage")], string=u"Type de modification du prix de vente")
    sale_price_update_value = fields.Float(string=u"Valeur de modification du prix de vente")
    description_update = fields.Text(string=u"Description de la ligne de commande")


import rospy
from std_msgs.msg import Float32
from std_msgs.msg import Int16
import time
a = 0
rospy.init_node('flag', anonymous=True)
def listener():
    while 1:
        flag = raw_input("enter flag: ")
        if flag == "f":
            global a
            a = a+1
            #print(a)
            rospy.loginfo("flag = %d",a)
            start =time.time()
            rospy.loginfo("time = %f ",start)

if __name__ == '__main__':
    listener()


N = int(input())
switch = list(map(int, input().split()))
S = int(input())
s_n = []
s_change = [1, 0]

for i in range(S):
    s, n = map(int, input().split())
    s_n.append([s, n])

for j in range(S):
    if s_n[j][0] == 1:
        for m in range(N):
            if not (m+1) % s_n[j][1]:
                switch[m] = s_change[switch[m]]

    else:
        change_idx =[s_n[j][1]-1]
        for k in range(1, min(N-s_n[j][1]+1 , s_n[j][1])):
            if switch[s_n[j][1]-1 - k] == switch[s_n[j][1]-1 + k]:
                change_idx.extend([s_n[j][1]-1 - k, s_n[j][1]-1 + k])
            else:
                break

        for idx in change_idx:
            switch[idx] = s_change[switch[idx]]

for idx in range(len(switch)):
    if idx !=0 and idx % 20 == 19:
        print(switch[idx])
    else:
        print(switch[idx], end = ' ')


import sys

def check(lst):
    for i in range(0,len(lst)-1):
        if lst[i] in (lst[:i]+lst[(i+1):]):
            return False
    return True

def main():
    file=open('input','r')
    lst=file.readlines()

    for i in range(4,len(lst[0])-1):
        if check(lst[0][i-4:i]):
            print(i)
            break

if __name__=="__main__":
    main()


#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# @author: orleven

from lib.core.env import *
import json
from sqlalchemy import and_
from flask import request
from flask import Blueprint
from lib.core.enums import ApiStatus
from lib.core.model import DNSLog
from lib.core.model import WebLog
from lib.hander import db
from lib.hander.basehander import fix_response
from lib.hander.basehander import login_check

mod = Blueprint('api', __name__, url_prefix=f'{PREFIX_URL}/api')

@mod.route('/dnslog/list', methods=['POST', 'GET'])
@login_check
@fix_response
def api_dnslog_list():
    """获取dnslog信息"""
    response = {
        'data': {
            'res': [],
            'total': 0,
        }
    }
    page = request.json.get('page', 1)
    per_page = request.json.get('per_page', 10)
    domain = request.json.get('domain', '')
    ip = request.json.get('ip', '')
    condition = (1 == 1)

    if ip != '':
        condition = and_(condition, DNSLog.ip.like('%' + ip + '%'))

    if domain != '':
        condition = and_(condition, DNSLog.domain.like('%' + domain + '%'))

    if per_page == 'all':
        for row in db.session.query(DNSLog).filter(condition).all():
            response['data']['res'].append(row.to_json())
    else:
        for row in db.session.query(DNSLog).filter(condition).order_by(
                DNSLog.update_time.desc()).paginate(page=page, per_page=per_page).items:
            response['data']['res'].append(row.to_json())

    response['data']['total'] = db.session.query(DNSLog).filter(condition).count()
    return response

@mod.route('/dnslog/detail', methods=['POST', 'GET'])
@login_check
@fix_response
def api_dnslog_detail():
    """获取dnslog信息"""
    response = {'data': {'res': []}}
    dnslog_id = request.json.get('id', '')
    if dnslog_id != '':
        dnslog = db.session.query(WebLog).filter(WebLog.id == dnslog_id).first()
        if dnslog:
            response['data']['res'].append(dnslog.to_json())
            response['data']['total'] = 1
    return response

@mod.route('/weblog/list', methods=['POST', 'GET'])
@login_check
@fix_response
def api_weblog_list():
    """获取weblog信息"""
    response = {
        'data': {
            'res': [],
            'total': 0,
        }
    }
    page = request.json.get('page', 1)
    per_page = request.json.get('per_page', 10)
    ip = request.json.get('ip', '')
    url = request.json.get('url', '')
    condition = (1 == 1)

    if ip != '':
        condition = and_(condition, WebLog.ip.like('%' + ip + '%'))
    if url != '':
        condition = and_(condition, WebLog.url.like('%' + url + '%'))

    if per_page == 'all':
        for row in db.session.query(WebLog).filter(condition).all():
            response['data']['res'].append(row.to_json())
    else:
        for row in db.session.query(WebLog).filter(condition).order_by(
                WebLog.update_time.desc()).paginate(page=page, per_page=per_page).items:
            response['data']['res'].append(row.to_json())

    response['data']['total'] = db.session.query(WebLog).filter(condition).count()
    return response

@mod.route('/weblog/detail', methods=['POST', 'GET'])
@login_check
@fix_response
def api_weblog_detail():
    """获取weblog信息"""
    response = {'data': {'res': []}}
    weblog_id = request.json.get('id', '')
    if weblog_id != '':
        weblog = db.session.query(WebLog).filter(WebLog.id == weblog_id).first()
        if weblog:
            weblog_dic = {}
            request_headers = json.loads(weblog.request_headers)
            url_temp = weblog.url[weblog.url.replace('://', '___').index('/'):]
            weblog_dic['url'] = weblog.url
            weblog_dic['request'] = weblog.method + ' ' + url_temp + ' ' + weblog.request_http_version + '\r\n'
            weblog_dic['request'] += '\r\n'.join([key + ': ' + value for key, value in request_headers.items()])
            weblog_dic['request'] += '\r\n\r\n'
            weblog_dic['request'] += bytes.decode(weblog.request_content)
            response['data']['res'].append(weblog_dic)
        return response
    return ApiStatus.ERROR_IS_NOT_EXIST


import json
import argparse
from sklearn.metrics import classification_report
from eval_F1 import exact_match_score, f1_score, metric_max_over_ground_truths
import numpy as np

def compute_span_f1(pred_span, gold_span):
    gold_start, gold_end = gold_span
    pred_start, pred_end = pred_span
    tp, fp, fn = 0, 0, 0
    if pred_end >= gold_end:
        tp = gold_end - max(pred_start, gold_start) + 1
    else:
        tp = pred_end - max(pred_start, gold_start) + 1
    tp = max(tp, 0)
    fp = pred_end-pred_start+1-tp
    fn = gold_end-gold_start+1-tp
    precision = tp/(tp+fp)
    recall = tp/(tp+fn)
    if precision == 0.0 and recall == 0.0:
        return 0.0, precision, recall
    F1 = 2*precision*recall/(precision+recall)
    return F1, precision, recall

def score_claim(args):
    print("Evalutating Claim Detection Task")
    gold = json.load(open(args.gold_file))
    predictions = json.load(open(args.predictions_file))

    tp = 0.0
    total_prec = 0.0
    total_rec = 0.0

    for doc_id in gold:
        gold_sents = set(list(gold[doc_id].keys()))
        if doc_id in predictions:
            pred_sents = set(predictions[doc_id])
            tp += len(gold_sents.intersection(pred_sents))
            total_prec += len(pred_sents)

        total_rec += len(gold_sents)

    if total_prec != 0:
        prec = float(tp)/total_prec
    else:
        prec = 0
    rec = float(tp)/total_rec

    print("Prec: ", prec, tp, total_prec)
    print("Rec: ", rec, tp, total_rec)
    if tp != 0:
        print("F1: ", 2*prec*rec/(prec+rec))
    else:
        print("F1: ", 0.0)

def score_topic(args):
    print("Evalutating Topic Classification Task")
    gold = json.load(open(args.gold_file))
    predictions = json.load(open(args.predictions_file))

    gold_topics = list()
    predicted_topics = list()

    for doc_id in gold:
        assert doc_id in predictions
        for segment_id in gold[doc_id]:
            assert segment_id in predictions[doc_id]
            gold_topics.append(gold[doc_id][segment_id]["topic"])
            predicted_topics.append(predictions[doc_id][segment_id]["topic"])

    print(classification_report(gold_topics, predicted_topics, digits=4))

def score_claim_object(args):
    print("Evalutating Claim Object Extraction Task")
    gold = json.load(open(args.gold_file))
    predictions = json.load(open(args.predictions_file))
    EM = 0.0
    F1 = 0.0
    count = 0

    for doc_id in gold:
        for segment_id in gold[doc_id]:
            if "claim_object" in gold[doc_id][segment_id]:
                assert doc_id in predictions
                assert segment_id in predictions[doc_id]

                EM += metric_max_over_ground_truths(exact_match_score, predictions[doc_id][segment_id]["claim_object"], [gold[doc_id][segment_id]["claim_object"]])
                F1 += metric_max_over_ground_truths(f1_score, predictions[doc_id][segment_id]["claim_object"], [gold[doc_id][segment_id]["claim_object"]])
                count += 1

    print("EM: ", EM/count,  "F1: ", F1/count, "Count: ", count)

def score_stance(args):
    print("Evalutating Stance Detection Task")
    gold = json.load(open(args.gold_file))
    predictions = json.load(open(args.predictions_file))
    gold_stance = list()
    predicted_stance = list()

    for doc_id in gold:
        for segment_id in gold[doc_id]:
            if "stance" in gold[doc_id][segment_id]: # and "claim_object" in gold[doc_id][segment_id]:
                assert doc_id in predictions
                assert segment_id in predictions[doc_id]

                gold_stance.append(gold[doc_id][segment_id]["stance"])
                predicted_stance.append(predictions[doc_id][segment_id]["stance"])

    print(classification_report(gold_stance, predicted_stance, digits=4))

def score_claim_span(args):
    print("Evalutating Claim Span Detection Task")
    gold = json.load(open(args.gold_file))
    predictions = json.load(open(args.predictions_file))

    prec = list()
    recall = list()

    for doc_id in gold:
        for segment_id in gold[doc_id]:
            if "claim_span" in gold[doc_id][segment_id]:
                _, ex_prec, ex_recall = compute_span_f1(predictions[doc_id][segment_id]["claim_span"], gold[doc_id][segment_id]["claim_span"])
                prec.append(ex_prec)
                recall.append(ex_recall)

    final_prec = np.mean(prec)
    final_recall = np.mean(recall)
    print("P: ", final_prec, "R: ", final_recall, "F1: ", (2*final_prec*final_recall)/(final_prec+final_recall), "Count: ", len(prec))

def score_claimer(args):
    print("Evaluating Claimer Extraction Task")
    gold = json.load(open(args.gold_file))
    predictions = json.load(open(args.predictions_file))

    in_sent_f1 = list()
    out_of_sent_f1 = list()

    ans_f1 = list()
    no_claimer_tp = 0.0
    no_claimer_total_prec = 0.0
    no_claimer_total_rec = 0.0

    for doc_id in gold:
        for segment_id in gold[doc_id]:
            if "has_claimer" in gold[doc_id][segment_id]:
                assert doc_id in predictions
                assert segment_id in predictions[doc_id]

                if predictions[doc_id][segment_id]["has_claimer"] == False:
                    no_claimer_total_prec += 1.0
                    pred_claimer = "<AUTHOR>"
                else:
                    pred_claimer = predictions[doc_id][segment_id]["claimer"]
                if gold[doc_id][segment_id]["has_claimer"]:
                    if gold[doc_id][segment_id]["claimer_in_sentence"]:
                        in_sent_f1.append(metric_max_over_ground_truths(f1_score, pred_claimer, [gold[doc_id][segment_id]["claimer"]]))
                    else:
                        out_of_sent_f1.append(metric_max_over_ground_truths(f1_score, pred_claimer, [gold[doc_id][segment_id]["claimer"]]))
                    ans_f1.append(metric_max_over_ground_truths(f1_score, pred_claimer, [gold[doc_id][segment_id]["claimer"]]))
                else:
                    if predictions[doc_id][segment_id]["has_claimer"] == False:
                        no_claimer_tp += 1.0
                    no_claimer_total_rec += 1.0

    print("Ans F1: ", np.mean(ans_f1), " Count: ", len(ans_f1))
    prec = no_claimer_tp/no_claimer_total_prec
    recall = no_claimer_tp/no_claimer_total_rec
    no_ans_f1 = 2*prec*recall/(prec + recall)
    print("No ANS P:", prec, "No ANS R:", recall, "No ANS F1:", no_ans_f1, "Count: ", no_claimer_total_rec)
    print("Final F1: ", (len(ans_f1)*np.mean(ans_f1) + no_claimer_total_rec*no_ans_f1)/(len(ans_f1) + no_claimer_total_rec))
    print("In Sent F1: ", np.mean(in_sent_f1), "Count: ", len(in_sent_f1))
    print("Out of Sent F1: ", np.mean(out_of_sent_f1), "Count: ", len(out_of_sent_f1))

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Parser')
    parser.add_argument('--gold_file', type=str, help="Path to gold data")
    parser.add_argument('--predictions_file', type=str, help="path to predictions")
    parser.add_argument("--eval_claim", action='store_true', help="Whether to evaluate claim detection performance")
    parser.add_argument("--eval_topic", action='store_true', help="Whether to evaluate topic detection performance")
    parser.add_argument("--eval_claim_object", action='store_true', help="Whether to evaluate topic detection performance")
    parser.add_argument("--eval_claim_span", action='store_true', help="Whether to evaluate topic detection performance")
    parser.add_argument("--eval_stance", action='store_true', help="Whether to evaluate topic detection performance")
    parser.add_argument("--eval_claimer", action='store_true', help="Whether to evaluate topic detection performance")
    args = parser.parse_args()
    if args.eval_claim:
        score_claim(args)

    if args.eval_topic:
        score_topic(args)

    if args.eval_claim_object:
        score_claim_object(args)

    if args.eval_claim_span:
        score_claim_span(args)

    if args.eval_stance:
        score_stance(args)

    if args.eval_claimer:
        score_claimer(args)


#06 - Write a program that asks the user for a number n and gives them the possibility to choose between computing the sum and computing the product of 1,…,n.

n = int(input("Insert a number:"))
choice = "x"
while choice.lower() != "s" and choice.lower() != "p":
    choice = input("Do you want to compute\nThe sum of 1,...,n[PRESS S]\tThe produtct of 1,...,n[PRESS P]")
if choice.lower() == "s":
    sum = 0
    for i in range (1, n):
        sum = sum + i
    print("The sum of 1,...,", n, "is", sum)

elif choice.lower() == "p":
    product = 1
    for i in range (1, n):
        product = product * i
    print("The product of 1,...,", n, "is", product)


import sys
from cravat import BaseAnnotator
from cravat import InvalidData
import sqlite3
import os

class CravatAnnotator(BaseAnnotator):
    def annotate(self, input_data, secondary_data=None):
        out = {}
        achange = input_data['achange']
        aa_321 = {
        'Asp': 'D', 'Ser': 'S', 'Gln': 'Q', 'Lys': 'K',
        'Trp': 'W', 'Asn': 'N', 'Pro': 'P', 'Thr': 'T',
        'Phe': 'F', 'Ala': 'A', 'Gly': 'G', 'Cys': 'C',
        'Ile': 'I', 'Leu': 'L', 'His': 'H', 'Arg': 'R',
        'Met': 'M', 'Val': 'V', 'Glu': 'E', 'Tyr': 'Y',
        'Ter': '*','':''}
        for key, value in aa_321.items():
            achange = achange.replace(key, value)
        q = 'select id, name, effect, relation, familial, functional, dist, pub, geneB,Ballele_one_cdna, Ballele_one_protein,Ballele_two_cdna, Ballele_two_protein,Aallele_two_cdna, Aallele_two_protein from gene where geneA = "{hugo}" and Aallele_one_protein = "{achange}"'.format(
            hugo = input_data['hugo'], achange = achange)
        self.cursor.execute(q)
        rows = self.cursor.fetchall()
        if rows:
            data = []
            for row in rows:
                idd = row[0]
                name = row[1]
                effect = row[2]
                if row[2] == 'TD':
                    effect = "True Digenic"
                elif row[2] == 'CO':
                    effect = "Composite"
                else:
                    effect = None
                relation = row[3]
                fam = row[4]
                funct = row[5]
                dist = row[6]
                pub = row[7]
                geneB = row[8]
                achangeB = row[10]
                achangeB2 = row[12]
                achangeA2 = row[14]
                for key, value in aa_321.items():
                    achangeB = achangeB.replace(value, key)
                    achangeB2 = achangeB2.replace(value, key)
                    achangeA2 = achangeA2.replace(value, key)
                dida = [idd, name, effect, relation, geneB, achangeB, achangeB2, achangeA2, fam, funct, dist, pub]
                data.append({'id': idd,'name': name, 'effect': effect, 'relation': relation, 'geneB': geneB,'fam':fam,'funct': funct, 'dist': dist, 'pub': pub, 'full': dida})
            if data:
                ids = idd
                out['name'] = name
                out['effect'] = effect
                out['relation'] = relation
                out['fam'] = fam
                out['funct'] = funct
                out['dist'] = dist
                out['pub'] = pub
                all_results_list = [x['full'] for x in data]
                out['all'] = all_results_list
                out['id'] = ids
            return out

    def cleanup(self):
        pass

if __name__ == '__main__':
    annotator = CravatAnnotator(sys.argv)
    annotator.run()


import numpy as np
from sklearn.cluster import DBSCAN
from sklearn import metrics

input_file = ('data_perf.txt')
# Load data 載入資料
x = []
with open(input_file, 'r') as f:
    for line in f.readlines():
        data = [float(i) for i in line.split(',')]
        x.append(data)

X = np.array(x)

# Find the best epsilon 找最佳的epsilon
eps_grid = np.linspace(0.3, 1.2, num=10)
silhouette_scores = []
eps_best = eps_grid[0]
silhouette_score_max = -1
labels_best = None
for eps in eps_grid:
    # Train DBSCAN clustering model 訓練DBSCAN分群模型
    model = DBSCAN(eps=eps, min_samples=5).fit(X)

    # Extract labels 提取標籤
    labels = model.labels_

    # Extract performance metric  提取性能指標
    silhouette_score = round(metrics.silhouette_score(X, labels), 4)
    silhouette_scores.append(silhouette_score)

    print("Epsilon:", eps, " --> silhouette score:", silhouette_score)

    if silhouette_score > silhouette_score_max:
        silhouette_score_max = silhouette_score
        eps_best = eps
        model_best = model
        labels_best = labels

# Best params
print("Best epsilon =", eps_best)

# Associated model and labels for best epsilon
model = model_best
labels = labels_best

# Check for unassigned datapoints in the labels
offset = 0
if -1 in labels:
    offset = 1

# Number of clusters in the data
num_clusters = len(set(labels)) - offset

print("Estimated number of clusters =", num_clusters)

# Extracts the core samples from the trained model
mask_core = np.zeros(labels.shape, dtype=np.bool)
mask_core[model.core_sample_indices_] = True


#!/usr/bin/env python3

import zipfile
import wget
import re

# CoVoST2 translations file
covost2_url = 'https://dl.fbaipublicfiles.com/covost/covost2.zip'

# File with CommonVoice English sentences translated to Catalan by humans
covost2_source_file = 'validated.en_ca.en'

# Output file
covost2_target_file = 'covost2-ca.txt'

# Discarted sentences
discarted_file = 'covost2-ca-discarted.txt'

# char replacements table
source_chars = '´‘’“”«»'
target_chars ='\'\'\'""""'
translation = str.maketrans(source_chars, target_chars)

def normalize_line(line):

    line = line.translate(translation)
    return(line)

def validate_line(line):

    if (re.match("^[a-zA-ZçÇáàèéíïòóóúüÁÀÈÉÍÏÒÓÚÜ·¡¿!\? ,;:.\"'\.\(\)/–-]+\n$", line)):
        return True
    return False

# Download CoVoST2 translations
wget.download(covost2_url)

# Get English to Catalan translated sentences
with zipfile.ZipFile('covost2.zip','r') as zip_ref:
    zip_ref.extract('covost2/' + covost2_source_file)

# Parse translations

# Store lines already seen
lines_seen = set()

with open(covost2_target_file, 'w') as output_file, open(discarted_file, 'w') as removed_file:
    for line in open('covost2/' + covost2_source_file, 'r'):
        # Normalize line
        line = normalize_line(line)
        # Check if line is duplicated
        if line not in lines_seen:
            # Store line as seen
            lines_seen.add(line)
            # If it's a valid sentece, write it
            if validate_line(line):
                 output_file.write(line)
            else:
                 removed_file.write(line)


#Ques - 1 (Index Based Iteration)

lst=[10,346,895,8,4721]
sum=0
for i in range(5):
    sum=sum+lst[i]
print(sum)

#List Based Iteration

sum=0
ist=[10,346,895,8,4721]
for value in ist:
    sum=sum+value
print(sum)


from pprint import pprint

from fukushima_population import data

import json
with open('periodic_table.json', 'r') as f:
    t = json.load(f)

# print(t)

for i in range(len(t["elements"])):
    n = t["elements"][i]["name"]
    sym = t["elements"][i]["symbol"]
    print(f"{n}という元素は{sym}と表記する")

# lookup = {}
# for e in t["elements"]:
#     lookup[e["symbol"]]=e["name"]
#
# lookup = {}

lookup = {e["symbol"]:e["name"] for e in t["elements"]}

symbol = "Cu"
print(f"{symbol} is the symbol for {lookup[symbol]}")


class Banka:
    jazyk = 1
    def __init__(ucet,heslo,suma,pokusy,cislo):
        ucet.heslo = heslo
        ucet.suma = suma
        ucet.pokusy = pokusy
        ucet.cislo = cislo

ucty = list()

file = open("ucty.txt", "r")
a = file.readline()
while a != "":
    b = file.readline()
    c = file.readline()
    d = file.readline()
    ucty.append(Banka(int(a),int(b),int(c),int(d)))
    a = file.readline()

def vypisUcty():
    for i in range (0,len(ucty)):
        print (str(ucty[i].heslo))
    print()
def cisloUctu(cislo):
    for i in range (0,len(ucty)):
        if ucty[i].cislo == cislo: return i
    return -1

def zmenJazyk():
    i = int(input("1.Slovencina\n2.English\n"))
    if (i == 1) or (i == 2):
        Banka.jazyk = i
        if Banka.jazyk == 1: input("Jazyk uspesne zmeneny!\n")
        elif Banka.jazyk == 2: input("Language succesfully changed!\n")
    else:
        print("Invalid choice")

def pristup(i):
    while ucty[i].pokusy > 0 and ucty[i].pokusy < 4:
        if Banka.jazyk == 1: vstup = int(input("Zadajte heslo: "))
        if Banka.jazyk == 2: vstup = int(input("Enter you password: "))
        if int(vstup) == ucty[i].heslo:
            if Banka.jazyk == 1: print("Pristup povoleny!")
            if Banka.jazyk == 2: print("Access granted!")
            ucty[i].pokusy = 4;
        else:
            ucty[i].pokusy -= 1;
            if Banka.jazyk == 1:
                print("Nespravne heslo!")
                print("Pocet zostavajucich pokusov: " + str(ucty[i].pokusy))
            if Banka.jazyk == 2:
                print("Invalid password!")
                print("Remaining attempts: " + str(ucty[i].pokusy))
    if ucty[i].pokusy == 0:
        if Banka.jazyk == 1: input("Zablokovany ucet!")
        if Banka.jazyk == 2: input("Acount blocked!")
        return 0
    else:
        ucty[i].pokusy = 3
        return 1

def vUcte():
    if Banka.jazyk == 1: i = cisloUctu(int(input("Zadajte cislo uctu: ")))
    if Banka.jazyk == 2: i = cisloUctu(int(input("Enter account numer: ")))
    if i == -1:
        if Banka.jazyk == 1: input("Neexistujuci ucet!")
        if Banka.jazyk == 2: input("Non existing account!")
        akcia = 0
    else: akcia = pristup(i)
    if akcia == 0: return
    while akcia > 0:
        if Banka.jazyk == 1: akcia = int(input("1.Vlozit peniaze\n2.Vybrat peniaze\n3.Pozriet zostatok\n0.Odhlasit sa\n"))
        if Banka.jazyk == 2: akcia = int(input("1.Deposit money\n2.Withdraw money\n3.Check balance\n0.Log out\n"))
        if akcia == 0: return
        if akcia == 1:
            if Banka.jazyk == 1: suma = int(input("Zadajte sumu: "))
            if Banka.jazyk == 2: suma = int(input("Enter the amount: "))
            ucty[i].suma += suma
            if Banka.jazyk == 1: print("Zostatok na ucte: " + str(ucty[i].suma) + "€\n")
            if Banka.jazyk == 2: print("Account balance:" + str(ucty[i].suma) + "€\n")
        elif akcia == 2:
            if Banka.jazyk == 1: suma = int(input("Zadajte sumu: "))
            if Banka.jazyk == 2: suma = int(input("Enter the amount: "))
            if ucty[i].suma < suma:
                if Banka.jazyk == 1: input("Nedostatocny kapital, operacia prerusena!\n")
                if Banka.jazyk == 2: input("Insufficient balance, transaction cancelled\n")
            else:
                ucty[i].suma -= suma
                if Banka.jazyk == 1: print("Zostatok na ucte: " + str(ucty[i].suma) + "€\n")
                if Banka.jazyk == 2: print("Account balance:" + str(ucty[i].suma) + "€\n")
        elif akcia == 3:
            if Banka.jazyk == 1: print("Zostatok na ucte: " + str(ucty[i].suma) + "€\n")
            if Banka.jazyk == 2: print("Account balance:" + str(ucty[i].suma) + "€\n")
            input()

def vytvor():
    if Banka.jazyk == 1:
        cislo = int(input("Zadajte cislo Vasho noveho uctu: "))
        heslo = int(input("Zadajte Vas pin kod: "))
    if Banka.jazyk == 2:
        cislo = int(input("Set you new account number: "))
        heslo = int(input("Enter your pin: "))
    ucty.append(Banka(heslo,0,3,cislo))
    if Banka.jazyk == 1: input("Ucet uspesne vytvoreny!")
    if Banka.jazyk == 2: input("Account succesfully created!")

while(1):
    if Banka.jazyk == 1:
        print("Vitajte v Banke")
        akcia = int(input("1. Vstupit do uctu \n2. Vytvorit ucet\n3. Zmenit jazyk\n0. Ukoncit\n"))
    elif Banka.jazyk == 2:
        print("Welcome to the Bank")
        akcia = int(input("1. Entry the account \n2. Create new account\n3. Change language\n0. Exit\n"))
    if akcia == 1:
        vUcte()
    elif akcia == 2:
        vytvor()
    elif akcia == 3:
        zmenJazyk()
    elif akcia == 9:
        vypisUcty()
    elif akcia == 0:
        break

file = open("ucty.txt", "w")
for i in range (0,len(ucty)):
    file.write(str(ucty[i].heslo) + "\n" + str(ucty[i].suma) + "\n" + str(ucty[i].pokusy) + "\n" +  str(ucty[i].cislo) + "\n")
file.close()
if Banka.jazyk == 1: print("Dovidenia!")
elif Banka.jazyk == 2: print("See you soon!")


import argparse
import glob
import logging
import os
import random
import re
import shutil
import stat
import subprocess

# Path to the directory containing this source file.
SRC_DIR = os.path.dirname(os.path.realpath(__file__))
BINARIES_DIR = os.path.join(SRC_DIR, 'binaries')

def generate_shaders(no_of_files, output_dir, verbose=False):
  """Generate at least |no_of_files| number of shaders using wgslsmith and write them
  to |output_dir|."""

  i = 0
  while i < no_of_files:
    wgslsmith_command = [ os.path.join(BINARIES_DIR, "wgslsmith"), "--preset", "tint", "--recondition" ]
    try:
      proc = subprocess.Popen(wgslsmith_command, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
      stdout, stderr = proc.communicate()
      if proc.returncode != 0:
        continue
      with open(os.path.join(output_dir, "fuzz-" + str(i) + ".wgsl"), "w") as outfile:
        outfile.write(stdout.decode('utf-8'))
    except subprocess.TimeoutExpired:
      continue
    i = i + 1

def main():
  """Parse arguments and generate WGSL shaders using wgslsmith."""
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--no_of_files',
      type=int,
      required=True,
      help='Number of WGSL shaders to generate.')
  parser.add_argument(
      '--input_dir',
      required=False,
      help='Ignored. Needed to support the ClusterFuzz fuzzer '
      'API.')
  parser.add_argument(
      '--output_dir',
      required=True,
      help='Directory to write WGSL shaders.')
  parser.add_argument(
      '--verbose',
      action='store_true',
      default=False,
      help='Use INFO log-level.')
  parser.add_argument(
      '--random_seed',
      type=int,
      default=0,
      help='Ignored. Needed to support the ClusterFuzz fuzzer.')

  opts = parser.parse_args()
  if opts.random_seed:
    random.seed(opts.random_seed)

  if opts.verbose:
    logging.basicConfig(level=logging.INFO)

  generate_shaders(opts.no_of_files, opts.output_dir, opts.verbose)

if __name__ == '__main__':
  main()


class tree_node:
    def __init__(self,data):
        self.data=data
        self.right=None
        self.left=None
class link_binary_search_tree:
    def __init__(self):
        self.root=None
    def search_tree_add_data(self,data):
        temp=self.root
        if not temp:
            self.root=tree_node(data)
            return
        q=[temp]
        new=tree_node(data)
        while len(q):
            temp=q.pop(0)
            if temp.left:
                q.append(temp.left)
            else:
                if data<temp.data:
                    temp.left=new
                    break
            if temp.right:
                q.append(temp.right)
            else:
                if data>=temp.data:
                    temp.right=new
                    break
    def serch_inorder_tree(self,root):
        if root:
            self.serch_inorder_tree(root.left)
            print(root.data)
            self.serch_inorder_tree(root.right)
    def serch_postrder_tree(self,root):
        if root:
            self.serch_postrder_tree(root.left)
            self.serch_postrder_tree(root.right)
            print(root.data)
    def serch_preorder_tree(self,root):
        if root:
            print(root.data)
            self.serch_preorder_tree(root.left)
            self.serch_preorder_tree(root.right)
    def search_delete_data(self,data):
        temp=self.root
        if not temp:
            return
        q=[temp]
        while len(q):
            temp=q.pop(0)
            pass
    def print_search_tree(self):
        temp=self.root
        if not temp:
            return
        q=[temp]
        while len(q):
            temp=q.pop(0)
            print(temp.data)
            if temp.left:
                q.append(temp.left)
            if temp.right:
                q.append(temp.right)
binary_serch_tree=link_binary_search_tree()
binary_serch_tree.search_tree_add_data(50)
binary_serch_tree.search_tree_add_data(30)
binary_serch_tree.search_tree_add_data(20)
binary_serch_tree.search_tree_add_data(40)
binary_serch_tree.search_tree_add_data(70)
binary_serch_tree.search_tree_add_data(60)
binary_serch_tree.search_tree_add_data(80)
#binary_serch_tree.print_search_tree()
binary_serch_tree.serch_inorder_tree(binary_serch_tree.root)