<commit_before># -*- coding: utf-8 -*-
##############################################
#
# ChriCar Beteiligungs- und Beratungs- GmbH
# Copyright (C) ChriCar Beteiligungs- und Beratungs- GmbH
# all rights reserved
# created 2008-07-05
#
###############################################
from osv import fields,osv
from tools.translate import _
import one2many_sorted
import logging

class res_partner_parent_company(osv.osv):
     _name  = "res.partner.parent_company"
     _order = "valid_from"

     def _children_ids(self, cr, uid, ids, name, args, context=None):
        res = {}
        for line in self.browse(cr, uid, ids, context=context):
           if line.partner_id.participation_ids:
             for participation in line.partner_id.participation_ids:
               res += participation.id
        return res

     _columns = \
         { 'name'               : fields.float   ('Share', digits=(16,2), required=True)
         , 'paid_in'            : fields.float   ('Paid in Share', digits=(16,2), required=True)
         , 'agio'               : fields.float   ('Agio', digits=(16,2), required=True)
         , 'percentage'         : fields.float   ('Percentage', digits=(9,6))
         , 'partner_id'         : fields.many2one('res.partner', 'Partner', required=True)
         , 'partner_parent_id'  : fields.many2one('res.partner', 'Parent', required=True)
         , 'valid_from'	        : fields.date    ('Valid From')
         , 'valid_until'	    : fields.date    ('Valid Until')
         , 'valid_fiscal_from'  : fields.date    ('Fiscal Valid From')
         , 'valid_fiscal_until' : fields.date    ('Fiscal Valid Until')
         , 'contract_date'      : fields.date    ('Contract Date')
         , 'contract_number'    : fields.char    ('Contract Number', size=32)
         , 'consolidation'      : fields.boolean ('Consolidation')
         , 'comment'            : fields.text    ('Notes')
         , 'active'             : fields.boolean ('Active')
         , 'state'              : fields.char    ('State', size=16)
         #, 'children_ids'       : fields.function(_children_ids, method=True, string='Participations'),
         #, 'children_ids'       :fields.one2many('res.partner.parent_company', 'parent_id','Children Items'),
         }
     _defaults = \
         { 'active'     : lambda *a: True
         , 'name'       : lambda *a: 0.0
         , 'paid_in'    : lambda *a: 0.0
         , 'agio'       : lambda *a: 0.0
         , 'percentage' : lambda *a: 0.0
         }

     def cmp_func(self):
         def cmp (a,b) :
             if a.partner_id.name == b.partner_id.name :
                 if a.valid_from == b.valid_from :
                     return 0
                 elif a.valid_from < b.valid_from :
                     return -1
                 else :
                     return 1
             elif a.partner_id.name < b.partner_id.name :
                 return -1
             else :
                 return 1

         return cmp
    # end def cmp_func
res_partner_parent_company()

class res_partner(osv.osv) :

    _inherit = "res.partner"

    def _get_share(self, cr, uid, share_owner_id, partner_id, share, owner_share, consolidate):
        _logger = logging.getLogger(__name__)
        share_child = share
        owners_share = owner_share
        for partner in self.browse(cr, uid, [partner_id]):
            self._logger.debug('partner %s share %s' %( partner.name, share) )
            for parent_share in partner.partner_current_ids:
                self._logger.debug('A partner %s parent %s share %s' %( parent_share.partner_id.name,parent_share.partner_parent_id.name, parent_share.percentage ) )
                if parent_share.percentage >0 :
                    if not share_child:
                        share = parent_share.percentage
                    else:
                        share = share_child * parent_share.percentage / 100
                else:
                    share = 0.0
                self._logger.debug('B partner %s share %s' %( parent_share.partner_id.name, share) )

                if parent_share.partner_parent_id.id == share_owner_id:
                    owners_share += share
                    self._logger.debug('FGF owner %s partner %s share-res %s' %(share_owner_id, partner.name, owners_share) )
                else:
                    owners_share = self._get_share(cr, uid, share_owner_id, parent_share.partner_parent_id.id,  share, owners_share, consolidate)
                    self._logger.debug('FGF not owner %s partner %s share-res %s' %(share_owner_id, partner.name, owners_share) )

        return owners_share

    def _get_owners_share(self, cr, uid, ids, name, args, context):
        if not context:
            context = {}
        res = {}
        for partner in self.browse(cr, uid, ids):
            if context.get('share_owner_id'):
               res[partner.id] = self._get_share(cr, uid, context['share_owner_id'], partner.id, None, 0, 'True')
            else:
               res[partner.id] = 0
        return res

    _columns = \
        { 'partner_ids'               : fields.one2many
            ('res.partner.parent_company','partner_id','Parent Companies')
        , 'partner_current_ids'       : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_id'
            , 'Parent Companies Current'
            , search = [('valid_until', '=', False)]
            , order  = 'partner_parent_id.name,valid_from'
            )
        , 'participation_ids'         : fields.one2many
            ('res.partner.parent_company','partner_parent_id','Participations')
        , 'participation_current_ids' : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_parent_id'
            , 'Participations Current'
            , search = [('valid_until', '=', False)]
            , order  = 'partner_id.name,valid_from'
            )
        , 'owners_share'             : fields.function(_get_owners_share, digits=(9,6), string='Owners Share', help="multi-level Owner share")

        }
res_partner()

    <commit_msg>[FIX] owners share<commit_after># -*- coding: utf-8 -*-
##############################################
#
# ChriCar Beteiligungs- und Beratungs- GmbH
# Copyright (C) ChriCar Beteiligungs- und Beratungs- GmbH
# all rights reserved
# created 2008-07-05
#
###############################################
from osv import fields,osv
from tools.translate import _
import one2many_sorted
import logging

class res_partner_parent_company(osv.osv):
     _name  = "res.partner.parent_company"
     _order = "valid_from"

     def _children_ids(self, cr, uid, ids, name, args, context=None):
        res = {}
        for line in self.browse(cr, uid, ids, context=context):
           if line.partner_id.participation_ids:
             for participation in line.partner_id.participation_ids:
               res += participation.id
        return res

     _columns = \
         { 'name'               : fields.float   ('Share', digits=(16,2), required=True)
         , 'paid_in'            : fields.float   ('Paid in Share', digits=(16,2), required=True)
         , 'agio'               : fields.float   ('Agio', digits=(16,2), required=True)
         , 'percentage'         : fields.float   ('Percentage', digits=(9,6))
         , 'partner_id'         : fields.many2one('res.partner', 'Partner', required=True)
         , 'partner_parent_id'  : fields.many2one('res.partner', 'Parent', required=True)
         , 'valid_from'	        : fields.date    ('Valid From')
         , 'valid_until'	    : fields.date    ('Valid Until')
         , 'valid_fiscal_from'  : fields.date    ('Fiscal Valid From')
         , 'valid_fiscal_until' : fields.date    ('Fiscal Valid Until')
         , 'contract_date'      : fields.date    ('Contract Date')
         , 'contract_number'    : fields.char    ('Contract Number', size=32)
         , 'consolidation'      : fields.boolean ('Consolidation')
         , 'comment'            : fields.text    ('Notes')
         , 'active'             : fields.boolean ('Active')
         , 'state'              : fields.char    ('State', size=16)
         #, 'children_ids'       : fields.function(_children_ids, method=True, string='Participations'),
         #, 'children_ids'       :fields.one2many('res.partner.parent_company', 'parent_id','Children Items'),
         }
     _defaults = \
         { 'active'     : lambda *a: True
         , 'name'       : lambda *a: 0.0
         , 'paid_in'    : lambda *a: 0.0
         , 'agio'       : lambda *a: 0.0
         , 'percentage' : lambda *a: 0.0
         }

     def cmp_func(self):
         def cmp (a,b) :
             if a.partner_id.name == b.partner_id.name :
                 if a.valid_from == b.valid_from :
                     return 0
                 elif a.valid_from < b.valid_from :
                     return -1
                 else :
                     return 1
             elif a.partner_id.name < b.partner_id.name :
                 return -1
             else :
                 return 1

         return cmp
    # end def cmp_func
res_partner_parent_company()

class res_partner(osv.osv) :

    _inherit = "res.partner"

    def _get_share(self, cr, uid, share_owner_id, partner_id, share, owner_share, consolidate):
        _logger = logging.getLogger(__name__)
        share_child = share
        owners_share = owner_share
        for partner in self.browse(cr, uid, [partner_id]):
            self._logger.debug('partner %s share %s' %( partner.name, share) )
            for parent_share in partner.partner_current_ids:
                self._logger.debug('A partner %s parent %s share %s' %( parent_share.partner_id.name,parent_share.partner_parent_id.name, parent_share.percentage ) )
                if parent_share.percentage >0 :
                    if not share_child:
                        share = parent_share.percentage
                    else:
                        share = share_child * parent_share.percentage / 100
                else:
                    share = 0.0
                self._logger.debug('B partner %s share %s' %( parent_share.partner_id.name, share) )

                if parent_share.partner_parent_id.id == share_owner_id:
                    owners_share += share
                    self._logger.debug('FGF owner %s partner %s share-res %s' %(share_owner_id, partner.name, owners_share) )
                else:
                    owners_share = self._get_share(cr, uid, share_owner_id, parent_share.partner_parent_id.id,  share, owners_share, consolidate)
                    self._logger.debug('FGF not owner %s partner %s share-res %s' %(share_owner_id, partner.name, owners_share) )

        return owners_share

    def _get_owners_share(self, cr, uid, ids, name, args, context):
        if not context:
            context = {}
        res = {}
        for partner in self.browse(cr, uid, ids):
            if context.get('share_owner_id'):
               res[partner.id] = self._get_share(cr, uid, context['share_owner_id'], partner.id, None, 0.0, 'True')
            else:
               res[partner.id] = 0
        return res

    _columns = \
        { 'partner_ids'               : fields.one2many
            ('res.partner.parent_company','partner_id','Parent Companies')
        , 'partner_current_ids'       : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_id'
            , 'Parent Companies Current'
            , search = [('valid_until', '=', False)]
            , order  = 'partner_parent_id.name,valid_from'
            )
        , 'participation_ids'         : fields.one2many
            ('res.partner.parent_company','partner_parent_id','Participations')
        , 'participation_current_ids' : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_parent_id'
            , 'Participations Current'
            , search = [('valid_until', '=', False)]
            , order  = 'partner_id.name,valid_from'
            )
        , 'owners_share'             : fields.function(_get_owners_share, digits=(9,6), string='Owners Share', help="multi-level Owner share")

        }
res_partner()

    <|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################
#
# ChriCar Beteiligungs- und Beratungs- GmbH
# Copyright (C) ChriCar Beteiligungs- und Beratungs- GmbH
# all rights reserved
# created 2008-07-05
#
###############################################
from osv import fields,osv
from tools.translate import _
import one2many_sorted
import logging

class res_partner_parent_company(osv.osv):
     _name  = "res.partner.parent_company"
     _order = "valid_from"

     def _children_ids(self, cr, uid, ids, name, args, context=None):
        res = {}
        for line in self.browse(cr, uid, ids, context=context):
           if line.partner_id.participation_ids:
             for participation in line.partner_id.participation_ids:
               res += participation.id
        return res

     _columns = \
         { 'name'               : fields.float   ('Share', digits=(16,2), required=True)
         , 'paid_in'            : fields.float   ('Paid in Share', digits=(16,2), required=True)
         , 'agio'               : fields.float   ('Agio', digits=(16,2), required=True)
         , 'percentage'         : fields.float   ('Percentage', digits=(9,6))
         , 'partner_id'         : fields.many2one('res.partner', 'Partner', required=True)
         , 'partner_parent_id'  : fields.many2one('res.partner', 'Parent', required=True)
         , 'valid_from'	        : fields.date    ('Valid From')
         , 'valid_until'	    : fields.date    ('Valid Until')
         , 'valid_fiscal_from'  : fields.date    ('Fiscal Valid From')
         , 'valid_fiscal_until' : fields.date    ('Fiscal Valid Until')
         , 'contract_date'      : fields.date    ('Contract Date')
         , 'contract_number'    : fields.char    ('Contract Number', size=32)
         , 'consolidation'      : fields.boolean ('Consolidation')
         , 'comment'            : fields.text    ('Notes')
         , 'active'             : fields.boolean ('Active')
         , 'state'              : fields.char    ('State', size=16)
         #, 'children_ids'       : fields.function(_children_ids, method=True, string='Participations'),
         #, 'children_ids'       :fields.one2many('res.partner.parent_company', 'parent_id','Children Items'),
         }
     _defaults = \
         { 'active'     : lambda *a: True
         , 'name'       : lambda *a: 0.0
         , 'paid_in'    : lambda *a: 0.0
         , 'agio'       : lambda *a: 0.0
         , 'percentage' : lambda *a: 0.0
         }

     def cmp_func(self):
         def cmp (a,b) :
             if a.partner_id.name == b.partner_id.name :
                 if a.valid_from == b.valid_from :
                     return 0
                 elif a.valid_from < b.valid_from :
                     return -1
                 else :
                     return 1
             elif a.partner_id.name < b.partner_id.name :
                 return -1
             else :
                 return 1

         return cmp
    # end def cmp_func
res_partner_parent_company()

class res_partner(osv.osv) :

    _inherit = "res.partner"
    _logger = logging.getLogger(__name__)

    def _get_share(self, cr, uid, share_owner_id, partner_id, share, owner_share, consolidate, level = 1):
        _logger = logging.getLogger(__name__)
        share_child = share
        owners_share = owner_share
        level_child = level
        for partner in self.browse(cr, uid, [partner_id]):
            self._logger.debug('partner %s share %s' %( partner.name, share) )

            for parent_share in partner.partner_current_ids:
                self._logger.debug('A parent %s share %s share_child %s' %( parent_share.partner_parent_id.name, parent_share.percentage, share_child ) )
                if parent_share.percentage >0 :
                    if share_child != None:
                        share = share_child * parent_share.percentage / 100
                    else:
                        share = parent_share.percentage
                else:
                    share = 0.0
                self._logger.debug('B partner %s share %s' %( parent_share.partner_id.name, share) )

                if parent_share.partner_parent_id.id == share_owner_id:
                  if consolidate == 'all':
                    owners_share += share
                    self._logger.debug('FGF owner %s partner %s share-res %s' %(share_owner_id, partner.name, owners_share) )
                  elif consolidate == 'direct' and level_child == 1:
                    owners_share += share
                  elif consolidate == 'consol' and parent_share.consolidation == True:
                    owners_share += share
                else:
                    level += 1
                    self._logger.debug('FGF A not owner - partner %s share %s share-res %s' %( partner.name, share, owners_share) )
                    owners_share = self._get_share(cr, uid, share_owner_id, parent_share.partner_parent_id.id,  share, owners_share, consolidate, level)
                    self._logger.debug('FGF B not owner - partner %s share %s share-res %s' %( partner.name, share, owners_share) )

        return owners_share

    def _get_owners_share(self, cr, uid, ids, name, args, context):
        if not context:
            context = {}
        res = {}
        for partner in self.browse(cr, uid, ids):
            if context.get('share_owner_id'):
               res[partner.id] = self._get_share(cr, uid, context['share_owner_id'], partner.id, None, 0.0, 'all')
            else:
               res[partner.id] = 0
        return res

    def _get_owners_direct_share(self, cr, uid, ids, name, args, context):
        if not context:
            context = {}
        res = {}
        for partner in self.browse(cr, uid, ids):
            if context.get('share_owner_id'):
               res[partner.id] = self._get_share(cr, uid, context['share_owner_id'], partner.id, None, 0.0, 'direct' )
            else:
               res[partner.id] = 0
        return res

    def _get_owners_consol_share(self, cr, uid, ids, name, args, context):
        if not context:
            context = {}
        res = {}
        for partner in self.browse(cr, uid, ids):
            if context.get('share_owner_id'):
               res[partner.id] = self._get_share(cr, uid, context['share_owner_id'], partner.id, None, 0.0, 'consol')
            else:
               res[partner.id] = 0
        return res

    _columns = \
        { 'partner_ids'               : fields.one2many
            ('res.partner.parent_company','partner_id','Parent Companies')
        , 'partner_current_ids'       : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_id'
            , 'Parent Companies Current'
            , search = [('valid_until', '=', False)]
            , order  = 'partner_parent_id.name,valid_from'
            )
        , 'participation_ids'         : fields.one2many
            ('res.partner.parent_company','partner_parent_id','Participations')
        , 'participation_current_ids' : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_parent_id'
            , 'Participations Current'
            , search = [('valid_until', '=', False)]
            , order  = 'partner_id.name,valid_from'
            )
        , 'owners_share'             : fields.function(_get_owners_share, digits=(9,6), string='Owners Share', help="multi-level Owner share")
        , 'owners_direct_share'      : fields.function(_get_owners_direct_share, digits=(9,6), string='Owners Direct Share', help="Owners direct share")
        , 'owners_consol_share'      : fields.function(_get_owners_consol_share, digits=(9,6), string='Owners Share Consolidate', help="Owners share to consolidate")
        }
res_partner()

class res_company(osv.osv):
    _inherit = 'res.company'

    _columns = {
          'owners_share'             : fields.related('partner_id', 'owners_share', type='float', string='Owners Share', help="multi-level Owner share")
        , 'owners_direct_share'      : fields.related('partner_id', 'owners_direct_share', type='float', string='Owners Direct Share', help="Owners direct share")
        , 'owners_consol_share'      : fields.related('partner_id', 'owners_consol_share', type='float',string='Owners Share Consolidate', help="Owners share to consolidate")
        }

res_company()

<commit_msg>[FIX] sort and filter<commit_after># -*- coding: utf-8 -*-
##############################################
#
# ChriCar Beteiligungs- und Beratungs- GmbH
# Copyright (C) ChriCar Beteiligungs- und Beratungs- GmbH
# all rights reserved
# created 2008-07-05
#
###############################################
from osv import fields,osv
from tools.translate import _
import one2many_sorted
import logging
import time

class res_partner_parent_company(osv.osv):
     _name  = "res.partner.parent_company"
     _order = "valid_from"

     def _children_ids(self, cr, uid, ids, name, args, context=None):
        res = {}
        for line in self.browse(cr, uid, ids, context=context):
           if line.partner_id.participation_ids:
             for participation in line.partner_id.participation_ids:
               res += participation.id
        return res

     _columns = \
         { 'name'               : fields.float   ('Share', digits=(16,2), required=True)
         , 'paid_in'            : fields.float   ('Paid in Share', digits=(16,2), required=True)
         , 'agio'               : fields.float   ('Agio', digits=(16,2), required=True)
         , 'percentage'         : fields.float   ('Percentage', digits=(9,6))
         , 'partner_id'         : fields.many2one('res.partner', 'Partner', required=True)
         , 'partner_parent_id'  : fields.many2one('res.partner', 'Parent', required=True)
         , 'valid_from'	        : fields.date    ('Valid From')
         , 'valid_until'	    : fields.date    ('Valid Until')
         , 'valid_fiscal_from'  : fields.date    ('Fiscal Valid From')
         , 'valid_fiscal_until' : fields.date    ('Fiscal Valid Until')
         , 'contract_date'      : fields.date    ('Contract Date')
         , 'contract_number'    : fields.char    ('Contract Number', size=32)
         , 'consolidation'      : fields.boolean ('Consolidation')
         , 'comment'            : fields.text    ('Notes')
         , 'active'             : fields.boolean ('Active')
         , 'state'              : fields.char    ('State', size=16)
         #, 'children_ids'       : fields.function(_children_ids, method=True, string='Participations'),
         #, 'children_ids'       :fields.one2many('res.partner.parent_company', 'parent_id','Children Items'),
         }
     _defaults = \
         { 'active'     : lambda *a: True
         , 'name'       : lambda *a: 0.0
         , 'paid_in'    : lambda *a: 0.0
         , 'agio'       : lambda *a: 0.0
         , 'percentage' : lambda *a: 0.0
         }

     def cmp_func(self):
         def cmp (a,b) :
             if a.partner_id.name == b.partner_id.name :
                 if a.valid_from == b.valid_from :
                     return 0
                 elif a.valid_from < b.valid_from :
                     return -1
                 else :
                     return 1
             elif a.partner_id.name < b.partner_id.name :
                 return -1
             else :
                 return 1

         return cmp
    # end def cmp_func
res_partner_parent_company()

class res_partner(osv.osv) :

    _inherit = "res.partner"
    _logger = logging.getLogger(__name__)

    def _get_share(self, cr, uid, share_owner_id, partner_id, share, owner_share, consolidate, level = 1):
        _logger = logging.getLogger(__name__)
        share_child = share
        owners_share = owner_share
        level_child = level
        for partner in self.browse(cr, uid, [partner_id]):
            self._logger.debug('partner %s share %s' %( partner.name, share) )

            for parent_share in partner.partner_current_ids:
                self._logger.debug('A parent %s share %s share_child %s' %( parent_share.partner_parent_id.name, parent_share.percentage, share_child ) )
                if parent_share.percentage >0 :
                    if share_child != None:
                        share = share_child * parent_share.percentage / 100
                    else:
                        share = parent_share.percentage
                else:
                    share = 0.0
                self._logger.debug('B partner %s share %s' %( parent_share.partner_id.name, share) )

                if parent_share.partner_parent_id.id == share_owner_id:
                  if consolidate == 'all':
                    owners_share += share
                    self._logger.debug('FGF owner %s partner %s share-res %s' %(share_owner_id, partner.name, owners_share) )
                  elif consolidate == 'direct' and level_child == 1:
                    owners_share += share
                  elif consolidate == 'consol' and parent_share.consolidation == True:
                    owners_share += share
                else:
                    level += 1
                    self._logger.debug('FGF A not owner - partner %s share %s share-res %s' %( partner.name, share, owners_share) )
                    owners_share = self._get_share(cr, uid, share_owner_id, parent_share.partner_parent_id.id,  share, owners_share, consolidate, level)
                    self._logger.debug('FGF B not owner - partner %s share %s share-res %s' %( partner.name, share, owners_share) )

        return owners_share

    def _get_owners_share(self, cr, uid, ids, name, args, context):
        if not context:
            context = {}
        res = {}
        for partner in self.browse(cr, uid, ids):
            if context.get('share_owner_id'):
               res[partner.id] = self._get_share(cr, uid, context['share_owner_id'], partner.id, None, 0.0, 'all')
            else:
               res[partner.id] = 0
        return res

    def _get_owners_direct_share(self, cr, uid, ids, name, args, context):
        if not context:
            context = {}
        res = {}
        for partner in self.browse(cr, uid, ids):
            if context.get('share_owner_id'):
               res[partner.id] = self._get_share(cr, uid, context['share_owner_id'], partner.id, None, 0.0, 'direct' )
            else:
               res[partner.id] = 0
        return res

    def _get_owners_consol_share(self, cr, uid, ids, name, args, context):
        if not context:
            context = {}
        res = {}
        for partner in self.browse(cr, uid, ids):
            if context.get('share_owner_id'):
               res[partner.id] = self._get_share(cr, uid, context['share_owner_id'], partner.id, None, 0.0, 'consol')
            else:
               res[partner.id] = 0
        return res

    _columns = \
        {
          'partner_ids'       : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_id'
            , 'Parent Companies'
            , order  = 'partner_parent_id.name,valid_from'
            )
        , 'partner_current_ids'       : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_id'
            , 'Parent Companies Current'
            , search = ['|',('valid_until', '=', False),('valid_until', '>',time.strftime('%Y-%m-%d')) ]
            , order  = 'partner_parent_id.name,valid_from'
            )
        , 'participation_ids' : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_parent_id'
            , 'Participations'
            , order  = 'partner_id.name,valid_from'
            )
        , 'participation_current_ids' : one2many_sorted.one2many_sorted
            ( 'res.partner.parent_company'
            , 'partner_parent_id'
            , 'Participations Current'
            , search = ['|',('valid_until', '=', False),('valid_until', '>',time.strftime('%Y-%m-%d')) ]
            , order  = 'partner_id.name,valid_from'
            )
        , 'owners_share'             : fields.function(_get_owners_share, digits=(9,6), string='Owners Share', help="multi-level Owner share")
        , 'owners_direct_share'      : fields.function(_get_owners_direct_share, digits=(9,6), string='Owners Direct Share', help="Owners direct share")
        , 'owners_consol_share'      : fields.function(_get_owners_consol_share, digits=(9,6), string='Owners Share Consolidate', help="Owners share to consolidate")
        }
res_partner()

class res_company(osv.osv):
    _inherit = 'res.company'

    _columns = {
          'owners_share'             : fields.related('partner_id', 'owners_share', type='float', string='Owners Share', help="multi-level Owner share")
        , 'owners_direct_share'      : fields.related('partner_id', 'owners_direct_share', type='float', string='Owners Direct Share', help="Owners direct share")
        , 'owners_consol_share'      : fields.related('partner_id', 'owners_consol_share', type='float',string='Owners Share Consolidate', help="Owners share to consolidate")
        }

res_company()

<|endoftext|>


<commit_before><commit_msg>import SW&T (closes #1496)<commit_after><|endoftext|>


<commit_before># "Do or do not. There is no try." — Yoda

def get_padawans(cohort):
    # add yourself to the temple trial roster
    data = {
        "R2Q4": {"skywalker": "PASSED" }}
    return data[cohort]

def test_trial_of_skill():
    assert get_padawans("R2Q4")["skywalker"]  == ""
<commit_msg>added name to skills_test<commit_after># "Do or do not. There is no try." — Yoda
# stdlib
from typing import Dict

def get_padawans(cohort: str) -> Dict[str, str]:
    # add yourself to the temple trial roster
    data = {"R2Q4": {"skywalker": "PASSED", "saffron": "PASSED"}}
    return data[cohort]

def test_trial_of_skill() -> None:
    assert get_padawans("R2Q4")["skywalker"] == "PASSED"
    assert get_padawans("R2Q4")["saffron"] == "PASSED"
<|endoftext|>


<commit_before>import sys
import json
import requests
import os
import mimetypes
import logging
from flask import render_template
from geobox.appstate import GeoBoxState
from geobox.defaults import GeoBoxConfig
from geobox.web import create_app

def static_files(basedir, attachments=None):
    if attachments is None:
        attachments = {}
    for subdir in ['css', 'js', 'img']:
        for root, _dirs, files in os.walk(os.path.join(basedir, subdir), followlinks=True):
            targetdir = root[len(basedir)+1:]
            for fname in files:
                source = os.path.join(root, fname)
                content_type = mimetypes.guess_type(source)[0]
                target = os.path.join(targetdir, fname)
                attachments[target] = {
                    'content_type': content_type,
                    'data': open(source, 'rb').read().encode('base64')
                }

    return attachments

def push_couchapp(attachments, couchurl, appname):
    doc = {
        '_attachments': attachments,
        'rewrites': [
            {'from': '/', 'to': '/index.html'},
            {'from': '/*', 'to': '*'},
        ],
    }

    resp = requests.head(couchurl + '/_design/' + appname,
        headers={'Accept': 'application/json'},
    )

    if resp.status_code == 200:
        doc['_rev'] = resp.headers['etag'].strip('"')

    app_url = couchurl + '/_design/' + appname
    resp = requests.put(app_url,
        headers={
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
        data=json.dumps(doc),
    )
    if resp.status_code != 201:
        log = logging.getLogger('geobox.offline')
        log.warn('failed to push app to %s: %s %s', app_url, resp, resp.content)

def main(config_filename=None):
    import optparse

    log = logging.getLogger('geobox.offline')
    requests_log = logging.getLogger("requests")
    requests_log.setLevel(logging.WARNING)
    logging.basicConfig(level=logging.DEBUG)

    parser = optparse.OptionParser()
    parser.description = 'Deploy the GBI-Client CouchApp to a CouchDB.'
    parser.add_option('--couchurl', help='url of couchdb', default='http://localhost:5984')
    parser.add_option('--dbname', help='database name')
    parser.add_option('--appname', help='name of couchapp, as in /dbname/_design/appname')
    options, args = parser.parse_args()

    if not options.dbname or not options.appname:
        parser.print_help()
        print >>sys.stderr, '\nERROR: --dbname and --appname required'
        sys.exit(1)

    if config_filename:
        config = GeoBoxConfig.from_file(config_filename)
        if not config:
            sys.exit(1)
        app_state = GeoBoxState(config)
    else:
        app_state = GeoBoxState()

    app = create_app(app_state)

    log.info('collecting files')
    attachments = {}
    with app.test_request_context('/'):
        editorhtml = render_template('editor.html', with_server=False)
        editorhtml = editorhtml.replace('"/static/', '"')
        editorhtml = editorhtml.replace('"/translations.js', '"translations.js')
        attachments['index.html'] = {
            'content_type': 'text/html',
            'data': editorhtml.encode('utf8').encode('base64')
        }

        translations = render_template('js/translation.js')
        attachments['translations.js'] = {
            'content_type': 'application/javascript',
            'data': translations.encode('utf8').encode('base64')
        }

    basedir = os.path.join(os.path.dirname(__file__), 'web', 'static')
    basedir = os.path.abspath(basedir)
    static_files(basedir, attachments)

    couchurl = options.couchurl.rstrip('/') + '/' + options.dbname
    log.info('pushing app to %s/_design/%s', couchurl, options.appname)
    push_couchapp(attachments, couchurl, options.appname)
    log.info('done. see %s/_design/%s/_rewrite', couchurl, options.appname)

if __name__ == '__main__':
    main()<commit_msg>Import logging in module<commit_after>import sys
import json
import requests
import os
import mimetypes
import logging
from flask import render_template
from geobox.appstate import GeoBoxState
from geobox.defaults import GeoBoxConfig
from geobox.web import create_app

import logging
log = logging.getLogger(__name__)

def static_files(basedir, attachments=None):
    if attachments is None:
        attachments = {}
    for subdir in ['css', 'js', 'img']:
        for root, _dirs, files in os.walk(os.path.join(basedir, subdir), followlinks=True):
            targetdir = root[len(basedir)+1:]
            for fname in files:
                source = os.path.join(root, fname)
                content_type = mimetypes.guess_type(source)[0]
                target = os.path.join(targetdir, fname)
                attachments[target] = {
                    'content_type': content_type,
                    'data': open(source, 'rb').read().encode('base64')
                }

    return attachments

def push_couchapp(attachments, couchurl, appname):
    doc = {
        '_attachments': attachments,
        'rewrites': [
            {'from': '/', 'to': '/index.html'},
            {'from': '/*', 'to': '*'},
        ],
    }

    resp = requests.head(couchurl + '/_design/' + appname,
        headers={'Accept': 'application/json'},
    )

    if resp.status_code == 200:
        doc['_rev'] = resp.headers['etag'].strip('"')

    app_url = couchurl + '/_design/' + appname
    resp = requests.put(app_url,
        headers={
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
        data=json.dumps(doc),
    )
    if resp.status_code != 201:
        log = logging.getLogger('geobox.offline')
        log.warn('failed to push app to %s: %s %s', app_url, resp, resp.content)

def main(config_filename=None):
    import optparse

    requests_log = logging.getLogger("requests")
    requests_log.setLevel(logging.WARNING)
    logging.basicConfig(level=logging.DEBUG)

    parser = optparse.OptionParser()
    parser.description = 'Deploy the GBI-Client CouchApp to a CouchDB.'
    parser.add_option('--couchurl', help='url of couchdb', default='http://localhost:5984')
    parser.add_option('--dbname', help='database name')
    parser.add_option('--appname', help='name of couchapp, as in /dbname/_design/appname')
    options, args = parser.parse_args()

    if not options.dbname or not options.appname:
        parser.print_help()
        print >>sys.stderr, '\nERROR: --dbname and --appname required'
        sys.exit(1)

    if config_filename:
        config = GeoBoxConfig.from_file(config_filename)
        if not config:
            sys.exit(1)
        app_state = GeoBoxState(config)
    else:
        app_state = GeoBoxState()

    app = create_app(app_state)

    log.info('collecting files')
    attachments = {}
    with app.test_request_context('/'):
        editorhtml = render_template('editor.html', with_server=False)
        editorhtml = editorhtml.replace('"/static/', '"')
        editorhtml = editorhtml.replace('"/translations.js', '"translations.js')
        attachments['index.html'] = {
            'content_type': 'text/html',
            'data': editorhtml.encode('utf8').encode('base64')
        }

        translations = render_template('js/translation.js')
        attachments['translations.js'] = {
            'content_type': 'application/javascript',
            'data': translations.encode('utf8').encode('base64')
        }

    basedir = os.path.join(os.path.dirname(__file__), 'web', 'static')
    basedir = os.path.abspath(basedir)
    static_files(basedir, attachments)

    couchurl = options.couchurl.rstrip('/') + '/' + options.dbname
    log.info('pushing app to %s/_design/%s', couchurl, options.appname)
    push_couchapp(attachments, couchurl, options.appname)
    log.info('done. see %s/_design/%s/_rewrite', couchurl, options.appname)

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


<commit_before># -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shoop Commerce Ltd. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

import hashlib
import itertools
from collections import defaultdict

import six
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _
from enumfields import Enum, EnumIntegerField
from parler.models import TranslatableModel, TranslatedFields

from shuup.core.fields import InternalIdentifierField
from shuup.utils.django_compat import force_bytes, force_text
from shuup.utils.models import SortableMixin

class ProductVariationLinkStatus(Enum):
    INVISIBLE = 0
    VISIBLE = 1

    class Labels:
        INVISIBLE = _('invisible')
        VISIBLE = _('visible')

@python_2_unicode_compatible
class ProductVariationVariable(TranslatableModel, SortableMixin):
    product = models.ForeignKey(
        "Product", related_name='variation_variables', on_delete=models.CASCADE, verbose_name=_("product"))
    identifier = InternalIdentifierField(unique=False)
    translations = TranslatedFields(
        name=models.CharField(max_length=128, verbose_name=_('name')),
    )

    class Meta:
        verbose_name = _('variation variable')
        verbose_name_plural = _('variation variables')
        unique_together = (("product", "identifier", ),)
        ordering = ('ordering', )

    def __str__(self):
        return force_text(self.safe_translation_getter("name") or self.identifier or repr(self))

@python_2_unicode_compatible
class ProductVariationVariableValue(TranslatableModel, SortableMixin):
    variable = models.ForeignKey(
        ProductVariationVariable, related_name='values', on_delete=models.CASCADE, verbose_name=_("variation variable"))
    identifier = InternalIdentifierField(unique=False)

    translations = TranslatedFields(
        value=models.CharField(max_length=128, verbose_name=_('value')),
    )

    class Meta:
        verbose_name = _('variation value')
        verbose_name_plural = _('variation values')
        unique_together = (("variable", "identifier", ),)
        ordering = ('ordering', )

    def __str__(self):
        return force_text(self.safe_translation_getter("value") or self.identifier or repr(self))

class ProductVariationResult(models.Model):
    product = models.ForeignKey(
        "Product", related_name='variation_result_supers', on_delete=models.CASCADE, verbose_name=_("product"))
    combination_hash = models.CharField(max_length=40, unique=True, db_index=True, verbose_name=_("combination hash"))
    result = models.ForeignKey(
        "Product", related_name='variation_result_subs', on_delete=models.CASCADE, verbose_name=_("result"))
    status = EnumIntegerField(
        ProductVariationLinkStatus, db_index=True, default=ProductVariationLinkStatus.VISIBLE, verbose_name=_("status"))

    @classmethod
    def resolve(cls, parent_product, combination):
        pvr = cls.objects.filter(
            product=parent_product,
            combination_hash=hash_combination(combination),
            status=ProductVariationLinkStatus.VISIBLE
        ).first()
        if pvr:
            return pvr.result

    class Meta:
        verbose_name = _('variation result')
        verbose_name_plural = _('variation results')

def hash_combination(combination):
    """
    Calculate the combination hash for a given mapping of variable PKs to value PKs.

    :param combination: Combination dict {pvv_pk: pvvv_pk}
    :type combination: dict[int, int]
    :return: Hash string
    :rtype: str
    """
    bits = []

    for variable, value in six.iteritems(combination):
        if isinstance(variable, six.integer_types) and isinstance(value, six.integer_types):
            bits.append("%d=%d" % (variable, value))
        else:
            bits.append("%d=%d" % (variable.pk, value.pk))

    bits.sort()
    raw_combination = ",".join(bits)
    hashed_combination = hashlib.sha1(force_bytes(raw_combination)).hexdigest()
    return hashed_combination

def get_combination_hash_from_variable_mapping(parent, variables):
    """
    Create a combination hash from a mapping of variable identifiers to value identifiers.

    If variables and values with the given identifier do not exist, they are created on the go.

    :param parent: Parent product
    :type parent: shuup.core.models.Product
    :param variables: Dict of {variable identifier: value identifier} for complex variable linkage
    :type variables: dict
    :return: Combination hash
    :rtype: str
    """
    mapping = {}
    for variable_identifier, value_identifier in variables.items():
        variable, _ = ProductVariationVariable.objects.get_or_create(
            product=parent, identifier=force_text(variable_identifier)
        )
        value, _ = ProductVariationVariableValue.objects.get_or_create(
            variable=variable, identifier=force_text(value_identifier)
        )
        mapping[variable] = value
    return hash_combination(mapping)

def get_all_available_combinations(product):
    results = product.get_available_variation_results()
    values_by_variable = defaultdict(list)
    values = (
        ProductVariationVariableValue.objects.filter(variable__product=product)
        .prefetch_related("variable").order_by("ordering")
    )
    for val in values:
        values_by_variable[val.variable].append(val)

    if not values_by_variable:
        return

    variables_list, value_sets_list = zip(*values_by_variable.items())

    for value_set_combo in itertools.product(*value_sets_list):
        variable_to_value = dict(zip(variables_list, value_set_combo))
        sorted_variable_to_value = sorted(variable_to_value.items(), key=lambda varval: varval[0].ordering)
        text_description = ", ".join(sorted("%s: %s" % (var, val) for (var, val) in sorted_variable_to_value))
        sku_part = "-".join(slugify(force_text(val))[:6] for (var, val) in sorted_variable_to_value)
        hash = hash_combination(variable_to_value)
        yield {
            "variable_to_value": variable_to_value,
            "hash": hash,
            "text_description": text_description,
            "sku_part": sku_part,
            "result_product_pk": results.get(hash)
        }
<commit_msg>Core: prefetch translations for product variations<commit_after># -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shoop Commerce Ltd. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

import hashlib
import itertools
from collections import defaultdict

import six
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _
from enumfields import Enum, EnumIntegerField
from parler.models import TranslatableModel, TranslatedFields

from shuup.core.fields import InternalIdentifierField
from shuup.utils.django_compat import force_bytes, force_text
from shuup.utils.models import SortableMixin

class ProductVariationLinkStatus(Enum):
    INVISIBLE = 0
    VISIBLE = 1

    class Labels:
        INVISIBLE = _('invisible')
        VISIBLE = _('visible')

@python_2_unicode_compatible
class ProductVariationVariable(TranslatableModel, SortableMixin):
    product = models.ForeignKey(
        "Product", related_name='variation_variables', on_delete=models.CASCADE, verbose_name=_("product"))
    identifier = InternalIdentifierField(unique=False)
    translations = TranslatedFields(
        name=models.CharField(max_length=128, verbose_name=_('name')),
    )

    class Meta:
        verbose_name = _('variation variable')
        verbose_name_plural = _('variation variables')
        unique_together = (("product", "identifier", ),)
        ordering = ('ordering', )

    def __str__(self):
        return force_text(self.safe_translation_getter("name") or self.identifier or repr(self))

@python_2_unicode_compatible
class ProductVariationVariableValue(TranslatableModel, SortableMixin):
    variable = models.ForeignKey(
        ProductVariationVariable, related_name='values', on_delete=models.CASCADE, verbose_name=_("variation variable"))
    identifier = InternalIdentifierField(unique=False)

    translations = TranslatedFields(
        value=models.CharField(max_length=128, verbose_name=_('value')),
    )

    class Meta:
        verbose_name = _('variation value')
        verbose_name_plural = _('variation values')
        unique_together = (("variable", "identifier", ),)
        ordering = ('ordering', )

    def __str__(self):
        return force_text(self.safe_translation_getter("value") or self.identifier or repr(self))

class ProductVariationResult(models.Model):
    product = models.ForeignKey(
        "Product", related_name='variation_result_supers', on_delete=models.CASCADE, verbose_name=_("product"))
    combination_hash = models.CharField(max_length=40, unique=True, db_index=True, verbose_name=_("combination hash"))
    result = models.ForeignKey(
        "Product", related_name='variation_result_subs', on_delete=models.CASCADE, verbose_name=_("result"))
    status = EnumIntegerField(
        ProductVariationLinkStatus, db_index=True, default=ProductVariationLinkStatus.VISIBLE, verbose_name=_("status"))

    @classmethod
    def resolve(cls, parent_product, combination):
        pvr = cls.objects.filter(
            product=parent_product,
            combination_hash=hash_combination(combination),
            status=ProductVariationLinkStatus.VISIBLE
        ).first()
        if pvr:
            return pvr.result

    class Meta:
        verbose_name = _('variation result')
        verbose_name_plural = _('variation results')

def hash_combination(combination):
    """
    Calculate the combination hash for a given mapping of variable PKs to value PKs.

    :param combination: Combination dict {pvv_pk: pvvv_pk}
    :type combination: dict[int, int]
    :return: Hash string
    :rtype: str
    """
    bits = []

    for variable, value in six.iteritems(combination):
        if isinstance(variable, six.integer_types) and isinstance(value, six.integer_types):
            bits.append("%d=%d" % (variable, value))
        else:
            bits.append("%d=%d" % (variable.pk, value.pk))

    bits.sort()
    raw_combination = ",".join(bits)
    hashed_combination = hashlib.sha1(force_bytes(raw_combination)).hexdigest()
    return hashed_combination

def get_combination_hash_from_variable_mapping(parent, variables):
    """
    Create a combination hash from a mapping of variable identifiers to value identifiers.

    If variables and values with the given identifier do not exist, they are created on the go.

    :param parent: Parent product
    :type parent: shuup.core.models.Product
    :param variables: Dict of {variable identifier: value identifier} for complex variable linkage
    :type variables: dict
    :return: Combination hash
    :rtype: str
    """
    mapping = {}
    for variable_identifier, value_identifier in variables.items():
        variable, _ = ProductVariationVariable.objects.get_or_create(
            product=parent, identifier=force_text(variable_identifier)
        )
        value, _ = ProductVariationVariableValue.objects.get_or_create(
            variable=variable, identifier=force_text(value_identifier)
        )
        mapping[variable] = value
    return hash_combination(mapping)

def get_all_available_combinations(product):
    results = product.get_available_variation_results()
    values_by_variable = defaultdict(list)
    values = (
        ProductVariationVariableValue.objects.filter(variable__product=product)
        .prefetch_related(
            "translations",
            "variable",
            "variable__translations"
        ).order_by("ordering")
    )
    for val in values:
        values_by_variable[val.variable].append(val)

    if not values_by_variable:
        return

    variables_list, value_sets_list = zip(*values_by_variable.items())

    for value_set_combo in itertools.product(*value_sets_list):
        variable_to_value = dict(zip(variables_list, value_set_combo))
        sorted_variable_to_value = sorted(variable_to_value.items(), key=lambda varval: varval[0].ordering)
        text_description = ", ".join(sorted("%s: %s" % (var, val) for (var, val) in sorted_variable_to_value))
        sku_part = "-".join(slugify(force_text(val))[:6] for (var, val) in sorted_variable_to_value)
        hash = hash_combination(variable_to_value)
        yield {
            "variable_to_value": variable_to_value,
            "hash": hash,
            "text_description": text_description,
            "sku_part": sku_part,
            "result_product_pk": results.get(hash)
        }
<|endoftext|>


<commit_before>#   Doc:
#   run this file - it starts HTTP Server (Thread 1)
#   post {nextState: 'init'} to Server:
#       - it starts Logic in state 'init' (Thread 2) which registers to Market
#
#   UI posts:
#       - {nextState: 'start'} --> Logic registers to producer etc. adds offers, switches to 'running'
#       - {nextState: 'stop'} --> Logic stops, switches to 'stopping'
#       - {nextState: 'start'} --> Logic continues changing prices, getting products, switches to 'running'
#       - {nextState: 'kill'} --> Logic unregisters to Market and termintes (exit Thread 2), switches to 'exiting'
#
#   To restart, use external Tool to post
#       - {nextState: 'init'}

import argparse
import json
import random
import threading
import time
from posixpath import join as urljoin
import traceback

import requests
from flask import Flask, request, Response
from flask_cors import CORS

settings = {
    'merchant_id': 0,
    'merchant_url': 'http://vm-mpws2016hp1-06.eaalab.hpi.uni-potsdam.de',
    'marketplace_url': 'http://vm-mpws2016hp1-04.eaalab.hpi.uni-potsdam.de',
    'producerEndpoint': 'http://vm-mpws2016hp1-03.eaalab.hpi.uni-potsdam.de',
    'priceDecrease': 1,
    'initialProducts': 5,
    'minPriceMargin': 16,
    'maxPriceMargin': 32,
    'shipping': 5,
    'primeShipping': 1,
    'debug': False
}

def get_from_list_by_key(dict_list, key, value):
    elements = [elem for elem in dict_list if elem[key] == value]
    if elements:
        return elements[0]
    return None

class MerchantLogic(object):
    def __init__(self):
        global settings
        self.execQueue = []
        self.state = 'init'
        self.interval = 5
        self.thread = None
        self.products = []
        self.offers = []
        self.request_session = requests.Session()
        adapter = requests.adapters.HTTPAdapter(pool_connections=100, pool_maxsize=100)
        self.request_session.mount('http://', adapter)

        self.merchantID = self.register_to_marketplace()
        settings.update({'merchant_id': self.merchantID})
        self.run_logic_loop()

    def run_logic_loop(self):
        self.interval = 3
        self.thread = threading.Thread(target=self.run, args=())
        self.thread.daemon = True  # Demonize thread
        self.thread.start()  # Start the execution

    def run(self):
        """ Method that runs forever """
        while not self.state == 'exiting':
            # default interval to avoid busy waiting, but merchant logic should still
            # be in charge of setting the interval (that is random, currently, but could change)
            self.interval = 5

            if self.state == 'running':
                self.interval = random.randint(2, 10) / 10.0
                try:
                    self.execute_logic()
                except Exception as e:
                    print('error on merchantLogic:\n', e)
                    traceback.print_exc()
                    print('safely stop Merchant')
                    self.stop()

            time.sleep(self.interval)

        self.on_exit()

    def game_init(self):
        self.products = self.get_initial_products()
        self.offers = []

        for product in self.products:
            newOffer = self.create_offer(product)
            newOffer['id'] = self.add_offer_to_marketplace(newOffer)
            self.offers.append(newOffer)

    def start(self):
        if self.state == 'init':
            self.game_init()
        self.state = 'running'

    def stop(self):
        self.state = 'stopping'

    def terminate(self):
        self.state = 'exiting'

    def on_exit(self):
        self.unregister_to_marketplace()

    def create_offer(self, product):
        return {
            "product_id": product['product_id'],
            "merchant_id": self.merchantID,
            "uid": product['uid'],
            "quality": product['quality'],
            "amount": product['amount'],
            "price": product['price'] + settings['maxPriceMargin'],
            "shipping_time": {
                "standard": settings['shipping'],
                "prime": settings['primeShipping']
            },
            "prime": True
        }

    def add_offer_to_marketplace(self, offer):
        url = urljoin(settings['marketplace_url'], 'offers')

        r = self.request_session.post(url, json=offer)
        return r.json()['offer_id']

    def register_to_marketplace(self):
        request_object = {
            "api_endpoint_url": settings['merchant_url'],
            "merchant_name": "Sample Merchant",
            "algorithm_name": "IncreasePrice"
        }
        url = urljoin(settings['marketplace_url'], 'merchants')

        r = self.request_session.post(url, json=request_object)
        print('registerToMarketplace', r.json())
        return r.json()['merchant_id']

    def unregister_to_marketplace(self):
        url = urljoin(settings['marketplace_url'], 'merchants/{:d}'.format(self.merchantID))
        print('unRegisterToMarketplace')

        self.request_session.delete(url)

    def get_initial_products(self):
        url = urljoin(settings['producerEndpoint'], 'buy?merchant_id={:d}'.format(self.merchantID))
        products = {}

        for i in range(settings['initialProducts']):
            r = self.request_session.get(url)
            product = r.json()
            if product['product_id'] in products:
                products[product['product_id']]['amount'] += 1
            else:
                products[product['product_id']] = product
        return list(products.values())

    def update_offer(self, new_offer):
        print('update offer:', new_offer)
        url = urljoin(settings['marketplace_url'], 'offers/{:d}'.format(new_offer['id']))

        try:
            self.request_session.put(url, json=new_offer)
        except Exception as e:
            print('failed to update offer', e)

    def adjust_prices(self, offer, min_price):
        if min_price < settings['minPriceMargin']:
            offer['price'] = settings['maxPriceMargin']
        else:
            offer['price'] = min(max(min_price - settings['priceDecrease'], settings['minPriceMargin']),
                                 settings['maxPriceMargin'])
        self.update_offer(offer)

    def get_offers(self, ):
        url = urljoin(settings['marketplace_url'], 'offers')

        r = self.request_session.get(url)
        offers = r.json()
        return offers

    def execute_logic(self):
        # execute queued methods
        tmp_queue = [e for e in self.execQueue]
        self.execQueue = []
        for method, kwargs in tmp_queue:
            method(*kwargs)

        offers = self.get_offers()
        for product in self.products:
            competitor_offers = []
            for offer in offers:
                if offer['merchant_id'] != self.merchantID and offer['product_id'] == product['product_id']:
                    competitor_offers.append(offer['price'])
            if len(competitor_offers) > 0:
                offer = get_from_list_by_key(self.offers, 'product_id', product['product_id'])
                self.adjust_prices(offer, min(competitor_offers))

    def sold_product(self, offer_id, amount, price):
        print('soldProduct')
        offer = [offer for offer in self.offers if offer['id'] == offer_id]
        if offer:
            offer = offer[0]
            print('found offer:', offer)
            offer['amount'] -= amount
            product = [product for product in self.products if product['product_id'] == offer['product_id']][0]
            print('found product:', product)

            product['amount'] -= amount
            if product['amount'] <= 0:
                print('product {:d} is out of stock!'.format(product['product_id']))

            # sample logic: TODO: improve
            self.buy_product_and_update_offer()

    def buy_product_and_update_offer(self):
        print('buy Product and update')
        new_product = self.buy_random_product()

        old_product = get_from_list_by_key(self.products, 'product_id', new_product['product_id'])
        if old_product:
            old_product['amount'] += 1
            offer = get_from_list_by_key(self.offers, 'product_id', new_product['product_id'])
            print('in this offer:', offer)
            url = urljoin(settings['marketplace_url'], 'offers/{:d}/restock'.format(offer['id']))
            offer['amount'] = old_product['amount']
            self.request_session.patch(url, json={'amount': 1})
        else:
            self.products.append(new_product)
            new_offer = self.create_offer(new_product)
            new_offer['id'] = self.add_offer_to_marketplace(new_offer)
            self.offers.append(new_offer)

    # returns product
    def buy_random_product(self):
        url = urljoin(settings['producerEndpoint'], 'buy?merchant_id={:d}'.format(self.merchantID))
        r = self.request_session.get(url)
        product = r.json()
        print('bought new product', product)
        return product

app = Flask(__name__)
CORS(app)
merchantLogic = None

def json_response(obj):
    js = json.dumps(obj)
    resp = Response(js, status=200, mimetype='application/json')
    return resp

@app.route('/settings', methods=['GET'])
def get_settings():
    return json_response(settings)

@app.route('/settings', methods=['PUT', 'POST'])
def put_settings():
    global settings
    new_settings = request.json
    settings.update(new_settings)
    return json_response(settings)

@app.route('/settings/execution', methods=['POST'])
def set_state():
    global settings
    global merchantLogic

    next_state = request.json['nextState']
    print(next_state)
    if next_state == 'init':
        # set endpoint url settings on init
        for keyword in ['merchant_url', 'marketplace_url']:
            if keyword in request.json:
                settings.update({keyword: request.json[keyword]})
        print('updated settings', settings)
        if merchantLogic:
            merchantLogic.terminate()
        print('new merchant')
        merchantLogic = MerchantLogic()
    elif next_state == 'start':
        print('merchant start')
        merchantLogic.start()
    elif next_state == 'stop':
        print('merchant stop')
        merchantLogic.stop()
    elif next_state == 'kill':
        print('merchant kill')
        merchantLogic.terminate()
        merchantLogic = None

    return json_response({})

@app.route('/sold', methods=['POST'])
def item_sold():
    global merchantLogic

    if merchantLogic:
        sent_json = request.get_json(force=True)
        # ignore 'consumer_id' and 'prime'
        # offer_id = int(sentJSON['offer_id'])
        # amount = int(sentJSON['amount'])
        # price = float(sentJSON['price'])

        offer_id = sent_json['offer_id']
        amount = sent_json['amount']
        price = sent_json['price']

        # consumer_id = sentJSON['consumer_id'] if 'consumer_id' in sentJSON else ''
        # prime = sentJSON['prime'] if 'prime' in sentJSON else ''

        print('sold {:d} items of the offer {:d} for {:f}'.format(amount, offer_id, price))
        merchantLogic.execQueue.append((merchantLogic.sold_product, (offer_id, amount, price)))
    else:
        print('merchantlogic not started')

    return json_response({})

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='PriceWars Merchant')
    parser.add_argument('--port', type=int,
                        help='port to bind flask App to')
    args = parser.parse_args()

    app.run(host='0.0.0.0', port=args.port)
<commit_msg>increasing poolsize due to weird resolution error in DNS<commit_after>#   Doc:
#   run this file - it starts HTTP Server (Thread 1)
#   post {nextState: 'init'} to Server:
#       - it starts Logic in state 'init' (Thread 2) which registers to Market
#
#   UI posts:
#       - {nextState: 'start'} --> Logic registers to producer etc. adds offers, switches to 'running'
#       - {nextState: 'stop'} --> Logic stops, switches to 'stopping'
#       - {nextState: 'start'} --> Logic continues changing prices, getting products, switches to 'running'
#       - {nextState: 'kill'} --> Logic unregisters to Market and termintes (exit Thread 2), switches to 'exiting'
#
#   To restart, use external Tool to post
#       - {nextState: 'init'}

import argparse
import json
import random
import threading
import time
from posixpath import join as urljoin
import traceback

import requests
from flask import Flask, request, Response
from flask_cors import CORS

settings = {
    'merchant_id': 0,
    'merchant_url': 'http://vm-mpws2016hp1-06.eaalab.hpi.uni-potsdam.de',
    'marketplace_url': 'http://vm-mpws2016hp1-04.eaalab.hpi.uni-potsdam.de',
    'producerEndpoint': 'http://vm-mpws2016hp1-03.eaalab.hpi.uni-potsdam.de',
    'priceDecrease': 1,
    'initialProducts': 5,
    'minPriceMargin': 16,
    'maxPriceMargin': 32,
    'shipping': 5,
    'primeShipping': 1,
    'debug': False
}

def get_from_list_by_key(dict_list, key, value):
    elements = [elem for elem in dict_list if elem[key] == value]
    if elements:
        return elements[0]
    return None

class MerchantLogic(object):
    def __init__(self):
        global settings
        self.execQueue = []
        self.state = 'init'
        self.interval = 5
        self.thread = None
        self.products = []
        self.offers = []
        self.request_session = requests.Session()
        adapter = requests.adapters.HTTPAdapter(pool_connections=300, pool_maxsize=300)
        self.request_session.mount('http://', adapter)

        self.merchantID = self.register_to_marketplace()
        settings.update({'merchant_id': self.merchantID})
        self.run_logic_loop()

    def run_logic_loop(self):
        self.interval = 3
        self.thread = threading.Thread(target=self.run, args=())
        self.thread.daemon = True  # Demonize thread
        self.thread.start()  # Start the execution

    def run(self):
        """ Method that runs forever """
        while not self.state == 'exiting':
            # default interval to avoid busy waiting, but merchant logic should still
            # be in charge of setting the interval (that is random, currently, but could change)
            self.interval = 5

            if self.state == 'running':
                self.interval = random.randint(2, 10) / 10.0
                try:
                    self.execute_logic()
                except Exception as e:
                    print('error on merchantLogic:\n', e)
                    traceback.print_exc()
                    print('safely stop Merchant')
                    self.stop()

            time.sleep(self.interval)

        self.on_exit()

    def game_init(self):
        self.products = self.get_initial_products()
        self.offers = []

        for product in self.products:
            newOffer = self.create_offer(product)
            newOffer['id'] = self.add_offer_to_marketplace(newOffer)
            self.offers.append(newOffer)

    def start(self):
        if self.state == 'init':
            self.game_init()
        self.state = 'running'

    def stop(self):
        self.state = 'stopping'

    def terminate(self):
        self.state = 'exiting'

    def on_exit(self):
        self.unregister_to_marketplace()

    def create_offer(self, product):
        return {
            "product_id": product['product_id'],
            "merchant_id": self.merchantID,
            "uid": product['uid'],
            "quality": product['quality'],
            "amount": product['amount'],
            "price": product['price'] + settings['maxPriceMargin'],
            "shipping_time": {
                "standard": settings['shipping'],
                "prime": settings['primeShipping']
            },
            "prime": True
        }

    def add_offer_to_marketplace(self, offer):
        url = urljoin(settings['marketplace_url'], 'offers')

        r = self.request_session.post(url, json=offer)
        return r.json()['offer_id']

    def register_to_marketplace(self):
        request_object = {
            "api_endpoint_url": settings['merchant_url'],
            "merchant_name": "Sample Merchant",
            "algorithm_name": "IncreasePrice"
        }
        url = urljoin(settings['marketplace_url'], 'merchants')

        r = self.request_session.post(url, json=request_object)
        print('registerToMarketplace', r.json())
        return r.json()['merchant_id']

    def unregister_to_marketplace(self):
        url = urljoin(settings['marketplace_url'], 'merchants/{:d}'.format(self.merchantID))
        print('unRegisterToMarketplace')

        self.request_session.delete(url)

    def get_initial_products(self):
        url = urljoin(settings['producerEndpoint'], 'buy?merchant_id={:d}'.format(self.merchantID))
        products = {}

        for i in range(settings['initialProducts']):
            r = self.request_session.get(url)
            product = r.json()
            if product['product_id'] in products:
                products[product['product_id']]['amount'] += 1
            else:
                products[product['product_id']] = product
        return list(products.values())

    def update_offer(self, new_offer):
        print('update offer:', new_offer)
        url = urljoin(settings['marketplace_url'], 'offers/{:d}'.format(new_offer['id']))

        try:
            self.request_session.put(url, json=new_offer)
        except Exception as e:
            print('failed to update offer', e)

    def adjust_prices(self, offer, min_price):
        if min_price < settings['minPriceMargin']:
            offer['price'] = settings['maxPriceMargin']
        else:
            offer['price'] = min(max(min_price - settings['priceDecrease'], settings['minPriceMargin']),
                                 settings['maxPriceMargin'])
        self.update_offer(offer)

    def get_offers(self, ):
        url = urljoin(settings['marketplace_url'], 'offers')

        r = self.request_session.get(url)
        offers = r.json()
        return offers

    def execute_logic(self):
        # execute queued methods
        tmp_queue = [e for e in self.execQueue]
        self.execQueue = []
        for method, kwargs in tmp_queue:
            method(*kwargs)

        offers = self.get_offers()
        for product in self.products:
            competitor_offers = []
            for offer in offers:
                if offer['merchant_id'] != self.merchantID and offer['product_id'] == product['product_id']:
                    competitor_offers.append(offer['price'])
            if len(competitor_offers) > 0:
                offer = get_from_list_by_key(self.offers, 'product_id', product['product_id'])
                self.adjust_prices(offer, min(competitor_offers))

    def sold_product(self, offer_id, amount, price):
        print('soldProduct')
        offer = [offer for offer in self.offers if offer['id'] == offer_id]
        if offer:
            offer = offer[0]
            print('found offer:', offer)
            offer['amount'] -= amount
            product = [product for product in self.products if product['product_id'] == offer['product_id']][0]
            print('found product:', product)

            product['amount'] -= amount
            if product['amount'] <= 0:
                print('product {:d} is out of stock!'.format(product['product_id']))

            # sample logic: TODO: improve
            self.buy_product_and_update_offer()

    def buy_product_and_update_offer(self):
        print('buy Product and update')
        new_product = self.buy_random_product()

        old_product = get_from_list_by_key(self.products, 'product_id', new_product['product_id'])
        if old_product:
            old_product['amount'] += 1
            offer = get_from_list_by_key(self.offers, 'product_id', new_product['product_id'])
            print('in this offer:', offer)
            url = urljoin(settings['marketplace_url'], 'offers/{:d}/restock'.format(offer['id']))
            offer['amount'] = old_product['amount']
            self.request_session.patch(url, json={'amount': 1})
        else:
            self.products.append(new_product)
            new_offer = self.create_offer(new_product)
            new_offer['id'] = self.add_offer_to_marketplace(new_offer)
            self.offers.append(new_offer)

    # returns product
    def buy_random_product(self):
        url = urljoin(settings['producerEndpoint'], 'buy?merchant_id={:d}'.format(self.merchantID))
        r = self.request_session.get(url)
        product = r.json()
        print('bought new product', product)
        return product

app = Flask(__name__)
CORS(app)
merchantLogic = None

def json_response(obj):
    js = json.dumps(obj)
    resp = Response(js, status=200, mimetype='application/json')
    return resp

@app.route('/settings', methods=['GET'])
def get_settings():
    return json_response(settings)

@app.route('/settings', methods=['PUT', 'POST'])
def put_settings():
    global settings
    new_settings = request.json
    settings.update(new_settings)
    return json_response(settings)

@app.route('/settings/execution', methods=['POST'])
def set_state():
    global settings
    global merchantLogic

    next_state = request.json['nextState']
    print(next_state)
    if next_state == 'init':
        # set endpoint url settings on init
        for keyword in ['merchant_url', 'marketplace_url']:
            if keyword in request.json:
                settings.update({keyword: request.json[keyword]})
        print('updated settings', settings)
        if merchantLogic:
            merchantLogic.terminate()
        print('new merchant')
        merchantLogic = MerchantLogic()
    elif next_state == 'start':
        print('merchant start')
        merchantLogic.start()
    elif next_state == 'stop':
        print('merchant stop')
        merchantLogic.stop()
    elif next_state == 'kill':
        print('merchant kill')
        merchantLogic.terminate()
        merchantLogic = None

    return json_response({})

@app.route('/sold', methods=['POST'])
def item_sold():
    global merchantLogic

    if merchantLogic:
        sent_json = request.get_json(force=True)
        # ignore 'consumer_id' and 'prime'
        # offer_id = int(sentJSON['offer_id'])
        # amount = int(sentJSON['amount'])
        # price = float(sentJSON['price'])

        offer_id = sent_json['offer_id']
        amount = sent_json['amount']
        price = sent_json['price']

        # consumer_id = sentJSON['consumer_id'] if 'consumer_id' in sentJSON else ''
        # prime = sentJSON['prime'] if 'prime' in sentJSON else ''

        print('sold {:d} items of the offer {:d} for {:f}'.format(amount, offer_id, price))
        merchantLogic.execQueue.append((merchantLogic.sold_product, (offer_id, amount, price)))
    else:
        print('merchantlogic not started')

    return json_response({})

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='PriceWars Merchant')
    parser.add_argument('--port', type=int,
                        help='port to bind flask App to')
    args = parser.parse_args()

    app.run(host='0.0.0.0', port=args.port)
<|endoftext|>


<commit_before>from django.db.models import Q, Prefetch
from dynamic_rest.datastructures import TreeMap
from dynamic_rest.fields import DynamicRelationField, field_is_remote

from rest_framework import serializers
from rest_framework.filters import BaseFilterBackend

class DynamicFilterBackend(BaseFilterBackend):
    VALID_FILTER_OPERATORS = (
        'in',
        'any',
        'all',
        'like',
        'range',
        'gt',
        'lt',
        'gte',
        'lte',
        'isnull',
        None,
    )

    def filter_queryset(self, request, queryset, view):
        """
        Filter queryset. This is the main/single entry-point to this class, and
        is called by DRF's list handler.
        """
        self.request = request
        self.view = view
        return self._filter_queryset(root_queryset=queryset)

    def _extract_filters(self, **kwargs):
        """
        Convert 'filters' query params into a dict that can be passed
        to Q. Returns a dict with two fields, 'include' and 'exclude',
        which can be used like:

          result = self._extract_filters()
          q = Q(**result['include'] & ~Q(**result['exclude'])

        """

        filters_map = kwargs.get('filters_map') or \
            self.view.get_request_feature(self.view.FILTER)

        out = TreeMap()

        for spec, value in filters_map.iteritems():

            # Inclusion or exclusion?
            if spec[0] == '-':
                spec = spec[1:]
                inex = '_exclude'
            else:
                inex = '_include'

            # for relational filters, separate out relation path part
            if '|' in spec:
                rel, spec = spec.split('|')
                rel = rel.split('.')
            else:
                rel = None

            parts = spec.split('.')

            # if dot-delimited, assume last part is the operator, otherwise
            # assume whole thing is a field name (with 'eq' implied).
            field = '__'.join(parts[:-1]) if len(parts) > 1 else parts[0]

            # Assume last part of a dot-delimited field spec is an operator.
            # Note, however, that 'foo.bar' is a valid field spec with an 'eq'
            # implied as operator. This will be resolved below.
            operator = (parts[-1] if len(parts) > 1
                        and parts[-1] != 'eq' else None)

            # All operators except 'range' and 'in' should have one value
            if operator == 'range':
                value = value[:2]
            elif operator == 'in':
                # no-op: i.e. accept `value` as an arbitrarily long list
                pass
            elif operator in self.VALID_FILTER_OPERATORS:
                value = value[0]
            else:
                # Unknown operator, we'll treat it like a field
                # e.g: filter{foo.bar}=baz
                field += '__' + operator
                operator = None
                value = value[0]

            param = field
            if operator:
                param += '__' + operator

            path = rel if rel else []
            path.extend([inex, param])
            out.insert(path, value)

        return out

    def _filters_to_query(self, includes, excludes, rewrites=None, q=None):
        """
        Construct Django Query object from request.
        Arguments are dictionaries, which will be passed to Q() as kwargs.

        e.g.
            includes = { 'foo' : 'bar', 'baz__in' : [1, 2] }
          produces:
            Q(foo='bar', baz__in=[1, 2])

        Arguments:
          includes: Dictionary of inclusion filters.
          excludes: Dictionary of inclusion filters.
          rewrites: Dictionary of field rewrites. (e.g. when field and source
              are different)

        Returns:
          Q() instance or None if no inclusion or exclusion filters
          were specified.
        """

        def rewrite_filters(filters, rewrites):
            if not rewrites:
                return filters
            out = {}
            for k, v in filters.iteritems():
                if k in rewrites:
                    out[rewrites[k].replace('.', '__')] = v
                else:
                    out[k] = v
            return out

        q = q or Q()

        if not includes and not excludes:
            return None

        if includes:
            includes = rewrite_filters(includes, rewrites)
            q &= Q(**includes)
        if excludes:
            excludes = rewrite_filters(excludes, rewrites)
            for k, v in excludes.iteritems():
                q &= ~Q(**{k: v})
        return q

    def _filter_queryset(
            self, serializer=None, filters=None, root_queryset=None):
        """
        Recursive queryset builder.
        Handles nested prefetching of related data and deferring fields
        at the queryset level.

        Arguments:
          serializer: An optional serializer to use a base for the queryset.
            If no serializer is passed, the `get_serializer` method will
            be used to initialize the base serializer for the viewset.
          filters: Optional nested filter map (TreeMap)
          queryset: Optional queryset. Only applies to top-level.
        """

        if serializer:
            queryset = serializer.Meta.model.objects
        else:
            queryset = root_queryset
            serializer = self.view.get_serializer()

        prefetch_related = []
        only = set()
        use_only = getattr(serializer.Meta, 'use_only', True)
        model = getattr(serializer.Meta, 'model', None)
        if not model:
            return queryset

        if filters is None:
            filters = self._extract_filters()

        field_rewrites = {}

        for name, field in serializer.fields.iteritems():
            if isinstance(field, DynamicRelationField):
                field = field.serializer
            if isinstance(field, serializers.ListSerializer):
                field = field.child

            source = field.source or name
            source0 = source.split('.')[0]
            remote = False

            if isinstance(field, serializers.ModelSerializer):
                remote = field_is_remote(model, source0)
                id_only = getattr(field, 'id_only', lambda: False)()
                if not id_only or remote:
                    prefetch_qs = self._filter_queryset(
                        serializer=field, filters=filters.get(name, {}))
                    prefetch_related.append(
                        Prefetch(
                            source,
                            queryset=prefetch_qs))

            if name != source:
                field_rewrites[name] = source

            if use_only:
                if source == '*':
                    use_only = False
                elif not remote and not getattr(field, 'is_computed', False):
                    # TODO: optimize for nested sources
                    only.add(source0)

        if getattr(serializer, 'id_only', lambda: False)():
            only = serializer.get_id_fields()
            use_only = True

        if use_only:
            queryset = queryset.only(*only)

        q = self._filters_to_query(
            includes=filters.get('_include'), excludes=filters.get('_exclude'),
            rewrites=field_rewrites)
        if q:
            queryset = queryset.filter(q)
        return queryset.prefetch_related(*prefetch_related)
<commit_msg>always include id fields<commit_after>from django.db.models import Q, Prefetch
from dynamic_rest.datastructures import TreeMap
from dynamic_rest.fields import DynamicRelationField, field_is_remote

from rest_framework import serializers
from rest_framework.filters import BaseFilterBackend

class DynamicFilterBackend(BaseFilterBackend):
    VALID_FILTER_OPERATORS = (
        'in',
        'any',
        'all',
        'like',
        'range',
        'gt',
        'lt',
        'gte',
        'lte',
        'isnull',
        None,
    )

    def filter_queryset(self, request, queryset, view):
        """
        Filter queryset. This is the main/single entry-point to this class, and
        is called by DRF's list handler.
        """
        self.request = request
        self.view = view
        return self._filter_queryset(root_queryset=queryset)

    def _extract_filters(self, **kwargs):
        """
        Convert 'filters' query params into a dict that can be passed
        to Q. Returns a dict with two fields, 'include' and 'exclude',
        which can be used like:

          result = self._extract_filters()
          q = Q(**result['include'] & ~Q(**result['exclude'])

        """

        filters_map = kwargs.get('filters_map') or \
            self.view.get_request_feature(self.view.FILTER)

        out = TreeMap()

        for spec, value in filters_map.iteritems():

            # Inclusion or exclusion?
            if spec[0] == '-':
                spec = spec[1:]
                inex = '_exclude'
            else:
                inex = '_include'

            # for relational filters, separate out relation path part
            if '|' in spec:
                rel, spec = spec.split('|')
                rel = rel.split('.')
            else:
                rel = None

            parts = spec.split('.')

            # if dot-delimited, assume last part is the operator, otherwise
            # assume whole thing is a field name (with 'eq' implied).
            field = '__'.join(parts[:-1]) if len(parts) > 1 else parts[0]

            # Assume last part of a dot-delimited field spec is an operator.
            # Note, however, that 'foo.bar' is a valid field spec with an 'eq'
            # implied as operator. This will be resolved below.
            operator = (parts[-1] if len(parts) > 1
                        and parts[-1] != 'eq' else None)

            # All operators except 'range' and 'in' should have one value
            if operator == 'range':
                value = value[:2]
            elif operator == 'in':
                # no-op: i.e. accept `value` as an arbitrarily long list
                pass
            elif operator in self.VALID_FILTER_OPERATORS:
                value = value[0]
            else:
                # Unknown operator, we'll treat it like a field
                # e.g: filter{foo.bar}=baz
                field += '__' + operator
                operator = None
                value = value[0]

            param = field
            if operator:
                param += '__' + operator

            path = rel if rel else []
            path.extend([inex, param])
            out.insert(path, value)

        return out

    def _filters_to_query(self, includes, excludes, rewrites=None, q=None):
        """
        Construct Django Query object from request.
        Arguments are dictionaries, which will be passed to Q() as kwargs.

        e.g.
            includes = { 'foo' : 'bar', 'baz__in' : [1, 2] }
          produces:
            Q(foo='bar', baz__in=[1, 2])

        Arguments:
          includes: Dictionary of inclusion filters.
          excludes: Dictionary of inclusion filters.
          rewrites: Dictionary of field rewrites. (e.g. when field and source
              are different)

        Returns:
          Q() instance or None if no inclusion or exclusion filters
          were specified.
        """

        def rewrite_filters(filters, rewrites):
            if not rewrites:
                return filters
            out = {}
            for k, v in filters.iteritems():
                if k in rewrites:
                    out[rewrites[k].replace('.', '__')] = v
                else:
                    out[k] = v
            return out

        q = q or Q()

        if not includes and not excludes:
            return None

        if includes:
            includes = rewrite_filters(includes, rewrites)
            q &= Q(**includes)
        if excludes:
            excludes = rewrite_filters(excludes, rewrites)
            for k, v in excludes.iteritems():
                q &= ~Q(**{k: v})
        return q

    def _filter_queryset(
            self, serializer=None, filters=None, root_queryset=None):
        """
        Recursive queryset builder.
        Handles nested prefetching of related data and deferring fields
        at the queryset level.

        Arguments:
          serializer: An optional serializer to use a base for the queryset.
            If no serializer is passed, the `get_serializer` method will
            be used to initialize the base serializer for the viewset.
          filters: Optional nested filter map (TreeMap)
          queryset: Optional queryset. Only applies to top-level.
        """

        if serializer:
            queryset = serializer.Meta.model.objects
        else:
            queryset = root_queryset
            serializer = self.view.get_serializer()

        prefetch_related = []
        only = set()
        use_only = True
        model = getattr(serializer.Meta, 'model', None)
        if not model:
            return queryset

        if filters is None:
            filters = self._extract_filters()

        field_rewrites = {}

        for name, field in serializer.fields.iteritems():
            if isinstance(field, DynamicRelationField):
                field = field.serializer
            if isinstance(field, serializers.ListSerializer):
                field = field.child

            source = field.source or name
            source0 = source.split('.')[0]
            remote = False

            if isinstance(field, serializers.ModelSerializer):
                remote = field_is_remote(model, source0)
                id_only = getattr(field, 'id_only', lambda: False)()
                if not id_only or remote:
                    prefetch_qs = self._filter_queryset(
                        serializer=field, filters=filters.get(name, {}))
                    prefetch_related.append(
                        Prefetch(
                            source,
                            queryset=prefetch_qs))

            if name != source:
                field_rewrites[name] = source

            if use_only:
                if source == '*':
                    use_only = False
                elif not remote and not getattr(field, 'is_computed', False):
                    # TODO: optimize for nested sources
                    only.add(source0)

        if getattr(serializer, 'id_only', lambda: False)():
            use_only = True

        if use_only:
            only = set(serializer.get_id_fields() + list(only))
            queryset = queryset.only(*only)

        q = self._filters_to_query(
            includes=filters.get('_include'), excludes=filters.get('_exclude'),
            rewrites=field_rewrites)
        if q:
            queryset = queryset.filter(q)
        return queryset.prefetch_related(*prefetch_related)
<|endoftext|>