from openerp.osv import osv, fields
import datetime
import openerp.addons.decimal_precision as dp
import time
from openerp.tools.translate import _
from openerp import SUPERUSER_ID, netsvc, api
from openerp.exceptions import ValidationError
from openerp.tools.sql import drop_view_if_exists
from openerp.addons.base_suspend_security.base_suspend_security import BaseSuspendSecurityUid

import logging

_logger = logging.getLogger(__name__)

_ESTADO_HISTORIAL_RESP = [('AC', 'Aceptado'), ('EP', 'En proceso'), ('RE', 'Rechazado')]

class grp_compras_solicitud_recursos(osv.osv):
    _inherit = ['mail.thread']
    _description = 'Solicitud de Recursos'
    _name = 'grp.compras.solicitud.recursos'

    _mail_post_access = 'read'

    def _get_solicitante_editable(self, cr, uid, ids, name, args, context=None):
        res = {}
        in_grp = self.pool.get('res.users').has_group(cr, uid, 'grp_seguridad.grp_compras_sr_Solicitante')
        for record in self.browse(cr, uid, ids, context=context):
            res[record.id] = (record.state in ('inicio', 'nuevo')) and in_grp
        return res

    def _get_aprobador_editable(self, cr, uid, ids, name, args, context=None):
        res = {}
        in_grp = self.pool.get('res.users').has_group(cr, uid, 'grp_seguridad.grp_compras_sr_Aprobador')
        for record in self.browse(cr, uid, ids, context=context):
            res[record.id] = (record.state in ('en_aprobacion')) and in_grp
        return res

    def _get_almacenero_editable(self, cr, uid, ids, name, args, context=None):
        res = {}
        in_grp = self.pool.get('res.users').has_group(cr, uid,
                                                      'grp_seguridad.grp_compras_sr_Encargado_de_almacen')
        for record in self.browse(cr, uid, ids, context=context):
            res[record.id] = (record.state in ('codificando', 'esperando_almacen')) and in_grp
        return res

    LISTA_ESTADOS_SOLICITUD = [
        ('inicio', 'Borrador'),
        ('en_aprobacion', u'En aprobación'),
        ('rechazado', 'Rechazado'),
        ('codificando', u'Aprobado'),
        ('esperando_almacen', u'En Proceso'),
        ('aprobado', 'Cerrado'),
    ]

    ##################################################
    ### FUNCIÓN QUE CREA LAS SC A PARTIR DE LAS SR ###
    ##################################################
    def merge_sr(self, cr, uid, ids, context=None):
        '''
             @return: purchase order view
        '''
        solicitud_obj = self.pool.get('grp.solicitud.compra')
        solicitud_obj.do_merge_sr(cr, uid, context.get('active_ids', []), context)
        return True

    def onchange_solicitante_id(self, cr, uid, ids, solicitante_id):
        value = {}
        if solicitante_id:
            value = {'department_id': self.pool.get('res.users').get_departamento(cr, uid, solicitante_id)}
        return {'value': value}

    # PCARBALLO -- Funciones get y set para el campo funcional presup_view_line_copy, de tipo one2many
    def _get_lineas_presup_line(self, cr, uid, ids, fields, args, context=None):
        line_obj = self.pool.get('grp.compras.solicitud.recursos.line.sr')
        res = {}
        for obj in self.browse(cr, uid, ids):
            args = [('estado', 'in', ['enaprobcom']), ('grp_id', '=', obj.id)]
            if obj.ir_filter_id:
                args += eval(obj.ir_filter_id.domain)
            line_ids = line_obj.search(cr, uid, args)
            res[obj.id] = line_ids
        return res

    def _set_lineas_presup_line(self, cr, uid, id, name, value, inv_arg, context):
        line_obj = self.pool.get('grp.compras.solicitud.recursos.line.sr')
        for line in value:
            if line[0] == 1:  # one2many Update
                line_id = line[1]
                line_obj.write(cr, uid, [line_id], line[2])
        return True

    def crear_solicitud_compra(self, cr, uid, ids, context=None):
        sc_pool = self.pool.get('grp.solicitud.compra')
        for lines in self.browse(cr, uid, ids, context=context)[0].grp_sr_id:
            if not sc_pool.search(cr, uid, [('linea_solicitud_recursos_id','=',lines.id)], context=context):
                sc_pool._crear_solicitud_linea(cr, uid, lines, context=context)
        return True

    # def _get_tipo_sr(self, cr, uid, context=None):
    #     res = [
    #         ('I', 'Insumos/Materiales'),
    #         ('S', 'Servicios'),
    #         ('AF', 'Activo Fijo'),
    #         ('PL', 'Planificada'),
    #     ]
    #     return res

    def _get_lista_tipo_sr(self, cr, uid, context=None):
        es_solic_planif = self.pool.get('res.users').has_group(cr, uid,
                                                               'grp_compras_estatales.grp_sr_solicitante_planif')
        es_solic_comun = self.pool.get('res.users').has_group(cr, uid, 'grp_seguridad.grp_compras_sr_Solicitante')
        select = [
            ('I', 'Insumos/Materiales'),
            ('S', 'Servicios'),
            ('AF', 'Activo Fijo'),
            ('PL', 'Planificada')
        ]
        if es_solic_comun and not es_solic_planif:
            select = [
                ('I', 'Insumos/Materiales'),
                ('S', 'Servicios'),
                ('AF', 'Activo Fijo')
            ]
        elif es_solic_planif and not es_solic_comun:
            select = [
                ('PL', 'Planificada')
            ]
        return select

    _columns = {
        'name': fields.char('Nro. Solicitud', size=32),  # Nro Solicitud
        'grp_sr_id': fields.one2many('grp.compras.solicitud.recursos.line.sr', 'grp_id', 'Solicitud'),
        'solicitante_id': fields.many2one('res.users', 'Solicitante', readonly=True, copy=False),

        'ir_filter_id': fields.many2one('ir.filters', 'Additional Filter',
                                        domain=[('model_id', '=', 'grp.compras.solicitud.recursos.line.sr')]),

        'presup_view_line_copy': fields.function(_get_lineas_presup_line, fnct_inv=_set_lineas_presup_line,
                                                 relation="grp.compras.solicitud.recursos.line.sr", type="one2many",
                                                 string="Presupuesto"),
        'warehouse': fields.many2one('stock.warehouse', u'Almacén'),

        'encargado_ids': fields.related('warehouse', 'encargado_ids', type="many2many", relation="res.users",
                                       string=u'Encargados de almacén', readonly=True),
        'aprobador_id': fields.many2one('res.users', 'Aprobador', readonly=True, copy=False),
        'company_id': fields.many2one('res.company', u'Compañía', readonly=True),
        'department_id': fields.many2one('hr.department', string=u'Departamento', readonly=True, copy=False),
        'state': fields.selection(LISTA_ESTADOS_SOLICITUD, 'Estado', size=20, track_visibility='onchange'),
        'inciso': fields.char('Inciso'),
        'u_e': fields.char('UE'),
        'tipo_sr': fields.selection(_get_lista_tipo_sr, string='Tipo SR', required=True),  # Estado
        'date_start': fields.date('Fecha de Solicitud', copy=False),  # Fecha de solicitud
        'description': fields.text(u'Observaciones', size=250),  # Descripcion
        'solicitante_editable': fields.function(_get_solicitante_editable, type='boolean', store=False),
        'aprobador_editable': fields.function(_get_aprobador_editable, type='boolean', store=False),
        'almacenero_editable': fields.function(_get_almacenero_editable, type='boolean', store=False),
    }

    ######################## Estados ####################################

    _defaults = {
        'state': 'inicio',
        'name': 'SR-Borrador',
        'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'product.template',
                                                                                context=c),
        'solicitante_id': lambda s, cr, uid, c: uid,
        'department_id': lambda s, cr, uid, c: s.pool.get('res.users').get_departamento(cr, uid, uid),
        'date_start': fields.date.context_today,
        'solicitante_editable': True,
        'aprobador_editable': True,
        'almacenero_editable': False
    }

    @api.constrains('tipo_sr','grp_sr_id')
    def _check_grp_sr_id(self):
        reception_obj = self.env['grp.fecha.planificada']
        for record in self:
            pos_line = 0
            for line in record.grp_sr_id:
                pos_line += 1
                if line.product_id:
                    #Servicios
                    if record.tipo_sr == 'S' and line.product_id.type != 'service':
                        raise ValidationError(u"En la línea %s de la SR, el tipo del producto %s no corresponde con el tipo de la SR. Debe corregir esto para continuar." % (str(pos_line), line.product_id.name))
                    #Activo Fijo
                    if record.tipo_sr == 'AF' and not line.product_id.categ_id.activo_fijo:
                        raise ValidationError(u"En la línea %s de la SR, el tipo del producto %s no corresponde con el tipo de la SR. Debe corregir esto para continuar." % (str(pos_line), line.product_id.name))
                    #Insumos/Materiales
                    if record.tipo_sr == 'I' and (line.product_id.type not in ['consu', 'product'] or line.product_id.categ_id.activo_fijo):
                        raise ValidationError(u"En la línea %s de la SR, el tipo del producto %s no corresponde con el tipo de la SR. Debe corregir esto para continuar." % (str(pos_line), line.product_id.name))
                #Planificada
                if record.tipo_sr == 'PL':
                    date_start = datetime.datetime.strptime(record.date_start, '%Y-%m-%d')
                    reception_rows = reception_obj.search([('year', '=', str(date_start.year))])
                    if reception_rows:
                        for reception in reception_obj.search([('year', '=', str(date_start.year))]):
                            if record.date_start > reception.reception_date:
                                raise ValidationError(u"No se puede crear una SR planificada con una fecha de solicitud posterior a la fecha de recepción correspondiente.")
                    else:
                        raise ValidationError(u"No se puede crear una SR planificada sin tener configurada una fecha de solicitud planificada válida.")

class grp_compras_solicitud_recursos_almacen(osv.osv):
    _name = 'grp.compras.solicitud.recursos.almacen'
    _description = 'Solicitud de Recursos en almacen'
    _inherits = {'grp.compras.solicitud.recursos': 'sr_id'}
    _inherit = ['mail.thread']
    _order = "id desc"

    _mail_post_access = 'read'

    def _domain_almacenero(self, cr, uid, ids, d1, d2, context=None):
        in_grp = self.pool.get('res.users').has_group(cr, uid,
                                                      'grp_seguridad.grp_compras_sr_Encargado_de_almacen')
        res = {}
        for lines in self.browse(cr, uid, ids, context=None):
            res[lines.id] = in_grp and lines.state in ['codificando', 'esperando_almacen']
        return res

    def _cumple_stock(self, cursor, user, ids, name, arg, context=None):
        res = {}
        for sr_almacen in self.browse(cursor, user, ids, context=context):
            res[sr_almacen.id] = all(line.cantidad_pendiente == 0 for line in sr_almacen.grp_sr_id)
        return res

    def onchange_tipo_sr(self, cr, uid, ids, tipo_sr, context=None):
        """Cuando cambia la ubicacion, actualizo el valor del almacen"""
        return {
            'value': {'ubicacion': False},
            'domain': {'ubicacion': [
                ('active','=',True),
                ('usage','=','internal')
            ]}}

    @api.onchange('ubicacion')
    def _onchange_ubicacion(self):
        self.warehouse = False
        # self.warehouse_view = False
        self.encargado_ids = False
        if self.ubicacion:
            _w = self.ubicacion.get_warehouse(self.ubicacion)
            if _w:
                self.warehouse = _w
                # self.warehouse_view = _w
                self.encargado_ids = self.env['stock.warehouse'].browse(_w).encargado_ids

    @api.depends('warehouse')
    def _compute_warehouse_view(self):
        for row in self:
            row.warehouse_view = row.warehouse

    def _get_copy(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for rec in self.browse(cr, uid, ids, context=context):
            res[rec.id] = rec.warehouse.id
        return res

    def button_solicitar_aprobacion(self, cr, uid, ids, context=None):
        sr_ids = self.browse(cr, uid, ids[0], context=context)[0].grp_sr_id
        for sr_line in sr_ids:
            if (sr_line.cantidad_pendiente > 0):
                self.pool.get('grp.compras.solicitud.recursos.line.sr').write(cr, uid, sr_line.id,
                                                                              {'boton_check': True})

        cr.execute("""  select partner_id from res_users where id = %s""",
                   (uid,))
        cli = cr.fetchone()[0]
        subject = u'SR Cambio de Sub-estado'
        message = self.pool.get('mail.message')
        message.create(cr, uid, {
            'res_id': ids[0],
            'date': time.strftime('%Y-%m-%d %H:%M:%S'),
            'author_id': cli,
            'model': self._name,
            'subject': subject,
            'body': u'Solicitud de Recursos ha cambiado al sub-estado: En Aprobación Compras',
        }, context=context)
        self.write(cr, uid, ids, {'check_button_solicitar': True, 'estado_en_proc': 'eaprobcomp'})
        return True

    def _get_en_aprobacion(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for rec in self.browse(cr, uid, ids, context=context):
            acum = False
            for item in rec.grp_sr_id:
                acum = acum or item.boton_check
            res[rec.id] =  acum
        return res

    _columns = {
        'sr_id': fields.many2one('grp.compras.solicitud.recursos', required=True, ondelete='cascade'),
        'warehouse_view': fields.many2one('stock.warehouse', string=u"Almacén", compute='_compute_warehouse_view', readonly=True),
        'ubicacion': fields.many2one('stock.location', u'Ubicación', domain="[('active','=',True),('usage','=','internal')]"),
        'check_aprobacion': fields.function(_get_en_aprobacion, type='boolean',
                                            string=u"Tiene lineas en aprobacion de compra?"),
        'check_button_solicitar': fields.boolean('Ya se presiono el boton de solicitar?'),
        'director': fields.boolean('Director'),
        # condicion para ocultar/mostrar botones
        'domain_almacenero': fields.function(_domain_almacenero, type='boolean', store=False),
        'cumple_stock': fields.function(_cumple_stock, string='Cumple stock', type='boolean'),
        'grp_sr_af_id': fields.one2many('grp.compras.solicitud.recursos.line.sr.af', 'grp_id', 'Solicitud'),
        'description': fields.char(u'Observaciones', size=500),
        'estado_en_proc': fields.selection([
                                                ('eprocalm', u'En proceso almacén'),
                                                ('eaprobcomp', u'En Aprobación Compras'),
                                                ('envcomp', u'Enviado a Depto de compras'),
                                                ('pendrecep', u'Pendiente Recepción'),
                                            ], "Estado en proceso"),
    }

    _defaults = {
        'check_aprobacion': False,
        'check_button_solicitar': False,
        'state': 'inicio',
        'solicitante_id': lambda self, cr, uid, context: uid
    }

    def button_cumplir_af(self, cr, uid, ids, context):
        return {
            'name': _('Activos Fijos'),
            'res_model': 'account.asset.asset',
            'type': 'ir.actions.act_window',
            'view_id': False,
            'view_mode': 'tree,form',
            'view_type': 'form',
            'limit': 80,
        }

    def onchange_solicitante_id(self, cr, uid, ids, solicitante_id):
        return self.pool.get('grp.compras.solicitud.recursos').onchange_solicitante_id(cr, uid, ids, solicitante_id)

    # Generacion de solicitudes de compra al estar aprobada la SR (almacen)
    def sr_almacen_aprobada(self, cr, uid, ids, context=None):
        grp_compras_sr_obj = self.pool.get('grp.compras.solicitud.recursos')
        for sr_almacen in self.browse(cr, uid, ids, context=context):
            if not grp_compras_sr_obj.crear_solicitud_compra(cr, uid, [sr_almacen.sr_id.id], context=context):
                return False
        return True

    def unlink(self, cr, uid, ids, context=None):
        sr_almacen_ids = self.browse(cr, uid, ids, context=context)
        sr_a_borrar = len(sr_almacen_ids)
        for sr_almacen in sr_almacen_ids:
            if sr_almacen.state in ('inicio', 'nuevo'):
                super(grp_compras_solicitud_recursos_almacen, self).unlink(cr, uid, ids, context=context)
                sr_a_borrar -= 1
        if sr_a_borrar > 0:
            raise osv.except_osv('Error!', u'Solamente de puede eliminar las SR en estado borrador')
        else:
            return True

    # al pasar el estado a 'en_aprobacion' se asigna nuevo número de SR
    # Se envian mensajes
    def act_sr_almacen_aprobacion(self, cr, uid, ids, context=None):
        _logger.info('act_sr_almacen_aprobacion')
        vals = {}
        context = dict(context or {})
        for r in self.browse(cr, uid, ids, context=context):
            if not r.grp_sr_id:
                raise osv.except_osv(_('Error!'), _('No puede enviar a aprobar una solicitud que no tiene líneas.'))
            vals['state'] = 'en_aprobacion'
            fiscalyear_obj = self.pool.get('account.fiscalyear')
            uid_company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
            fecha_hoy = r.date_start
            if fecha_hoy:
                fiscal_year_id = fiscalyear_obj.search(cr, uid,
                                                       [('date_start', '<=', fecha_hoy), ('date_stop', '>=', fecha_hoy),
                                                        ('company_id', '=', uid_company_id)], context=context)
                fiscal_year_id = fiscal_year_id and fiscal_year_id[0] or False
                context.update({'fiscalyear_id': fiscal_year_id})
            sequence = self.pool.get('ir.sequence').get(cr, uid, 'resource.requisition.number', context=context)
            ind= sequence.index('-') +1
            inciso = self.pool.get('res.company').browse(cr, uid, uid_company_id, context=context).inciso
            vals['name'] =  sequence[0:ind] +inciso + sequence[ind:len(sequence)]
            self.write(cr, uid, [r.id], vals, context=context)
            self.send_notification(cr, uid, r, u'En aprobación', context=context)
        return True

    def send_notification(self, cr, uid, rs, state, context=None):
        rs_row = self.browse(cr, uid, rs.id, context=context)
        group_id = self.pool.get('res.groups').get_group_by_ref(cr, uid,
            rs_row.tipo_sr == 'PL' and 'grp_compras_estatales.grp_sr_solicitante_planif' or 'grp_seguridad.grp_compras_sr_Aprobador')
        if group_id:
            partner_ids = [
                user_ingroup.partner_id.id for user_ingroup in self.pool.get('res.groups').browse(cr, uid, group_id[0], context=context).users \
                if rs_row.department_id and rs_row.department_id.id in [e.department_id.id for e in user_ingroup.employee_ids]
            ]
            if partner_ids:
                msg = _(u"Solicitud de recursos en almacén Nro %s cambiado a estado %s por usuario %s") % \
                        (rs_row.name, state, self.pool.get('res.users').browse(cr, uid, uid, context=context).name)
                self.message_post(cr, uid, [rs_row.id], body=msg, type='notification', subtype='mt_comment',
                                partner_ids=partner_ids, context=context)

    # Se debe poner en el campo 'aprobador' el nombre de quien aprueba
    def act_sr_almacen_codificando(self, cr, uid, ids, context=None):
        lines_pool = self.pool.get('grp.compras.solicitud.recursos.line.sr')
        sc_pool = self.pool.get('grp.solicitud.compra')
        for r in self.browse(cr, uid, ids, context=context):
            self.write(cr, uid, [r.id],
                       {'date_approved': time.strftime('%Y-%m-%d'), 'state': 'codificando', 'aprobador_id': uid},
                       context=context)

            ## Crear de forma automática una solicitud de compras para las líneas
            ## con productos consumibles no inventariables o productos servicios
            for line in r.grp_sr_id:
                if line.product_id and \
                  (line.product_id.type=='service' or (line.product_id.type=='consu' and line.product_id.no_inventory)):
                    if line.cantidad_pedida < 0:
                        raise osv.except_osv('Error!', u'No se puede ingresar una cantidad pedida negativa.')
                    elif line.cantidad_pedida + line.cantidad_entregada + line.cantidad_en_pedido > line.cantidad_solicitada:
                        raise osv.except_osv('Error!', u'No se puede pedir mayor cantidad a la solicitada (' + unicode(line.product_id.name) + u')')

                    lines_pool.write(cr, SUPERUSER_ID, [line.id],
                                     {'cantidad_pedida': max(0, line.cantidad_solicitada - line.cantidad_entregada - line.cantidad_en_pedido)},
                                     {'solicitar_compra': True})

                    ctx = dict(context or {}, es_wizard=True)
                    sc_pool._crear_solicitud_linea(cr, uid, line, context=ctx)
                    lines_pool.write(cr, SUPERUSER_ID, [line.id], {'cantidad_en_pedido': line.cantidad_pedida + line.cantidad_en_pedido, 'boton_check': False, 'estado': 'acompra'})

        return True

    def act_sr_almacen_esperando_almacen(self, cr, uid, ids, context=None):
        sc_pool = self.pool.get('grp.solicitud.compra')
        for r in self.browse(cr, uid, ids, context=context):
            __uid = uid
            if (hasattr(r, 'solicitar_compra') and r.solicitar_compra) or r.all_products_no_stockable():
                __uid = BaseSuspendSecurityUid(uid)
            if r.tipo_sr in ['AF']:
                lista = []
                for line in r.grp_sr_id:
                    if line.product_id.id:
                        #se crea tantas lineas como la cantidad solicitada
                        for x in range(int(line.cantidad_solicitada)):
                            lista.append((0, 0, {'product_id': line.product_id.id}))

                if lista:
                    fields = {
                        'grp_sr_af_id': lista,
                    }
                    self.write(cr, __uid, [r.id], fields,
                               context=context)

            if r.tipo_sr not in ['S']:
                cr.execute("""  select partner_id from res_users where id = %s""",
                           (uid,))
                cli = cr.fetchone()[0]
                subject = u'SR Cambio de Sub-estado'
                message = self.pool.get('mail.message')
                message.create(cr, __uid, {
                    'res_id': ids[0],
                    'date': time.strftime('%Y-%m-%d %H:%M:%S'),
                    'author_id': cli,
                    'model': self._name,
                    'subject': subject,
                    'body': u'Solicitud de Recursos ha cambiado al sub-estado: En Proceso Almacenero',
                }, context=context)
                self.write(cr, __uid, [r.id], {'state': 'esperando_almacen', 'estado_en_proc': 'eprocalm'},
                           context=context)
            else:
                cr.execute("""  select partner_id from res_users where id = %s""",
                           (uid,))
                cli = cr.fetchone()[0]
                subject = u'SR Cambio de Sub-estado'
                message = self.pool.get('mail.message')
                message.create(cr, __uid, {
                    'res_id': ids[0],
                    'date': time.strftime('%Y-%m-%d %H:%M:%S'),
                    'author_id': cli,
                    'model': self._name,
                    'subject': subject,
                    'body': u'Solicitud de Recursos ha cambiado al sub-estado: Enviado a Compras',
                }, context=context)
                self.write(cr, __uid, [r.id], {'state': 'esperando_almacen', 'estado_en_proc': 'envcomp'},
                           context=context)

            # Si la SR tiene una solicitudes de compras asociadas, se deben actualizar
            # los campos “producto” (product_id)  y “cantidad solicitada” (cantidad_solicitada)
            # de la SC con los datos de la SR de estos productos.
            for line in r.grp_sr_id:
                sc_ids = sc_pool.search(cr, __uid, [('linea_solicitud_recursos_id','=',line.id)], context=context)
                if sc_ids:
                    vals_updated = sc_pool._prepare_crear_solicitud_linea(cr, __uid, line)
                    vals_updated.pop('linea_solicitud_recursos_id', None)
                    vals_updated.pop('company_id', None)
                    sc_pool.write(cr, __uid, sc_ids, vals_updated, context=context)

        return True

    # Se debe poner en el campo 'aprobador' el nombre de quien rechaza
    def act_sr_almacen_rechazado(self, cr, uid, ids, context=None):
        self.write(cr, uid, ids, {'state': 'rechazado', 'aprobador_id': uid}, context=context)
        return True

    def act_sr_almacen_aprobado(self, cr, uid, ids, context=None):
        self.write(cr, uid, ids, {'state': 'aprobado'}, context=context)
        return True

    def get_tipo_sc(self, cr, uid, ids, context=None):
        s_r = self.browse(cr, uid, ids, context=context)[0]
        return s_r.tipo_sr == 'PL'

    def all_products_no_stockable(self, cr, uid, ids, context=None):
        for sr in self.browse(cr, uid, ids, context=context):
            if not sr.grp_sr_id:
                return False
            for line in sr.grp_sr_id:
                if not line.product_id:
                    return False
                if line.product_id.type in ('product','consu') and \
                   not line.product_id.no_inventory:
                    return False
        return True

    def has_stockable_products(self, cr, uid, ids, context=None):
        for sr in self.browse(cr, uid, ids, context=context):
            for line in sr.grp_sr_id:
                if line.product_id and line.product_id.type in ('product','consu') and \
                   not line.product_id.no_inventory:
                    return True
        return False

    def act_sr_generar_sc(self, cr, uid, ids, context=None):
        for r in self.browse(cr, uid, ids, context=context):
            if not self.sr_almacen_aprobada(cr, uid, ids, context=context):
                raise osv.except_osv('Error!', u'Generando SC desde SR almacen')
            if r.tipo_sr == 'S':
                self.change_en_aprobacion_a_almacen_en_proceso(cr, uid, r.id, context=context)
        return True

    def sr_aprobar_planificada(self, cr, uid, ids, context=None):
        for r in self.browse(cr, uid, ids, context=context):
            self.act_sr_generar_sc(cr, uid, [r.id], context=context)
            wf_service = netsvc.LocalService("workflow")
            wf_service.trg_validate(uid, 'grp.compras.solicitud.recursos.almacen', r.id,
                                    'aprobar_planificada', cr)
        return True

    def change_en_aprobacion_a_almacen_en_proceso(self, cr, uid, id, context=None):
        if not isinstance(id, (list,)):
            id = [id]
        s_r = self.browse(cr, uid, id[0], context=context)
        for line in s_r.grp_sr_id:
            line.write({'estado': 'acompra'})
        self.write(cr, uid, id, {'state': 'esperando_almacen', 'estado_en_proc': 'envcomp'}, context=context)
        return True

    def act_sr_cerrar_almacen(self, cr, uid, ids, context=None):
        self.write(cr, uid, ids, {'state': 'aprobado'}, context=context)
        return True

    def trans_sr_almacen_codificando_esperando_almacen(self, cr, uid, ids, context=None):
        if not ids or len(ids) < 1:  # No refiere a registro alguno
            return True
        sr_brw = self.browse(cr, uid, ids, context=context)
        if not sr_brw or len(sr_brw) < 1:  # No encuentra registros con esos ids
            return True
        for line in sr_brw[0].grp_sr_id:  # Se asume que solamente retorna una fila
            if not line.product_id:
                raise osv.except_osv('Error!',
                                     u'Una línea no tiene producto seleccionado! Seleccione el producto antes de realizar el pedido (' + unicode(
                                         line.descripcion) + u')')
        #Para SR de Insumos/materiales se controla que quien valide sea encargado del almacen de la SR
        if sr_brw.tipo_sr == 'I':
            encargados = [e.id for e in sr_brw.encargado_ids]
            if uid not in encargados:
                raise osv.except_osv('Error!', u"No se ha podido validar esta solicitud porque usted no se encuentra configurado "
                                               u"como Encargado/a de Almacén de la ubicación seleccionada. "
                                               u"Por favor revise la ubicación o contacte al Encargado/a de Almacén correspondiente")
        return True

    def button_solicitar_compra(self, cr, uid, ids, context=None):
        s_r = self.browse(cr, uid, ids, context=context)[0]
        lines_pool = self.pool.get('grp.compras.solicitud.recursos.line.sr')
        for lines in s_r.grp_sr_id:
            lines_pool.write(cr, SUPERUSER_ID, [lines.id],
                             {'cantidad_pedida': max(0, lines.cantidad_solicitada - lines.cantidad_entregada - lines.cantidad_en_pedido)},
                             {'solicitar_compra': True})

        wizard_pool = self.pool.get('grp.solicitar.compra')
        solicitud = s_r
        wizard_id = wizard_pool.create(cr, uid, {'solicitud_id': solicitud.id}, context=context)
        model_pool = self.pool.get('ir.model.data')
        id2 = model_pool._get_id(cr, uid, 'grp_compras_estatales', 'view_solicitud_compra_wizard')
        if id2:
            id2 = model_pool.browse(cr, uid, id2, context=context).res_id
        return {
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'grp.solicitar.compra',
            'res_id': wizard_id,
            'views': [(id2, 'form')],
            'type': 'ir.actions.act_window',
            'target': 'new'
        }

    def _prepare_move_line(self, cr, uid, ids, cabezal, lineas_solicitud, location_dest_id, pick_type_id, context=None):
        return {
            'product_id': lineas_solicitud.product_id.id,
            'product_uom_qty': lineas_solicitud.cantidad_pendiente,
            'cantidad_solicitada': lineas_solicitud.cantidad_solicitada,
            'product_uom': lineas_solicitud.uom_id.id,
            'location_dest_id': location_dest_id,
            'location_id': cabezal.ubicacion and cabezal.ubicacion.id or False,
            'picking_type_id': pick_type_id,
            'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'date_expected': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'name': lineas_solicitud.descripcion,
            'invoice_state': 'none',
            'procure_method': 'make_to_stock',
            'state': 'draft',
            'line_sr_id': lineas_solicitud.id,
        }

    def button_view_pickings(self, cr, uid, ids, context=None):
        return {
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'stock.picking',
            'domain': [('doc_origin', 'in', ids)]
        }

    def button_view_pedidos(self, cr, uid, ids, context=None):
        linea_pedido_obj = self.pool.get('grp.linea.pedido.compra')
        solicitud_compra_obj = self.pool.get('grp.solicitud.compra')
        sc_ids = solicitud_compra_obj.search(cr, uid, [('solicitud_recursos_id','in',ids)], context=context)
        line_pc_ids = linea_pedido_obj.search(cr, uid, [('solicitud_compra_id','in',sc_ids)], context=context)
        pedido_ids = []
        for linea in linea_pedido_obj.browse(cr, uid, line_pc_ids, context=context):
            if linea.pedido_compra_id.id not in pedido_ids:
                pedido_ids.append(linea.pedido_compra_id.id)
        if not pedido_ids:
            raise ValidationError(u"No existen pedidos de compra para esta SR")
        return {
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'grp.pedido.compra',
            'domain': [('id', 'in', pedido_ids)]
        }

    def button_cumplir_stock(self, cr, uid, ids, context=None):
        stock_picking_type = self.pool.get('stock.picking.type')
        cabezal = self.browse(cr, uid, ids[0], context=context)
        view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'view_picking_form')
        view_id = view_ref and view_ref[1] or False
        if not ids:
            return False
        ctx = {}
        if context:
            ctx = context.copy()
        ctx['default_origin'] = cabezal.sr_id.name
        ctx['default_doc_origin'] = cabezal.id
        ctx['default_warehouse'] = cabezal.warehouse and cabezal.warehouse.id or False,
        ctx['default_solicitante_id'] = cabezal.sr_id.solicitante_id.id,
        lines_list = []

        pick_type_id = stock_picking_type.search(cr, uid,
                                                    [('warehouse_id', '=', ctx['default_warehouse']),
                                                   ('code', '=', 'outgoing')], limit=1, context=context)
        pick_type_id = pick_type_id and pick_type_id[0] or False
        if pick_type_id:
            stock_picking_type_row = stock_picking_type.browse(cr, uid, pick_type_id, context=context)
            location_dest_id = stock_picking_type_row.default_location_dest_id and stock_picking_type_row.default_location_dest_id.id or False
        location_id = cabezal.ubicacion and cabezal.ubicacion.id or False

        for lineas_solicitud in cabezal.grp_sr_id:
            if lineas_solicitud.cantidad_pendiente and \
               not (lineas_solicitud.product_id.type=='service' or (lineas_solicitud.product_id.type=='consu' and lineas_solicitud.product_id.no_inventory)):
                date_today = datetime.date.today()
                date_formatted = date_today.strftime('%Y-%m-%d %H:%M:%S')
                lines_list.append((0, 0, self._prepare_move_line(cr, uid, ids, cabezal, lineas_solicitud, location_dest_id, pick_type_id, context=context)))
        ctx['default_picking_type_id'] = pick_type_id
        ctx['default_location_id'] = location_id
        ctx['default_location_dest_id'] = location_dest_id
        ctx['default_move_type'] = 'direct'
        if lines_list:
            ctx['default_move_lines'] = lines_list
            return {
                'type': 'ir.actions.act_window',
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': 'stock.picking',
                'context': ctx,
            }
        else:
            raise osv.except_osv('Error!',
                                 u'Se ha entregado todo el stock correspondiente a la solicitud de recursos seleccionada!')

    def button_add_line_sr(self, cr, uid, ids, context=None):
        values = {}
        values['grp_id'] = ids[0]
        values['cantidad_pedida'] = 0
        values['cantidad_solicitada'] = 0
        values['cantidad_en_pedido'] = 0
        values['domain_solicitante'] = True
        values['domain_almacenero'] = False
        values['domain_aprobador'] = False
        values['descripcion'] = ' '
        # por default va el primer registro cuyo name contenga "unit"
        product_uom_ids = self.pool.get('product.uom').search(cr, uid, [('name', 'ilike', '%unit%')], context=context)
        if not product_uom_ids:
            # No se encuentra unidad de medida para poner por defecto, se usa "1"
            values['uom_id'] = 1
        else:
            values['uom_id'] = product_uom_ids[0]
        line_sr_obj = self.pool.get('grp.compras.solicitud.recursos.line.sr')
        new_line_sr = line_sr_obj.create(cr, uid, values, context=context)
        if not new_line_sr:
            return False
        else:
            return True

    # agregado compras grp
    def copy(self, cr, uid, id, default=None, context=None):
        default = default or {}
        line_sr_obj = self.pool.get('grp.compras.solicitud.recursos.line.sr')
        r = self.browse(cr, uid, id)
        lines = []
        for line in r.sr_id.grp_sr_id:
            l_new = line_sr_obj.copy(cr, uid, line.id, default={})
            lines.append(l_new)
        default.update({
            'name': u'SR-Borrador',
            'grp_sr_id': [(6, 0, lines)]
        })
        id_sr_alm = super(grp_compras_solicitud_recursos_almacen, self).copy(cr, uid, id, default, context)
        id_lines = []
        sr_almacen = self.browse(cr, uid, id_sr_alm, context=context)
        for line_sr in sr_almacen.sr_id.grp_sr_id:
            id_lines.append(line_sr.id)
        if id_lines:
            line_sr_obj.write(cr, SUPERUSER_ID, id_lines,
                              {'cantidad_pedida': 0, 'cantidad_en_pedido': 0, 'cantidad_entregada': 0,
                               'state': 'inicio'})
        return id_sr_alm

    def button_cumplido_af(self, cr, uid, ids, context=None):
        pool_asset = self.pool.get('account.asset.asset')
        pool_historia = self.pool.get('grp.historial_responsable')
        for rec in self.browse(cr, uid, ids, context=context):
            #diccionario para guardar cantidades por cada producto
            dict_prod_cant = {}
            list_af = [l.articulo_af.numero_activo for l in rec.grp_sr_af_id if l.articulo_af
                       and l.articulo_af.numero_activo]
            list_af_2 = list(set(list_af))
            if len(list_af_2) < len(list_af):
                raise ValidationError(u"No es posible ingresar dos o más líneas con el mismo Artículo AF. "
                                      u"Por favor, elimine los repetidos.")
            for line_af in rec.grp_sr_af_id:
                if not line_af.product_id:
                    raise ValidationError(u"Debe ingresar un Producto en las líneas de AF.")
                if not line_af.articulo_af:
                    raise ValidationError(u"Debe ingresar un Artículo AF en las líneas de AF.")
                if not line_af.ubicacion_origen:
                    raise ValidationError(u"Debe ingresar una Ubicación de origen en las líneas de AF.")
                if not line_af.responsable_actual:
                    raise ValidationError(u"Debe ingresar un Responsable en las líneas de AF.")
                # HACK
                if hasattr(line_af, 'ubicacion_fisica') and not line_af.ubicacion_fisica:
                    raise ValidationError(u"Debe ingresar una Ubicación física en las líneas de AF.")
                ##
                if line_af.articulo_af.product_id:
                    if line_af.product_id.id != line_af.articulo_af.product_id.id:
                        raise ValidationError(u"Debe ingresar un AF cuyo producto sea el mismo que el ingresado en"
                                              u" la línea de la SR.")
                else:
                    raise ValidationError(u"Debe ingresar un AF cuyo producto sea el mismo que el ingresado en"
                                              u" la línea de la SR.")
                articulo = line_af.articulo_af.id
                vals = {
                    'product_id': line_af.product_id.id,
                    'department_id': line_af.ubicacion_origen.id,
                    'user_id': line_af.responsable_actual.id,
                }
                if hasattr(line_af, 'ubicacion_fisica'):
                    vals.update({'ubicacion_fisica': line_af.ubicacion_fisica.id})
                ctx = context.copy()
                ctx = dict(ctx)
                ctx.update({'doc_origin': rec.id,
                            'restrict_lot_ids':
                                {articulo: line_af.restrict_lot_id.id}
                            })
                pool_asset.write(cr, uid, [articulo], vals, context=ctx)
                #actualizo el diccionario de cantidades por producto
                key = line_af.product_id.id
                if not key in dict_prod_cant:
                    dict_prod_cant[key] = 1
                else:
                    dict_prod_cant[key] += 1
            #Actualizo estado de las lineas segun lo que tengo para entregar
            for line_sr in rec.grp_sr_id:
                if line_sr.product_id.id in dict_prod_cant:
                    #si la cantidad coincide la dejo en 0 y actualizo
                    if line_sr.cantidad_solicitada == dict_prod_cant[line_sr.product_id.id]:
                        line_sr.write({'cantidad_entregada': line_sr.cantidad_solicitada})
                        dict_prod_cant[line_sr.product_id.id] = 0
                    #si tengo mas de lo que solicite, entrego y resto esa cantidad
                    elif line_sr.cantidad_solicitada < dict_prod_cant[line_sr.product_id.id]:
                        line_sr.write({'cantidad_entregada': line_sr.cantidad_solicitada})
                        dict_prod_cant[line_sr.product_id.id] -= line_sr.cantidad_solicitada
                    #si no me da para entregar completo entrego lo que tengo
                    else:
                        line_sr.write({'cantidad_entregada': dict_prod_cant[line_sr.product_id.id]})
                        dict_prod_cant[line_sr.product_id.id] = 0

            wf_service = netsvc.LocalService('workflow')
            wf_service.trg_validate(uid, 'grp.compras.solicitud.recursos.almacen', rec.id, 'button_wkf_sr_cerrar_sr_almacen', cr)
        return True

class grp_compras_solicitud_recursos_line_sr(osv.osv):
    _name = 'grp.compras.solicitud.recursos.line.sr'
    _description = 'Lineas de Solicitud de Recurso'
    _rec_name = 'product_id'

    def fields_get(self, cr, uid, allfields=None, context=None, write_access=True, attributes=None):
        res = super(grp_compras_solicitud_recursos_line_sr, self).fields_get(cr, uid, allfields=allfields, context=context, write_access=write_access, attributes=attributes)
        if not self.pool.get('res.users').has_group(cr, uid,
                                                    'grp_seguridad.grp_compras_sr_Solicitante') and not self.pool.get(
            'res.users').has_group(cr, uid, 'grp_seguridad.grp_compras_sr_Aprobador'):
            res['descripcion']['readonly'] = True
        return res

    LISTA_ESTADOS_LINEA_SOLICITUD = [
        ('noe', 'No entregado'),
        ('noh', 'No hay'),
        ('parcial', 'Entregado Parcial'),
        ('total', 'Entregado Total'),
        ('enaprobcom', 'En aprobacion compras'),
        ('acompra', 'Enviado a compras'),
        ('rechazado', 'Rechazado por Compras'),
    ]

    def onchange_product_id(self, cr, uid, ids, product_id):
        value = {}
        if product_id:
            prod = self.pool.get('product.product').browse(cr, uid, product_id)
            if prod.product_tmpl_id.id:
                prod_uom_id = prod.product_tmpl_id.uom_id.id or prod.uom_id.id
                value = {'precio': prod.product_tmpl_id.standard_price,
                         'uom_id': prod_uom_id}
                # if prod.grp_sice_cod:
                #     pool_art_serv_obra = self.pool.get('grp.sice_art_serv_obra')
                #     pool_uom = self.pool.get('product.uom')
                #     sice_cod = prod.grp_sice_cod
                #     art_id = pool_art_serv_obra.search(cr, uid, [('cod', '=', sice_cod)])
                #     if len(art_id) > 0:
                #         art_id = art_id[0]
                #     art_obj = pool_art_serv_obra.browse(cr, uid, art_id)
                #     uom_ids = [prod_uom_id]
                #     for uom in art_obj.unidades_med_ids:
                #         prod_uom = pool_uom.search(cr, uid, [('sice_uom_id', '=', uom.id)])
                #         if len(prod_uom) > 0:
                #             prod_uom = prod_uom[0]
                #         prod_uom_obj = pool_uom.browse(cr, uid, prod_uom)
                #         if prod_uom and prod_uom_obj.category_id.id == prod.product_tmpl_id.uom_id.category_id.id:
                #             uom_ids.append(prod_uom)
                #     domain = {
                #         'uom_id': str([('id', 'in', uom_ids)])
                #     }
                #     return {'value': value}
        return {'value': value}

    def _domain_sc(self, cr, uid, ids, d1, d2, context=None):
        res = {}
        for lines in self.browse(cr, uid, ids, context=None):
            res[lines.id] = lines.cantidad_solicitada - lines.cantidad_pedida - lines.cantidad_entregada > 0
        return res

    def _domain_solicitante(self, cr, uid, ids, d1, d2, context=None):
        in_grp = self.pool.get('res.users').has_group(cr, uid, 'grp_seguridad.grp_compras_sr_Solicitante')
        res = {}
        for lines in self.browse(cr, uid, ids, context=None):
            res[lines.id] = in_grp and lines.grp_id.state in ['inicio', 'nuevo']
        return res

    def _domain_aprobador(self, cr, uid, ids, d1, d2, context=None):
        in_grp = self.pool.get('res.users').has_group(cr, uid, 'grp_seguridad.grp_compras_sr_Aprobador')
        res = {}
        for lines in self.browse(cr, uid, ids, context=None):
            res[lines.id] = (in_grp and lines.grp_id.state in ['en_aprobacion']) or (
                in_grp and lines.estado in ['enaprobcom'])
        return res

    def _domain_almacenero(self, cr, uid, ids, d1, d2, context=None):
        in_grp = self.pool.get('res.users').has_group(cr, uid,
                                                      'grp_seguridad.grp_compras_sr_Encargado_de_almacen')
        res = {}
        for lines in self.browse(cr, uid, ids, context=None):
            # res[lines.id] = in_grp and lines.grp_id.state in ['codificando']
            res[lines.id] = in_grp and lines.grp_id.state in ['codificando']
        return res

    def _domain_product(self, cr, uid, ids, d1, d2, context=None):
        res = {}
        for lines in self.browse(cr, uid, ids, context=None):
            res[lines.id] = lines.grp_id.state not in ['nuevo,inicio,en_aprobacion,esperando_almacen']
        return res

    def get_estado_linea(self, cr, uid, ids, fieldname, args, context=None):
        res = {}
        for lines in self.browse(cr, uid, ids, context=context):
            if lines.boton_check:
                res[lines.id] = 'enaprobcom'
            elif lines.cantidad_solicitada == lines.cantidad_entregada:
                res[lines.id] = 'total'
            elif lines.cantidad_entregada > 0:
                res[lines.id] = 'parcial'
            # Falta un estado
            elif lines.cantidad_en_pedido > 0:
                res[lines.id] = 'acompra'
            else:
                res[lines.id] = 'noe'
                # TODO dar opciones para que no se entregue o no haya
        return res

    def _pendiente(self, cr, uid, ids, prop, unknow_none, unknow_dict):
        res = {}
        for line in self.browse(cr, uid, ids):
            pendiente = line.cantidad_solicitada - line.cantidad_entregada
            res[line.id] = pendiente
        return res

    LISTA_ESTADOS_SOLICITUD = [
        ('inicio', 'Borrador'),
        ('en_aprobacion', u'En aprobación'),
        ('rechazado', 'Rechazado'),
        ('codificando', u'Aprobado'),
        ('esperando_almacen', u'En Proceso'),
        ('aprobado', 'Cerrado'),
    ]

    LISTA_TIPO_SR_ALMACEN_REL = [
        ('I', 'Insumos/Materiales'),
        ('S', 'Servicios'),
        ('AF', 'Activo Fijo'),
        ('PL', 'Planificada')
    ]

    TIPO_ESTADO_PROCESO = [
        ('eprocalm', u'En Proceso Almacenero'),
        ('eaprobcomp', u'En Aprobación Compras'),
        ('envcomp', u'Enviado a Compras'),
        ('pendrecep', u'Pendiente Recepción'),
    ]

    def _get_estado_en_proc(self, cr, uid, ids, name, args, context=None):
        res = {}
        sr_alm_obj = self.pool.get('grp.compras.solicitud.recursos.almacen')
        for rec in self.browse(cr, uid, ids, context=context):
            res[rec.id] = False
            if rec.grp_id:
                sr_id = sr_alm_obj.search(cr, uid, [('sr_id', '=', rec.grp_id.id)], context=context)
                if sr_id:
                    sr_alm = sr_alm_obj.browse(cr, uid, sr_id)
                    res[rec.id] = sr_alm.estado_en_proc
        return res

    _columns = {
        'grp_id': fields.many2one('grp.compras.solicitud.recursos', 'Solicitud', ondelete='cascade'),

        'product_id': fields.many2one('product.product', 'Producto'),
        'uom_id': fields.many2one('product.uom', string='UdM', ondelete='restrict'),
        'descripcion': fields.char(u'Descripción', size=250, required=True),

        'cantidad_solicitada': fields.float('Cantidad', digits_compute=dp.get_precision('Cantidad'), required=True),
        'cantidad_en_pedido': fields.float('En pedido', digits_compute=dp.get_precision('Cantidad'), required=True),
        'cantidad_entregada': fields.float('Cantidad Entregada', digits_compute=dp.get_precision('Cantidad')),
        'estado': fields.function(get_estado_linea, type='selection', selection=LISTA_ESTADOS_LINEA_SOLICITUD,
                                  string='Estado', store=True),
        'cantidad_pedida': fields.float('Cantidad a pedir', digits_compute=dp.get_precision('Cantidad')),
        'domain_sc': fields.function(_domain_sc, type='boolean', store=True),
        'domain_solicitante': fields.function(_domain_solicitante, type='boolean', store=False),
        'domain_aprobador': fields.function(_domain_aprobador, type='boolean', store=False),
        'domain_almacenero': fields.function(_domain_almacenero, type='boolean', store=False),
        'domain_product': fields.function(_domain_product, type='boolean', store=False),
        'cantidad_pendiente': fields.function(_pendiente, string='Cantidad pendiente', type='float',
                                              digits_compute=dp.get_precision('Account')),
        # campos para vista tree Listado SR por Solicitante
        'nro_solicitud': fields.related('grp_id', 'name', string='Nro. Solicitud', type='char'),
        'fecha_solicitud': fields.related('grp_id', 'date_start', string='Fecha de Solicitud', type='date'),
        'description': fields.related('grp_id', 'description', type='text', string=u'Descripción'),
        'solicitante_id': fields.related('grp_id', 'solicitante_id', type='many2one', relation='res.users',
                                         string='Solicitante', store=True),
        'department_id': fields.related('grp_id', 'department_id', type='many2one', relation='hr.department',
                                       string='Departamento', store=True),
        'estado_en_proc': fields.function(_get_estado_en_proc, type='selection', selection=TIPO_ESTADO_PROCESO,
                                          string="Estado en proceso"),
        'fecha_necesidad': fields.date('Fecha de necesidad'),
        'boton_check': fields.boolean(u'Botón pulsado'),
        'monto': fields.float('Monto'),
        'moneda': fields.many2one('res.currency', 'Moneda'),
        'cuenta_analitica': fields.many2one('account.analytic.account', u'Cuenta analítica'),
        'cuenta_contable': fields.many2one('account.account', u'Cuenta contable'),
        'disponible': fields.char('Disponible', size=64),
    }

    _defaults = {
        'boton_check': False,
        'cantidad_pedida': 0,
        'cantidad_solicitada': 1,
        'cantidad_en_pedido': 0,
        'cantidad_pendiente': 0,
        'domain_solicitante': True,
        'domain_almacenero': False,
        'domain_aprobador': False,
    }

class grp_compras_solicitud_recursos_line_sr_af(osv.osv):
    _name = 'grp.compras.solicitud.recursos.line.sr.af'
    _description = 'Lineas de sr Activo fijo'
    _columns = {
        'product_id': fields.many2one('product.product', 'Producto'),
        'grp_id': fields.many2one('grp.compras.solicitud.recursos.almacen', 'Solicitud', ondelete='cascade'),

        'articulo_af': fields.many2one('account.asset.asset', u'Artículo AF'),
        'ubicacion_origen': fields.many2one('hr.department', u'Ubicación'),
        'responsable_actual': fields.many2one('res.users',u'Responsable'),
        'estado_aprob_af': fields.related('articulo_af', 'estado_responsable', string='Estado de aprobación del AF',
                                          type='selection', selection=_ESTADO_HISTORIAL_RESP),
    }

LISTA_ESTADOS_SOLICITUD = [
    ('noe', 'No entregado'),
    ('noh', 'No hay'),
    ('parcial', 'Entregado Parcial'),
    ('total', 'Entregado Total'),
    ('enaprobcom', u'En aprobación compras'),
    ('acompra', 'Enviado a compras'),
    ('rechazado', 'Rechazado por Compras'),
]

class solicitud_recursos_line_list(osv.osv):
    _name = "grp.compras.sr.lines.transacciones"
    _description = "Listado de Transacciones Unificadas SR"
    _order = "id asc"
    _auto = False
    _columns = {
        'id': fields.integer('Id', readonly=True),
        'product_id': fields.many2one('product.product', 'Producto', required=True, select=True),
        'uom_id': fields.many2one('product.uom', 'Unidad de medida', required=True),
        'cantidad_solicitada': fields.float('Cantidad solicitada',
                                            digits_compute=dp.get_precision('Product Unit of Measure')),
        'cantidad_entregada': fields.float('Cantidad entregada',
                                           digits_compute=dp.get_precision('Product Unit of Measure')),
        'cantidad_pendiente': fields.float('Cantidad pendiente',
                                           digits_compute=dp.get_precision('Product Unit of Measure')),
        'sr_id': fields.many2one('grp.compras.solicitud.recursos', u'Nº SR', ondelete='set null'),
        'sc_id': fields.many2one('grp.solicitud.compra', u'Nº SC', ondelete='set null'),
        'pedido_compra_id': fields.many2one('grp.pedido.compra', u'Nº PC', ondelete='set null'),
        'order_id': fields.many2one('purchase.order', u'Nº OC', ondelete='set null'),
        'estado': fields.selection(LISTA_ESTADOS_SOLICITUD, string='Estado', select=True),
        'descripcion': fields.char(u'Descripción', size=64, select=True),
        'state': fields.char('Estado',size=20),
        'department_id': fields.many2one('hr.department', string=u'Departamento', select=True),
        'operating_unit_id': fields.many2one('operating.unit', string=u'UE', select=True),
        'date_start': fields.date(string='Fecha de Solicitud'),

    }

    def init(self, cr):
        drop_view_if_exists(cr, 'grp_compras_sr_lines_transacciones')
        cr.execute("""
         create or replace view grp_compras_sr_lines_transacciones as (
         SELECT ROW_NUMBER() over() as id, lsr.product_id, puom.id as uom_id, lsr.cantidad_solicitada, lsr.cantidad_entregada,
            (COALESCE(lsr.cantidad_solicitada,0) - COALESCE(lsr.cantidad_entregada,0)) as cantidad_pendiente,
            lsr.estado,lsr.descripcion, sr.name,
            sr.id as sr_id,sc.id as sc_id,
            pc.id as pedido_compra_id,
            ocl.order_id,
            sr.state,
            sr.department_id,
            sra.operating_unit_id as operating_unit_id,
            sr.date_start
            from grp_compras_solicitud_recursos_line_sr lsr
            left outer join product_product p on p.id = lsr.product_id
            left outer join product_template pt on pt.id = p.product_tmpl_id
            left outer join product_uom puom on puom.id = pt.uom_id
            left outer join grp_compras_solicitud_recursos sr on lsr.grp_id = sr.id
            left outer join grp_compras_solicitud_recursos_almacen sra on sra.sr_id = sr.id
            left outer join grp_solicitud_compra sc on lsr.id = sc.linea_solicitud_recursos_id
            left outer join grp_linea_pedido_compra lpc on lpc.solicitud_compra_id = sc.id
            left outer join grp_pedido_compra pc on pc.id = lpc.pedido_compra_id
            left outer join purchase_order oc on oc.pedido_compra_id = pc.id
            left outer join purchase_order_line ocl on ocl.order_id = oc.id and ocl.product_id = p.id
            WHERE sr.state <> 'inicio'
            ORDER BY id asc)""")

    def open_sr(self, cr, uid, ids, context=None):
        sr_alm_obj = self.pool.get('grp.compras.solicitud.recursos.almacen')
        for linea in self.browse(cr, uid, ids, context=context):
            if linea.sr_id:
                sr_id = sr_alm_obj.search(cr, uid, [('sr_id', '=', linea.sr_id.id)], context=context)
                if sr_id:
                    return {
                        'type': 'ir.actions.act_window',
                        'view_type': 'form',
                        'view_mode': 'form',
                        'res_model': 'grp.compras.solicitud.recursos.almacen',
                        'res_id': sr_id and sr_id[0],
                        'target': 'new',
                        'context': context,
                    }

    def open_sc(self, cr, uid, ids, context=None):
        for linea in self.browse(cr, uid, ids, context=context):
            if linea.sc_id:
                return {
                    'type': 'ir.actions.act_window',
                    'view_type': 'form',
                    'view_mode': 'form',
                    'res_model': 'grp.solicitud.compra',
                    'res_id': linea.sc_id.id,
                    'target': 'new',
                    'context': context,
                }

    def open_pdc(self, cr, uid, ids, context=None):
        for linea in self.browse(cr, uid, ids, context=context):
            if linea.pedido_compra_id:
                return {
                    'type': 'ir.actions.act_window',
                    'view_type': 'form',
                    'view_mode': 'form',
                    'res_model': 'grp.pedido.compra',
                    'res_id': linea.pedido_compra_id.id,
                    'target': 'new',
                    'context': context,
                }

    def open_oc(self, cr, uid, ids, context=None):
        for linea in self.browse(cr, uid, ids, context=context):
            if linea.order_id:
                return {
                    'type': 'ir.actions.act_window',
                    'view_type': 'form',
                    'view_mode': 'form',
                    'res_model': 'purchase.order',
                    'res_id': linea.order_id.id,
                    'target': 'new',
                    'context': context,
                }

class grp_fecha_planificada(osv.osv):
    _name = 'grp.fecha.planificada'

    def _get_years(self, cr, uid, context=None):
        current_year = datetime.date.today().year
        select = [(str(year),str(year)) for year in range(current_year, current_year+5)]
        select.reverse()
        return select

    _columns = {
        'year': fields.selection(_get_years, string=u'Año', required=True, default=str(datetime.date.today().year)),
        'reception_date': fields.date(u'Fecha recepción', required=True)
    }
    _sql_constraints = [
        ('year_planif_uniq', 'unique(year)', 'Debe existir una sola fecha configurada por cada año'),
    ]


from tabulate import tabulate
from ipykernel.kernelbase import Kernel

from pytezos.repl.helpers import helpers_prim
from pytezos.michelson.macros import primitives
from pytezos.repl.interpreter import Interpreter

from michelson_kernel import __version__
from michelson_kernel.docs import docs

static_macros = [
    'CMPEQ', 'CMPNEQ', 'CMPLT', 'CMPGT', 'CMPLE', 'CMPGE',
    'IFEQ', 'IFNEQ', 'IFLT', 'IFGT', 'IFLE', 'IFGE',
    'IFCMPEQ', 'IFCMPNEQ', 'IFCMPLT', 'IFCMPGT', 'IFCMPLE', 'IFCMPGE',
    'FAIL',
    'ASSERT_EQ', 'ASSERT_NEQ', 'ASSERT_LT', 'ASSERT_GT', 'ASSERT_LE', 'ASSERT_GE',
    'ASSERT_CMPEQ', 'ASSERT_CMPNEQ', 'ASSERT_CMPLT', 'ASSERT_CMPGT', 'ASSERT_CMPLE', 'ASSERT_CMPGE',
    'ASSERT_NONE', 'ASSERT_SOME', 'ASSERT_LEFT', 'ASSERT_RIGHT',
    'UNPAIR',
    'IF_SOME',
    'SET_CAR', 'SET_CDR',
    'MAP_CAR', 'MAP_CDR'
]

def parse_token(line, cursor_pos):
    begin_pos = next((i + 1 for i in range(cursor_pos - 1, 0, -1) if line[i] in ' ;({\n'), 0)
    end_pos = next((i for i in range(cursor_pos, len(line)) if line[i] in ' ;){\n'), len(line))
    return line[begin_pos:end_pos], begin_pos, end_pos

def html_table(items):
    def pre(s):
        return f'
{s}
'

    def pre_dict(d):
        return {k: pre(v) for k, v in d.items()}

    res = tabulate(list(map(pre_dict, items)), tablefmt='html', headers="keys")
    res = res.replace('<', '<').replace('>', '>')  # tabulate escapes our
 tags
    return res

def plain_table(items):
    return tabulate(items, tablefmt='simple', headers="keys")

def format_result(result, execution_count):
    kind = result['kind']
    if kind in ['message', 'code']:
        plain, html = result['value'], result['value']
    elif kind in ['stack', 'big_map_diff']:
        plain = plain_table(result['value'])
        html = html_table(result['value'])
    elif kind == 'output':
        ops, storage, bmd = result['value']
        data = list(filter(lambda x: len(x) > 0, [storage, bmd, ops]))
        plain = '\n'.join(map(plain_table, data))
        html = '
'.join(map(html_table, data))
    else:
        assert False, kind
    return {'data': {'text/plain': plain,
                     'text/html': html},
            'metadata': {},
            'execution_count': execution_count}

class MichelsonKernel(Kernel):
    implementation = 'IMichelson'
    implementation_version = __version__
    language_info = {
        'name': 'Michelson',
        'mimetype': 'text/x-michelson',
        'file_extension': '.tz',
        'codemirror_mode': 'michelson'
    }
    banner = 'Michelson (Tezos VM language)'
    help_links = [
        'https://michelson.nomadic-labs.com/',
        'https://tezos.gitlab.io/whitedoc/michelson.html'
    ]

    def __init__(self, **kwargs):
        super(MichelsonKernel, self).__init__(**kwargs)
        self.interpreter = Interpreter(debug=False)

    def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False):
        int_res = self.interpreter.execute(code)
        if not silent and int_res.get('stdout'):
            self.send_response(
                self.iopub_socket, 'stream', {'name': 'stdout',
                                              'text': int_res['stdout']})

        if int_res.get('success'):
            res = {'status': 'ok'}
            if not silent and int_res.get('result'):
                self.send_response(
                    self.iopub_socket, 'execute_result', format_result(int_res['result'], self.execution_count))
        else:
            res = {'status': 'error'}
            if int_res.get('stderr'):
                traceback = [f'{int_res["stderr"]["name"]}: {int_res["stderr"]["value"]}',
                             int_res["stderr"]["trace"]]
                res = {'ename': int_res['stderr']['name'],
                       'evalue': int_res['stderr']['value'],
                       'traceback': traceback}
                self.send_response(
                    self.iopub_socket, 'stream', {'name': 'stderr',
                                                  'text': '\n'.join(traceback)})

        res['execution_count'] = self.execution_count
        return res

    def do_complete(self, code, cursor_pos):
        token, begin_pos, end_pos = parse_token(code, cursor_pos)

        suggests = []
        for word_set in [primitives, static_macros, helpers_prim]:
            for word in word_set:
                if word.startswith(token):
                    suggests.append(word)

        if suggests:
            res = {
                'matches': suggests,
                'cursor_start': begin_pos,
                'cursor_end': end_pos}
        else:
            res = {
                'matches': [],
                'cursor_start': cursor_pos,
                'cursor_end': cursor_pos}

        res['status'] = 'ok'
        return res

    def do_inspect(self, code, cursor_pos, detail_level=0):
        token, _, _ = parse_token(code, cursor_pos)
        docstring = docs.get(token)
        if docstring:
            res = {
                'found': True,
                'data': {'text/plain': docstring}}
        else:
            res = {'found': False}

        res['status'] = 'ok'
        return res


import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
from CNNModel import CNNModel
from MyDataset import MyDataset
from torch.utils.data import DataLoader
import argparse
import yaml
from easydict import EasyDict

parser = argparse.ArgumentParser(description='Classifier Task With CNN')
parser.add_argument("--config_path", type=str, default="config.yaml")
args = parser.parse_args()
config_path =args.config_path
config = yaml.load(open(config_path, 'r'), Loader=yaml.Loader)
config = EasyDict(config)
config = config["CNN"]

train_path = config["Train"]["train_path"]
val_path = config["Val"]["val_path"]
class_num = config["General"]["class_num"]
batch_size = config["Train"]["batch_size"]
epochs = config["Train"]["epochs"]
lr = config["Train"]["lr"]
is_load = config["Train"]["is_load"]
load_path = config["Train"]["load_path"]
save_path = config["Train"]["save_path"]

def eval(model):
    eval_dataset = MyDataset(data_path = val_path,
                              class_num = class_num, )
    eval_loader = DataLoader(dataset = eval_dataset,
                              shuffle = True,
                              batch_size = batch_size,
                              drop_last = False)
    acc_num = 0
    for i, batch in enumerate(eval_loader):
        img_tensor, label_tensor = batch # torch.tensor
        with torch.no_grad():
            pred_tensor = model(img_tensor.to(device))
            for j in range(pred_tensor.size(0)):
                pred = torch.argmax(pred_tensor[j]).item()
                if pred==label_tensor[j]:
                    acc_num+=1

    acc_rate = acc_num / len(eval_dataset)
    print("Acc_rate %.2f%% \n" % (acc_rate*100))
    return acc_rate

if __name__ == "__main__":

    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    model = CNNModel()
    model.to(device)

    if is_load:
        print(" -------< Loading parameters from {} >------- \n".format(load_path))
        params = torch.load(load_path, map_location=device)
        model.load_state_dict(params, strict=True)

    # softmax
    loss_function = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=lr)

    train_dataset = MyDataset(data_path=train_path,
                              class_num=class_num, )
    train_loader = DataLoader(dataset = train_dataset,
                              shuffle = True,
                              batch_size = batch_size,
                              drop_last = True)
    best_acc = 0
    epoch_record_x = []
    acc_rate_record_y = []
    epoch_record_x.append(0)
    acc_rate_y = eval(model)
    acc_rate_record_y.append(acc_rate_y)

    for epoch in range(0, epochs+1):
        for i, batch in enumerate(train_loader):
            img_tensor, label_tensor = batch
            pred_tensor = model(img_tensor.to(device))
            loss = loss_function(pred_tensor, label_tensor.to(device))
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

        if epoch % 5 == 0:
            print("Epoch" , epoch)
            epoch_record_x.append(epoch)
            acc_rate_y = eval(model)

            if best_acc < acc_rate_y:
                print(" -------< Saved Best Model >------- \n")
                torch.save(model.state_dict(), save_path)
                best_acc = acc_rate_y

            acc_rate_record_y.append(acc_rate_y)

    plt.plot(epoch_record_x, acc_rate_record_y,
                color="green", label="acc_rate_record")
    plt.title("Loss with epoch")
    plt.legend()
    plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 15 17:35:48 2018
@author: Lucas Liu

Implementing Deep Q Network (DQN)
On my designed task.
"""

import torch.nn.functional as F
import torch.optim as optim
import torch.nn as nn
import torch

import pybullet as p
import pybullet_data

from collections import namedtuple
import matplotlib.pyplot as plt
from math import exp
import numpy as np
import random
import copy

class Net(nn.Module):

    def __init__(self):

        super(Net, self).__init__()

        self.bn0 = nn.BatchNorm1d(ob_size)
        self.fc1 = nn.Linear(ob_size, 400)
        self.bn1 = nn.BatchNorm1d(400)
        self.fc2 = nn.Linear(400, 300)
        self.bn2 = nn.BatchNorm1d(300)
        self.fc3 = nn.Linear(300, ac_size)

    def forward(self, x):

        x = self.bn0(x)
        x = F.relu( self.bn1(self.fc1(x)) )
        x = F.relu( self.bn2(self.fc2(x)) )
        x = self.fc3(x)

        return x

class Env():

    def __init__(self):

        self.physicsClient = p.connect(p.DIRECT)   # p.DIRECT or p.GUI
        p.setAdditionalSearchPath(pybullet_data.getDataPath())
        p.configureDebugVisualizer(p.COV_ENABLE_GUI, 0)
        p.resetDebugVisualizerCamera(cameraDistance=3, cameraYaw=90, cameraPitch=-30, cameraTargetPosition=[0,1,0])
        self.base_pos = []

        self.angle1 = np.deg2rad(20)
        self.length1 = 2
        self.angle2 = np.deg2rad(10)
        self.length2 = 1
        self.radius = 15
        self.plane_urdf = '/home/lucas/modules/summer_project/bullet3-master/pybullet/gym/pybullet_data/plane.urdf'

        self.action_delta = 2 * action_bound / (ac_size-1)
        self.action_range = [-action_bound+self.action_delta*i for i in range(ac_size)]

    def reset(self):

        self.vt = 0

        p.resetSimulation()
        p.setGravity(0,0,-9.8)
        #self.planeId = p.loadURDF("plane.urdf")
        self.plane1 = p.loadURDF(self.plane_urdf,
                                 [0, -13, 0],
                                 p.getQuaternionFromEuler([0, 0, 0]))

        self.plane2 = p.loadURDF(self.plane_urdf,
                                 [0, 2+self.radius*np.cos(self.angle1), -self.radius*np.sin(self.angle1)],
                                 p.getQuaternionFromEuler([-self.angle1, 0, 0]))

        self.plane3 = p.loadURDF(self.plane_urdf,
                                 [0, 3+self.length1*np.cos(self.angle1), -self.length1*np.sin(self.angle1)],
                                 p.getQuaternionFromEuler([0, 0, 0]))
        '''
        self.plane4 = p.loadURDF(self.plane_urdf,
                                 [0, 4+self.length1*np.cos(self.angle1)+self.length2*np.cos(self.angle2)-self.radius*np.cos(self.angle2),
                                  self.length2*np.sin(self.angle2)-self.length1*np.sin(self.angle1)-self.radius*np.sin(self.angle2)],
                                 p.getQuaternionFromEuler([self.angle2, 0, 0]))

        self.plane5 = p.loadURDF(self.plane_urdf,
                                 [0, 4+self.length1*np.cos(self.angle1)+self.length2*np.cos(self.angle2)+self.radius,
                                  self.length2*np.sin(self.angle2)-self.length1*np.sin(self.angle1)],
                                 p.getQuaternionFromEuler([0, 0, 0]))
        '''
        self.botId = p.loadURDF("/home/lucas/modules/summer_project/my_models/bot_origin2.urdf", StartPos, StartOrien)

        p.stepSimulation()

        observation = self.step_observation()

        return observation

    def step(self, action, ob):

        self.step_action(action)

        p.stepSimulation()

        ob2 = self.step_observation()

        reward = self.step_reward(ob2)

        done = self.step_done(ob2)

        return (ob2, reward, done, ob2[0][4])

    def step_action(self, action):

        acceleration = self.action_range[action]
        self.vt += acceleration
        self.vt = max(-25, min(self.vt, 25))
        # execute the action
        p.setJointMotorControl2(bodyUniqueId = self.botId,
                                jointIndex = 0,
                                controlMode = p.VELOCITY_CONTROL,
                                targetVelocity = self.vt)
        p.setJointMotorControl2(bodyUniqueId = self.botId,
                                jointIndex = 1,
                                controlMode = p.VELOCITY_CONTROL,
                                targetVelocity = -self.vt)

    def step_observation(self):

        base_pos, base_orn = p.getBasePositionAndOrientation(self.botId)
        base_orn = p.getEulerFromQuaternion(base_orn)
        base_linV, bas_anguV = p.getBaseVelocity(self.botId)
        _, joint_V, _, _ = p.getJointState(self.botId, 0)

        observation = torch.tensor([base_orn[0], bas_anguV[0], base_linV[1], joint_V, base_pos[1]], dtype=torch.float)
        observation = torch.unsqueeze(observation, 0)
        #print(observation)

        return observation

    def step_reward(self, ob2):
        #-exp(2*(x+0.2)^2)+1
        reward = -exp(2*(ob2[0][0]+0.05)**2)+1 + 2*ob2[0][4] + 4*ob2[0][2]
        reward = torch.tensor([reward], dtype=torch.float)
        reward = torch.unsqueeze(reward, 0)

        return reward

    def step_done(self, ob2):

        done = abs(ob2[0][0]) > 1.2

        return done

class DQN():

    def __init__(self):

        self.greedy_end = 0.05
        self.memory_size = memory_size
        self.anneal = 0.05 / self.memory_size # check!!!!

        self.memory = []
        self.Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state'))

        self.dqnNet = Net()
        self.dqnNet.zero_grad()
        self.optimizor = optim.RMSprop(self.dqnNet.parameters(), lr=0.001)
        #Surprisingly, RMSprop+smooth_l1_loss is much better than SGD+MSELoss, especially the optimizor.

        self.targetNet = copy.deepcopy(self.dqnNet)
        self.targetNet.zero_grad()

    def store_transitions(self, ob, action, reward, ob2):

        if(step_count < self.memory_size):
            self.memory.append(None)
            self.memory[step_count] = self.Transition(ob, action, reward, ob2)
        else:
            position = step_count % self.memory_size
            self.memory[position] = self.Transition(ob, action, reward, ob2)

    def step_action(self, ob, steps):

        a = random.random()

        # annealing greedy (0.9 - 0.1)
        if(steps > self.memory_size):
            greedy = self.greedy_end
        else:
            greedy = 0.9 - self.anneal * steps

        if(a < greedy):
            action = random.randint(0, ac_size - 1)
        else:
            self.dqnNet = self.dqnNet.eval()
            output = self.dqnNet(ob)
            _, index = torch.max(output, dim=1)
            action = index.item()
            self.dqnNet = self.dqnNet.train()

        return action

    def learn(self):

        input_state = torch.zeros(batch_size, ob_size)
        input_nextState = torch.zeros(batch_size, ob_size)
        reward = torch.zeros(batch_size, 1)
        target_Q = torch.zeros(batch_size, 1)
        current_Q = torch.zeros(batch_size, 1)
        nextState_max = torch.zeros(batch_size, 1)

        # randomly sampled from experience
        drawn = random.sample(range(self.memory_size), batch_size)

        for i in range(batch_size):
            input_state[i] = self.memory[drawn[i]][0]
            input_nextState[i] = self.memory[drawn[i]][3]

        # calculate current/target q (action) values
        output_state = self.dqnNet(input_state)
        output_nextState = self.targetNet(input_nextState)
        output_nextState = output_nextState.detach()

        # get current state-action value and maximum target q_value
        for i in range(batch_size):
            a = self.memory[drawn[i]][1]
            current_Q[i] = output_state[i][a]

            reward[i] = self.memory[drawn[i]][2]
            _, index = torch.max(output_nextState[i], 0)
            nextState_max[i] = output_nextState[i][index]

        target_Q = reward + gamma * nextState_max

        # update neural network
        loss = F.smooth_l1_loss(current_Q, target_Q)
        self.optimizor.zero_grad()
        loss.backward()
        self.optimizor.step()

def main():

    global targetNet_step, step_count, train_flag

    env1 = Env()

    dqn = DQN()

    for episode in range(episode_count):#episode_count

        ob = env1.reset()

        survival = 0

        while True:

            action = dqn.step_action(ob, step_count)

            ob2, reward, done, base_pos = env1.step(action, ob)

            # store memory
            dqn.store_transitions(ob, action, reward, ob2)

            # train
            if(step_count>memory_size and train_flag):
                if(step_count == memory_size + 1):
                    print('-------------learning_start-------------')
                dqn.learn()
                targetNet_step += 1
                if(targetNet_step>target_update):
                    dqn.targetNet = copy.deepcopy(dqn.dqnNet)
                    targetNet_step = 0

            ob = ob2
            survival += 1
            step_count += 1
            if(done or survival>1999 or base_pos>9):
                print('Episode:', episode, ' survival:', survival, ' position: %.2f' % base_pos, 'action: %.2f' % action,
                      'base_linV: %.2f' % ob2[0][2], 'joint_V: %.2f' % ob2[0][3],)
                record_survival.append(survival)
                record_position.append(base_pos)
                if(base_pos>7):
                   train_flag = False
                   print('--------------Training STOP--------------')
                break

if __name__ == '__main__':

    random.seed(1) #2 # 1
    torch.manual_seed(1) # 2 # 1

    ob_size = 5
    ac_size = 21

    # for bullet environment
    StartOrien = p.getQuaternionFromEuler([0, 0, 0])
    StartPos = [0, 0, 0]
    action_bound = torch.tensor([[4]], dtype=torch.float)

    # for deep Q network
    episode_count = 1000 # 2000 # 1000
    memory_size = 10000 # 100000 # 10000
    step_count = 0
    batch_size = 32 # 64 # 32
    gamma = 0.99
    target_update = 100
    targetNet_step = 0

    train_flag = True
    record_survival = []
    record_position = []

    main()


import easyocr
import streamlit as st
import pandas as pd
import re
from pymongo import MongoClient
from PIL import Image

st.markdown(
    """
    <style>
    .main {
    background-color: #FF9DC4;
    }
    </style>
    """,
    unsafe_allow_html=True
    )

# To Display the title for the project
st.title("BizCardX: Extract Business Card Details with OCR")
st.header('(Credit: Mayur)')

#  Input for Collection name in database
name = st.text_input("Enter Your Name")

# upload button
uploaded_file = st.file_uploader("Choose a file")

if uploaded_file is not None:
        bytes_data = uploaded_file.getvalue()
        image = Image.open(uploaded_file)
        new_image = image.resize((500, 333))
        st.write("Image Preview")
        st.image(new_image)

        # showing the details
        show_details = st.button("Show Details")

        reader = easyocr.Reader(['en'])
        result1 = reader.readtext(bytes_data)

        # Creating string to iterate
        df1 = pd.DataFrame(result1, columns=['a', 'b', 'c'])
        name_list= df1['b'].tolist()
        my_string = ', '.join(name_list)

# Creating Patterns
        pattern1 = r"[A-Za-z]+(?:\s+[A-Za-z]+)*(?:\s+(?:&|-)\s+[A-Za-z]+(?:\s+[A-Za-z]+)*)*"
        match1 = re.search(pattern1, my_string)
        if match1:
            card_holder = match1.group()
        else:
            card_holder = 'NA'

        pattern2 = r'\b([A-Z]+[ -&]+[A-Z]+?)+\b'
        match2 = re.search(pattern2, my_string)
        if match2:
            designation = match2.group()
        else:
            designation = 'NA'

        #pattern3 = r"^\+91-\d{10},$"
        #pattern3 = r'[\+\(]?[1-9][0-9 .\-\(\)]{8,}[0-9]'
        pattern3 = r'\d{3}-\d{3}-\d{4}'
        match3 = re.search(pattern3, my_string)
        if match3:
            mobile = match3.group()
        else:
            mobile = 'NA'

        #pattern4 = r"[a-z0-9\.\-+_]+@[a-z0-9\.\-+_]+\.[a-z]+"
        pattern4 = r"\w+@\w+\.\w+"
        match4 = re.search(pattern4, my_string)
        if match4:
            email = match4.group()
        else:
            email = 'NA'

        pattern5 = r"\bglobal\.com\b"
        #pattern6 = r"https?://(?:www\.)?[A-Za-z0-9.-]+(?:\.[A-Za-z]{2,})?/?[A-Za-z0-9_./~#&=+-]*"
        match5 = re.search(pattern5, my_string)
        if match5:
            website = match5.group()
        else:
            website = 'NA'

        pattern6 = r"Andhra Pradesh|Arunachal Pradesh|Assam|Bihar|Chhattisgarh|Goa|Gujarat|Haryana|Himachal Pradesh|Jharkhand|Karnataka|Kerala|Madhya Pradesh|Maharashtra|Manipur|Meghalaya|Mizoram|Nagaland|Odisha|Punjab|Rajasthan|Sikkim|TamilNadu|Telangana|Tripura|Uttar Pradesh|Uttarakhand|West Bengal"
        match6 = re.search(pattern6, my_string)
        if match6:
            state = match6.group()
        else:
            state = 'NA'

        pattern7 = r"\d{6}"
        match7 = re.search(pattern7, my_string)
        if match7:
            pincode = match7.group()
        else:
            pincode = 'NA'

        l = [card_holder, designation, mobile, email, website, state, pincode]
        df = pd.DataFrame(l).T
        df.columns = ['Card_holder', 'Designation', 'Mobile', 'Email', 'Website', 'State', 'Pincode']

        # Showing details in dataframe form
        if show_details:
          st.write(df)

        upload = st.button('Upload to Database')

        # Codes to Store the Datas in to the Data Base
        client = MongoClient(("mongodb://localhost:27017"))
        # Data Base
        db = client["Card_Reader"]

        if upload:
            col1, col2 = st.columns(2)
            with col1:
                mydb = db[name]
                df.reset_index(inplace=True)
                df_dict = df.to_dict("records")
                mydb.insert_one({"index": name + 'data', "data": df_dict})
                st.write("Details uploaded")

        if upload:
            # downloading the data
             data_from_db = mydb.find_one({"index":name+'data'})
             df = pd.DataFrame(data_from_db["data"])
             df.to_csv("card.csv")
             df.to_json("card.json")

             col3, col4 = st.columns(2)
             with col3:
                 st.download_button("Download as CSV",
                                df.to_csv(),
                                mime = 'text/csv')
             with col4:
                 st.download_button("Download as Json",
                                df.to_json(),
                                mime='json')


import sys
from collections import deque
input = sys.stdin.readline

n = int(input().rstrip())
queue = deque()
for i in range(1, n + 1):
    queue.append(i)

while queue:
    now = queue.popleft()
    print(now, end=' ')

    if queue:
        go = queue.popleft()
        queue.append(go)


#!/usr/bin/env python
# coding: utf-8

# In[16]:

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import requests
import time
from scipy.stats import linregress
import citipy
from citipy import citipy
from config import api_key
import json
import os

output_data_file = "output/cities.csv"

# In[2]:

#Set ranges
lat_range = (-90, 90)
lng_range = (-180, 180)

# In[ ]:

#Create lists for latitudes/longitudes and cities
lat_lngs = []
cities = []

# In[3]:

# Use .random to select random latitudes/longitudes
lats = np.random.uniform(low=-90.000, high=90.000, size=1500)
lngs = np.random.uniform(low=-180.000, high=180.000, size=1500)

#Use zip to combine latitudes/longitudes and put in list
lat_lngs = zip(lats, lngs)

# In[ ]:

# Using the list made, identify nearest city for latitudes/longitudes combinations
for lat_lng in lat_lngs:
    near_city = citipy.nearest_city(lat_lng[0], lat_lng[1]).city_name

    # If the city is unique, then add it to a our cities list
    if near_city not in cities:
        cities.append(near_city)

# In[4]:

#Set up lists to append data into
city_list = []
max_temp_list = []
humidity_list = []
cloud_list = []
wind_speed_list = []
country_code_list = []
date_list = []
lat_list = []
lng_list = []

# In[24]:

index = 0
set_count = 1
print("Begin")

base_url = "http://api.openweathermap.org/data/2.5/weather?"
imperial = "imperial"

#Use f string to set up query url
query_url = f"{base_url}appid={api_key}&units={imperial}&q="

# For each city name in cities list, do below things...
for city in (cities):
    try:
        response_json = requests.get(query_url + city).json()
        city_list.append(response_json["name"])
        cloud_list.append(response_json["clouds"]["all"])
        country_code_list.append(response_json["sys"]["country"])
        date_list.append(response_json["dt"])
        humidity_list.append(response_json["main"]["humidity"])
        lat_list.append(response_json["coord"]["lat"])
        lng_list.append(response_json["coord"]["lon"])
        max_temp_list.append(response_json['main']['temp_max'])
        wind_speed_list.append(response_json["wind"]["speed"])

        if index > 49:
            index = 1
            set_count += 1
        else:
            index += 1
        print(f"Processing Record #{index}, city name: {city}, set #{set_count}")
    except KeyError:
        print("Oops, that key doesn't exist.")

print("-----------------------------")
print("All Finished!")
print("-----------------------------")

# In[25]:

print(f"The first response is {json.dumps(response, indent=2)}.")

# In[26]:

print(len(city_list))
print(len(cloud_list))
print(len(country_code_list))
print(len(date_list))
print(len(humidity_list))
print(len(lat_list))
print(len(lng_list))
print(len(max_temp_list))
print(len(date_list))
print(len(wind_speed_list))

# In[57]:

# In[59]:

# Create a pandas dataframe using data retrieved
weather_df = pd.DataFrame({
                "City" : city_list,
                "Cloudiness" : cloud_list,
                "Country" : country_code_list,
                "Date" : date_list,
                "Humidity" : humidity_list,
                "Lat" : lat_list,
                "Lng" : lng_list,
                "Max Temp" : max_temp_list,
                "Wind Speed" : wind_speed_list
})

# In[58]:

weather_df.to_csv("output/cities_final.csv", index = False)

# In[60]:

weather_df

# In[61]:

weather_df.describe()

# In[64]:

# Create scatter plot
plt.scatter(weather_df["Lat"], weather_df["Max Temp"], facecolor = "red", edgecolor = "black")

# Set title, label and grid
plt.title("City Latitude vs. Maximum Temperature")
plt.xlabel("Laitude")
plt.ylabel("Maximum Temperature")
plt.grid(linewidth=.5, alpha = 1)

# Save as .pngs
plt.savefig("images/City Latitude vs Maximum Temperature.png")
print("The lower the latitude the more likely temperature is to drop")

# In[65]:

# Create scatter plot
plt.scatter(weather_df["Lat"], weather_df["Humidity"], facecolor = "yellow", edgecolor = "black")

# Set title, label and grid
plt.title("City Latitude vs. Humidity")
plt.xlabel("Laitude")
plt.ylabel("Humidity")
plt.grid(linewidth=.5, alpha = 1)

# Save as .pngs
plt.savefig("images/City Latitude vs. Humidity.png")
print("The higher latitude the higher the likelyhood of there being high humidity")

# In[42]:

# Create scatter plot
plt.scatter(weather_df["Lat"], weather_df["Cloudiness"], facecolor = "skyblue", edgecolor = "black")

# Set title, label and grid
plt.title("City Latitude vs. Cloudiness")
plt.xlabel("Laitude")
plt.ylabel("Cloudiness")
plt.grid(linewidth=.5, alpha = 1)

# Save as .pngs
plt.savefig("images/City Latitude vs. Cloudiness.png")
print("There doesn't appear to be much of a correlation betweent the two")

# In[43]:

# Create scatter plot
plt.scatter(weather_df["Lat"], weather_df["Wind Speed"], facecolor = "purple", edgecolor = "black")

# Set title, label and grid
plt.title("City Latitude vs. Wind Speed")
plt.xlabel("Laitude")
plt.ylabel("Wind Speed")
plt.grid(linewidth=.5, alpha = 1)

# Save as .pngs
plt.savefig("images/City Latitude vs. Wind Speed.png")
print("There are slightly higher wind speeds at higher latitudes")

# In[44]:

from datetime import datetime
ts = int("1611860897")
print(datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d'))

# In[45]:

weather_df['date'] = pd.to_datetime(weather_df['Date'],unit='s')

# In[46]:

weather_df

# In[47]:

#Sepperate northern and southern by latitude
Northern_Hem = weather_df[weather_df.Lat >= 0]
Southern_Hem = weather_df[weather_df.Lat <= 0]

# In[48]:

from sklearn.linear_model import LinearRegression

# In[77]:

#Create variables for northern and southern , temperature,humidity,coudiness and wind speed.
Lat_N = Northern_Hem.iloc[:, 5].values.reshape(-1, 1)
Lat_S = Southern_Hem.iloc[:, 5].values.reshape(-1, 1)

Temp_N = Northern_Hem.iloc[:, 7].values.reshape(-1, 1)
Temp_S = Southern_Hem.iloc[:, 7].values.reshape(-1, 1)

Humidity_N = Northern_Hem.iloc[:, 4].values.reshape(-1, 1)
Humidity_S = Southern_Hem.iloc[:, 4].values.reshape(-1, 1)

Cloudiness_N = Northern_Hem.iloc[:, 1].values.reshape(-1, 1)
Cloudiness_S = Southern_Hem.iloc[:, 1].values.reshape(-1, 1)

Wind_Speed_N = Northern_Hem.iloc[:, 8].values.reshape(-1, 1)
Wind_Speed_S = Southern_Hem.iloc[:, 8].values.reshape(-1, 1)

#Create predictor line
linear_regressor = LinearRegression()
linear_regressor.fit(Lat_N, Temp_N)
Temp_N_Predict = linear_regressor.predict(Lat_N)

#Create plot and set labels, title and grid
plt.scatter(Lat_N, Temp_N, facecolor = "red", edgecolor = "black")
plt.title("Northern Hemisphere - Max Temp vs. Latitude Linear Regression")
plt.xlabel("Latitude")
plt.ylabel("Max Temp (F)")
plt.plot(Lat_N, Temp_N_Predict, color='blue')
plt.grid(linewidth=.5, alpha = 1)
plt.show()

plt.savefig("images/Northern Hemisphere - Max Temp vs. Latitude Linear Regression.png")

# In[78]:

#Create predictor line
linear_regressor = LinearRegression()
linear_regressor.fit(Lat_S, Temp_S)
Temp_S_Predict = linear_regressor.predict(Lat_S)

#Create plot and set labels, title and grid
plt.scatter(Lat_S, Temp_S, facecolor = "red", edgecolor = "black")
plt.title("Southern Hemisphere - Max Temp vs. Latitude Linear Regression")
plt.xlabel("Latitude")
plt.ylabel("Max Temp (F)")
plt.plot(Lat_S, Temp_S_Predict, color='red')
plt.grid(linewidth=.5, alpha = 1)
plt.show()

plt.savefig("images/Southern Hemisphere - Max Temp vs. Latitude Linear Regression.png")

# In[79]:

#Create predictor line
linear_regressor = LinearRegression()
linear_regressor.fit(Lat_S, Humidity_S)
Humidity_S_Predict = linear_regressor.predict(Lat_S)

#Create plot and set labels, title and grid
plt.scatter(Lat_S, Humidity_S, facecolor = "yellow", edgecolor = "black")
plt.title("Southern Hemisphere - Humidity vs. Latitude Linear Regression")
plt.xlabel("Latitude")
plt.ylabel("Humidity")
plt.plot(Lat_S, Humidity_S_Predict, color='red')
plt.grid(linewidth=.5, alpha = 1)
plt.show()

plt.savefig("images/Southern Hemisphere - Humidity vs. Latitude Linear Regression.png")

# In[80]:

#Create predictor line
linear_regressor = LinearRegression()
linear_regressor.fit(Lat_N, Humidity_N)
Humidity_N_Predict = linear_regressor.predict(Lat_N)

#Create plot and set labels, title and grid
plt.scatter(Lat_N, Humidity_N, facecolor = "yellow", edgecolor = "black")
plt.title("Northern Hemisphere - Humidity vs. Latitude Linear Regression")
plt.xlabel("Latitude")
plt.ylabel("Humidity")
plt.plot(Lat_N, Humidity_N_Predict, color='blue')
plt.grid(linewidth=.5, alpha = 1)
plt.show()

plt.savefig("images/Northern Hemisphere - Humidity vs. Latitude Linear Regression.png")

# In[81]:

#Create predictor line
linear_regressor = LinearRegression()
linear_regressor.fit(Lat_S, Cloudiness_S)
Cloudiness_S_Predict = linear_regressor.predict(Lat_S)

#Create plot and set labels, title and grid
plt.scatter(Lat_S, Cloudiness_S, facecolor = "skyblue", edgecolor = "black")
plt.title("Southern Hemisphere - Cloudiness vs. Latitude Linear Regression")
plt.xlabel("Latitude")
plt.ylabel("Cloudiness")
plt.plot(Lat_S, Cloudiness_S_Predict, color='red')
plt.grid(linewidth=.5, alpha = 1)
plt.show()

plt.savefig("images/Southern Hemisphere - Cloudiness vs. Latitude Linear Regression.png")

# In[82]:

#Create predictor line
linear_regressor = LinearRegression()
linear_regressor.fit(Lat_N, Cloudiness_N)
Cloudiness_N_Predict = linear_regressor.predict(Lat_N)

#Create plot and set labels, title and grid
plt.scatter(Lat_N, Cloudiness_N, facecolor = "skyblue", edgecolor = "black")
plt.title("Northern Hemisphere - Cloudiness vs. Latitude Linear Regression")
plt.xlabel("Latitude")
plt.ylabel("Cloudiness")
plt.plot(Lat_N, Cloudiness_N_Predict, color='blue')
plt.grid(linewidth=.5, alpha = 1)
plt.show()

plt.savefig("Northern Hemisphere - Cloudiness vs. Latitude Linear Regression.png")

# In[83]:

#Create predictor line
linear_regressor = LinearRegression()
linear_regressor.fit(Lat_N, Wind_Speed_N)
Wind_Speed_N_Predict = linear_regressor.predict(Lat_N)

#Create plot and set labels, title and grid
plt.scatter(Lat_N, Wind_Speed_N, facecolor = "purple", edgecolor = "black")
plt.title("Northern Hemisphere - Wind_Speed vs. Latitude Linear Regression")
plt.xlabel("Latitude")
plt.ylabel("Wind_Speed")
plt.plot(Lat_N, Wind_Speed_N_Predict, color='blue')
plt.grid(linewidth=.5, alpha = 1)
plt.show()

plt.savefig("Northern Hemisphere - Wind_Speed vs. Latitude Linear Regression.png")

# In[84]:

#Create predictor line
linear_regressor = LinearRegression()
linear_regressor.fit(Lat_S, Wind_Speed_S)
Wind_Speed_S_Predict = linear_regressor.predict(Lat_S)

#Create plot and set labels, title and grid
plt.scatter(Lat_S, Wind_Speed_S, facecolor = "purple", edgecolor = "black")
plt.title("Southern Hemisphere - Wind_Speed vs. Latitude Linear Regression")
plt.xlabel("Latitude")
plt.ylabel("Wind_Speed")
plt.plot(Lat_S, Wind_Speed_S_Predict, color='red')
plt.grid(linewidth=.5, alpha = 1)
plt.show()

plt.savefig("Southern Hemisphere - Wind_Speed vs. Latitude Linear Regression.png")

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:


import os
import sys
import requests
import time
import random
import yaml
from fake_useragent import UserAgent

current_path = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.split(current_path)[0]
sys.path.append(rootPath)

from settings import CONF_DIR
from auxiliary.LogRecord import Logger

logger = Logger().logger

class ApiRequest:
    """
    抓取信息基类
    """

    def __init__(self):
        self.time_out = 20  # 访问链接超时设置
        self.num = 0  # 计算访问次数，用于访问次数限制
        self.proxies = {
            'http': 'http://192.168.2.251:1080',
            'https': 'https://192.168.2.251:1080',
        }

    def answer_the_url(self, url):
        """
        :param url: request url
        :return: return response
        """
        use_time = 0  # 计算同一个链接访问限制，当访问同一链接超过10次时就跳过该链接
        response = None
        # 读取source文件,获取headers
        with open(CONF_DIR + '/conf.yaml', 'r') as f:
            config_dict = yaml.load(f, Loader=yaml.FullLoader)
        headers_dict = config_dict["headers"]
        # headers_dict['user-agent'] = UserAgent().random
        while True:
            try:
                if self.num >= 500:
                    # 超过500次，等待10s
                    time.sleep(10)
                    self.num = 0
                response = requests.get(url=url, timeout=self.time_out, headers=headers_dict, proxies=self.proxies)
                self.num += 1
                break
            except Exception as e:
                time.sleep(random.random() * 2.0 + 0.3)
                use_time += 1
                if use_time > 10:
                    logger.error("<answer_the_url>" + str(e) + url)
                    break
                time.sleep(60)
                continue
        return response


import requests
from nu.modules.config import query_config

queryConfig = query_config()

class WeatherForecastApi:

    def __init__(self, key, location):
        self.key = key
        self.setLocation(location)

    def setLocation(self, location):
        self.location = location

    def current(self):
        response = self._current()
        data = 'unknown'
        if response != None:
            data = str(int(response['main']['temp'])) + ' Celsius with ' + response['weather'][0]['description']
        return data

    def forecastToday(self):
      response = self._forecast()
      data = 'unknown'
      if response != None:
          data = response['list'][0]['weather'][0]['description'] + ' with a minimum of ' + str(int(response['list'][0]['main']['temp_min'])) + ' and a maximum of ' + str(int(response['list'][0]['main']['temp_max'])) + ' Celsius'
      return data

    def forecastTomorrow(self):
      response = self._forecast()
      data = 'unknown'
      if response != None:
          data = response['list'][1]['weather'][0]['description'] + ' with a minimum of ' + str(int(response['list'][1]['main']['temp_min'])) + ' and a maximum of ' + str(int(response['list'][1]['main']['temp_max'])) + ' Celsius'
      return data

    # http://api.openweathermap.org/data/2.5/weather?q=Montreal&units=metric&APPID=f4b293fa455d861843e2afb0acd35852
    def _current(self):
        req = requests.get('http://api.openweathermap.org/data/2.5/weather?q=' + self.location + '&units=metric&APPID=' + self.key)
        if req.status_code == requests.codes.ok:
            return req.json()
        else:
            return None

    # http://api.openweathermap.org/data/2.5/forecast?q=Montreal&units=metric&APPID=f4b293fa455d861843e2afb0acd35852
    def _forecast(self):
        req = requests.get('http://api.openweathermap.org/data/2.5/forecast?q=' + self.location + '&units=metric&APPID=' + self.key)
        if req.status_code == requests.codes.ok:
            return req.json()
        else:
            return None

WeatherForecast = WeatherForecastApi(queryConfig.get('WeatherForecast', 'apiKey'), queryConfig.get('WeatherForecast', 'location'))


import sys
from collections import defaultdict
si = sys.stdin.readline

n = int(si())
# 딕셔너리의 기본값으로 빈 리스트 지정
x_dict = defaultdict(list)
y_dict = defaultdict(list)
result = 0

for _ in range(n):
    x, y = map(int, si().split())
    x_dict[x].append(y) # x좌표가 같은 y값끼리 하나의 리스트에 담기게 됨.
    y_dict[y].append(x) # y좌표가 같은 x값끼리 하나의 리스트에 담기게 됨.

# key값 마다 해당 value 리스트의 길이가 2를 넘어가면 전부 같은 직선으로 간주
for key in x_dict:
    if len(x_dict[key]) >= 2:
        result += 1

for key in y_dict:
    if len(y_dict[key]) >= 2:
        result += 1

print(result)

11_자료 구조/평행선.py