<commit_before># -*- coding:utf-8 -*-
import doctest

class DBConfigLoader(object):
	_config_instance=None
	_all_connections=('mysql','mongodb')
	def __new__(cls,*args,**kw):
		if cls._config_instance==None:
			cls._config_instance=object.__new__(cls,*args,**kw)
		return cls._config_instance
	def __init__(self):
		self._config=__import__('conf','app.conf').database
		self._default_connection=self._config['default']
		self._specific_connection=None
	def _get_specific_connection_config_item(self,item_name,connection):
		if connection.lower() not in self._all_connections:
			raise AttributeError("database config has not connection %s"%connection.lower())
		if item_name in self._config['connections'][connection]:
			return self._config['connections'][connection][item_name]
		else:
			raise AttributeError("database connection has not such item '%s'"%item_name)
	def _connection(self,conn_name):
		self._specific_connection=conn_name.lower()
		return self

	def __getattr__(self,key):
		if key.upper()=='CONNECTION':
			return self._connection
		if not self._specific_connection:
			return self._get_specific_connection_config_item(key,self._default_connection)
		else:
			return self._get_specific_connection_config_item(key,self._specific_connection)
class SessionConfigLoader(object):
	_all_drivers=('redis','file','mongo')
	def __new__(cls,*args,**kw):
		if not hasattr(cls,'_config_instance'):
			cls._config_instance=object.__new__(cls,*args,**kw)
		return cls._config_instance
	def __init__(self):
		self._config=__import__("conf",'app.confg').session
		self._default_driver=self._config.get('default')
		self._specific_driver=None
	def _get_specific_driver_config_item(self,item_name,driver_name):
		if driver_name.lower() not in self._all_drivers:
			raise AttributeError("session config has no driver '%s'"%(driver_name))
		if item_name in self._config['drivers'][driver_name]:
			return self._config['drivers'][driver_name].get(item_name)
		else:
			raise AttributeError("session driver %s has no such item '%s'"%(driver_name,item_name))
	def _driver(self,driver_name):
		self._specific_driver=driver_name.lower()
		return self
	def __getattr__(self,key):
		if key.upper()=='DRIVER':
			return self._driver
		if  not self._specific_driver:
			return self._get_specific_driver_config_item(key,self._default_driver)
		else:
			return self._get_specific_driver_config_item(key,self._specific_driver)
class AuthConfigLoader(object):
	def __new__(cls,*args,**kw):
		if not hasattr(cls,'_config_instance'):
			cls._config_instance=object.__new__(cls,*args,**kw)
		return cls._config_instance
	def __init__(self):
		self._config=__import__("conf",'app.config').authentication
	def __getattr__(self,key):
		if key not in self._config:
			raise AttributeError("authentication config has no such item '%s'"%(key))
		return self._config[key]
class classproperty(object):
	def __init__(self,func):
		self._func=func
	def __get__(self,instance,owner_class):
		return self._func(owner_class)

class Config(dict):
	r'''
	>>> Config.session.session_dir
	'/tmp/session/'
	>>> Config.session.expire_file
	'session_expire'
	>>> Config.session.driver('redis').host
	'localhost'
	>>> Config.session.driver('redis').port
	6379
	>>> Config.session.driver('mongo').port
	27017
	>>> Config.session.driver('mongo').host
	'localhost'
	>>> Config.database.port
	3306
	>>> Config.database.connection('mongodb').port
	27017
	'''
	_config_loader={'database':DBConfigLoader(),'session':SessionConfigLoader(),'authentication':AuthConfigLoader()}
	def __init__(self):
		print("__init__ start")
	def __call__(self,*args,**kw):
		pass
	@classproperty
	def database(cls):
		return cls._config_loader.get('database')
	@classproperty
	def session(cls):
		return cls._config_loader.get('session')
	@classproperty
	def authentication(cls):
		return cls._config_loader.get('authentication')
	def __getattr__(cls,key):
		return 'not attribute found'
if __name__=='__main__':
	doctest.testmod()
	r'''
	print(Config.session.session_dir)
	print(Config.session.expire_file)
	print(Config.session.expire)
	print(Config.session.driver('redis').host)
	print(Config.session.driver('redis').port)
	print(Config.session.driver('redis').db)
	print(Config.session.driver('redis').expire)
	print(Config.session.driver('mongo').host)
	print(Config.session.driver('mongo').port)
	print(Config.session.driver('mongo').db)
	print(Config.session.driver('mongo').expire)
	print(Config.database.port)
	print(Config.database.password)
	print(Config.database.connection('mongodb').port)
	print(Config.authentication.auth_table)
	print(Config.authentication.auth_id)
	print(Config.authentication.login_url)
	'''
<commit_msg>add supporing for getting connection name and getting connection all config item<commit_after># -*- coding:utf-8 -*-
import doctest

class DBConfigLoader(object):
	_config_instance=None
	_all_connections=('mysql','mongodb')
	def __new__(cls,*args,**kw):
		if cls._config_instance==None:
			cls._config_instance=object.__new__(cls,*args,**kw)
		return cls._config_instance
	def __init__(self):
		self._config=__import__('conf','app.conf').database
		self._default_connection=self._config['default']
		self._specific_connection=None
	def _get_specific_connection_config_item(self,item_name,connection):
		if connection.lower() not in self._all_connections:
			raise AttributeError("database config has no connection %s"%connection.lower())
		if item_name in self._config['connections'][connection]:
			return self._config['connections'][connection][item_name]
		else:
			raise AttributeError("database connection has no such item '%s'"%item_name)
	def _get_specific_connection_all_config_item(self,connection):
		if connection.lower() not in self._all_connections:
			raise AttributeError("database config has no connection %"%connection.lower())
		return self._config['connections'][connection]
	def _connection(self,conn_name):
		self._specific_connection=conn_name.lower()
		return self
	def __getattr__(self,key):
		if key.upper()=='CONNECTION':
			return self._connection
		if not self._specific_connection:
			if key.lower()=='connection_name':
				return self._default_connection
			if key.lower()=='all':
				return self._get_specific_connection_all_config_item(self._default_connection)
			return self._get_specific_connection_config_item(key,self._default_connection)
		else:
			if key.lower()=='connection_name':
				coon_name=self._specific_connection
				self._specific_connection=None
				return conn_name
			if key.lower()=='all':
				allitem= self._get_specific_connection_all_config_item(self._specific_connection)
				self._specific_connection=None
				return allitem
			item=self._get_specific_connection_config_item(key,self._specific_connection)
			self._specific_connection=None
			return item
class SessionConfigLoader(object):
	_all_drivers=('redis','file','mongo')
	def __new__(cls,*args,**kw):
		if not hasattr(cls,'_config_instance'):
			cls._config_instance=object.__new__(cls,*args,**kw)
		return cls._config_instance
	def __init__(self):
		self._config=__import__("conf",'app.confg').session
		self._default_driver=self._config.get('default')
		self._specific_driver=None
	def _get_specific_driver_config_item(self,item_name,driver_name):
		if driver_name.lower() not in self._all_drivers:
			raise AttributeError("session config has no driver '%s'"%(driver_name))
		if item_name in self._config['drivers'][driver_name]:
			return self._config['drivers'][driver_name].get(item_name)
		else:
			raise AttributeError("session driver %s has no such item '%s'"%(driver_name,item_name))
	def _driver(self,driver_name):
		self._specific_driver=driver_name.lower()
		return self
	def __getattr__(self,key):
		if key.upper()=='DRIVER':
			return self._driver
		if  not self._specific_driver:
			return self._get_specific_driver_config_item(key,self._default_driver)
		else:
			item=self._get_specific_driver_config_item(key,self._specific_driver)
			self._specific_driver=None
			return item
class AuthConfigLoader(object):
	def __new__(cls,*args,**kw):
		if not hasattr(cls,'_config_instance'):
			cls._config_instance=object.__new__(cls,*args,**kw)
		return cls._config_instance
	def __init__(self):
		self._config=__import__("conf",'app.config').authentication
	def __getattr__(self,key):
		if key not in self._config:
			raise AttributeError("authentication config has no such item '%s'"%(key))
		return self._config[key]
class classproperty(object):
	def __init__(self,func):
		self._func=func
	def __get__(self,instance,owner_class):
		return self._func(owner_class)

class Config(dict):
	r'''
	>>> Config.session.session_dir
	'/tmp/session/'
	>>> Config.session.expire_file
	'session_expire'
	>>> Config.session.driver('redis').host
	'localhost'
	>>> Config.session.driver('redis').port
	6379
	>>> Config.session.driver('mongo').port
	27017
	>>> Config.session.driver('mongo').host
	'localhost'
	>>> Config.database.port
	3306
	>>> Config.database.connection('mongodb').port
	27017
	'''
	_config_loader={'database':DBConfigLoader(),'session':SessionConfigLoader(),'authentication':AuthConfigLoader()}
	def __init__(self):
		print("__init__ start")
	def __call__(self,*args,**kw):
		pass
	@classproperty
	def database(cls):
		return cls._config_loader.get('database')
	@classproperty
	def session(cls):
		return cls._config_loader.get('session')
	@classproperty
	def authentication(cls):
		return cls._config_loader.get('authentication')
	def __getattr__(cls,key):
		return 'not attribute found'
if __name__=='__main__':
	doctest.testmod()
	r'''
	print(Config.session.session_dir)
	print(Config.session.expire_file)
	print(Config.session.expire)
	print(Config.session.driver('redis').host)
	print(Config.session.driver('redis').port)
	print(Config.session.driver('redis').db)
	print(Config.session.driver('redis').expire)
	print(Config.session.driver('mongo').host)
	print(Config.session.driver('mongo').port)
	print(Config.session.driver('mongo').db)
	print(Config.session.driver('mongo').expire)
	print(Config.database.port)
	print(Config.database.password)
	print(Config.database.connection('mongodb').port)
	print(Config.authentication.auth_table)
	print(Config.authentication.auth_id)
	print(Config.authentication.login_url)
	'''
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    Copyright 2013 Camptocamp
#    Copyright 2009-2013 Akretion,
#    Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau,
#            Benoît Guillot, Joel Grand-Guillaume
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, orm
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp import netsvc
from openerp.tools.translate import _
import time

class claim_make_picking(orm.TransientModel):

    _name = 'claim_make_picking.wizard'
    _description = 'Wizard to create pickings from claim lines'
    _columns = {
        'claim_line_source_location': fields.many2one(
            'stock.location',
            string='Source Location',
            help="Location where the returned products are from.",
            required=True),
        'claim_line_dest_location': fields.many2one(
            'stock.location',
            string='Dest. Location',
            help="Location where the system will stock the returned products.",
            required=True),
        'claim_line_ids': fields.many2many(
            'claim.line',
            'claim_line_picking',
            'claim_picking_id',
            'claim_line_id',
            string='Claim lines'),
    }

    def _get_claim_lines(self, cr, uid, context):
        #TODO use custom states to show buttons of this wizard or not instead
        # of raise an error
        if context is None:
            context = {}
        line_obj = self.pool.get('claim.line')
        if context.get('picking_type') == 'out':
            move_field = 'move_out_id'
        else:
            move_field = 'move_in_id'
        good_lines = []
        line_ids = line_obj.search(
            cr, uid,
            [('claim_id', '=', context['active_id'])],
            context=context)
        for line in line_obj.browse(cr, uid, line_ids, context=context):
            if not line[move_field] or line[move_field].state == 'cancel':
                good_lines.append(line.id)
        if not good_lines:
            raise orm.except_orm(
                _('Error'),
                _('A picking has already been created for this claim.'))
        return good_lines

    # Get default source location
    def _get_source_loc(self, cr, uid, context):
        loc_id = False
        if context is None:
            context = {}
        warehouse_obj = self.pool.get('stock.warehouse')
        warehouse_id = context.get('warehouse_id')
        if context.get('picking_type') == 'out':
            loc_id = warehouse_obj.read(
                cr, uid, warehouse_id,
                ['lot_stock_id'],
                context=context)['lot_stock_id'][0]
        elif context.get('partner_id'):
            loc_id = self.pool.get('res.partner').read(
                cr, uid, context['partner_id'],
                ['property_stock_customer'],
                context=context)['property_stock_customer']
        return loc_id

    def _get_common_dest_location_from_line(self, cr, uid, line_ids, context):
        """Return the ID of the common location between all lines. If no common
        destination was  found, return False"""
        loc_id = False
        line_obj = self.pool.get('claim.line')
        line_location = []
        for line in line_obj.browse(cr, uid, line_ids, context=context):
            if line.location_dest_id.id not in line_location:
                line_location.append(line.location_dest_id.id)
        if len(line_location) == 1:
            loc_id = line_location[0]
        return loc_id

    def _get_common_partner_from_line(self, cr, uid, line_ids, context):
        """Return the ID of the common partner between all lines. If no common
        partner was found, return False"""
        partner_id = False
        line_obj = self.pool.get('claim.line')
        line_partner = []
        for line in line_obj.browse(cr, uid, line_ids, context=context):
            if (line.warranty_return_partner
                    and line.warranty_return_partner.id
                    not in line_partner):
                line_partner.append(line.warranty_return_partner.id)
        if len(line_partner) == 1:
            partner_id = line_partner[0]
        return partner_id

    # Get default destination location
    def _get_dest_loc(self, cr, uid, context):
        """Return the location_id to use as destination.
        If it's an outoing shippment: take the customer stock property
        If it's an incomming shippment take the location_dest_id common to all
        lines, or if different, return None."""
        if context is None:
            context = {}
        loc_id = False
        if context.get('picking_type') == 'out' and context.get('partner_id'):
            loc_id = self.pool.get('res.partner').read(
                cr, uid, context.get('partner_id'),
                ['property_stock_customer'],
                context=context)['property_stock_customer'][0]
        elif context.get('picking_type') == 'in' and context.get('partner_id'):
            # Add the case of return to supplier !
            line_ids = self._get_claim_lines(cr, uid, context=context)
            loc_id = self._get_common_dest_location_from_line(cr, uid,
                                                              line_ids,
                                                              context=context)
        return loc_id

    _defaults = {
        'claim_line_source_location': _get_source_loc,
        'claim_line_dest_location': _get_dest_loc,
        'claim_line_ids': _get_claim_lines,
    }

    def action_cancel(self, cr, uid, ids, context=None):
        return {'type': 'ir.actions.act_window_close'}

    # If "Create" button pressed
    def action_create_picking(self, cr, uid, ids, context=None):
        picking_obj = self.pool.get('stock.picking')
        if context is None:
            context = {}
        view_obj = self.pool.get('ir.ui.view')
        name = 'RMA picking out'
        if context.get('picking_type') == 'out':
            p_type = 'out'
            write_field = 'move_out_id'
            note = 'RMA picking out'
            view_xml_id = 'stock_picking_form'
            view_name = 'stock.picking.form'
        else:
            p_type = 'in'
            view_xml_id = 'stock_picking_form'
            view_name = 'stock.picking.form'
            write_field = 'move_in_id'
            if context.get('picking_type'):
                note = 'RMA picking ' + str(context.get('picking_type'))
                name = note
        view_id = view_obj.search(cr, uid,
                                  [('xml_id', '=', view_xml_id),
                                   ('model', '=', 'stock.picking'),
                                   ('type', '=', 'form'),
                                   ('name', '=', view_name)
                                   ],
                                  context=context)[0]
        wizard = self.browse(cr, uid, ids[0], context=context)
        claim = self.pool.get('crm.claim').browse(cr, uid,
                                                  context['active_id'],
                                                  context=context)
        partner_id = claim.delivery_address_id.id
        line_ids = [x.id for x in wizard.claim_line_ids]
        # In case of product return, we don't allow one picking for various
        # product if location are different
        # or if partner address is different
        if context.get('product_return'):
            common_dest_loc_id = self._get_common_dest_location_from_line(
                cr, uid, line_ids, context=context)
            if not common_dest_loc_id:
                raise orm.except_orm(
                    _('Error !'),
                    _('A product return cannot be created for various '
                      'destination locations, please choose line with a '
                      'same destination location.'))
            common_dest_partner_id = self._get_common_partner_from_line(
                cr, uid, line_ids, context=context)
            if not common_dest_partner_id:
                raise orm.except_orm(
                    _('Error !'),
                    _('A product return cannot be created for various '
                      'destination addresses, please choose line with a '
                      'same address.'))
            partner_id = common_dest_partner_id
        # create picking
        picking_id = picking_obj.create(
            cr, uid,
            {'origin': claim.number,
             'type': p_type,
             'move_type': 'one',  # direct
             'state': 'draft',
             'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
             'partner_id': partner_id,
             'invoice_state': "none",
             'company_id': claim.company_id.id,
             'location_id': wizard.claim_line_source_location.id,
             'location_dest_id': wizard.claim_line_dest_location.id,
             'note': note,
             'claim_id': claim.id,
             },
            context=context)
        # Create picking lines
        for wizard_claim_line in wizard.claim_line_ids:
            move_obj = self.pool.get('stock.move')
            move_id = move_obj.create(
                cr, uid,
                {'name': wizard_claim_line.product_id.name_template,
                 'priority': '0',
                 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                 'date_expected': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                 'product_id': wizard_claim_line.product_id.id,
                 'product_qty': wizard_claim_line.product_returned_quantity,
                 'product_uom': wizard_claim_line.product_id.uom_id.id,
                 'partner_id': partner_id,
                 'prodlot_id': wizard_claim_line.prodlot_id.id,
                 'picking_id': picking_id,
                 'state': 'draft',
                 'price_unit': wizard_claim_line.unit_sale_price,
                 'company_id': claim.company_id.id,
                 'location_id': wizard.claim_line_source_location.id,
                 'location_dest_id': wizard.claim_line_dest_location.id,
                 'note': note,
                 },
                context=context)
            self.pool.get('claim.line').write(
                cr, uid, wizard_claim_line.id,
                {write_field: move_id}, context=context)
        wf_service = netsvc.LocalService("workflow")
        if picking_id:
            wf_service.trg_validate(uid, 'stock.picking',
                                    picking_id, 'button_confirm', cr)
            picking_obj.action_assign(cr, uid, [picking_id])
        domain = ("[('type', '=', '%s'), ('partner_id', '=', %s)]" %
                  (p_type, partner_id))
        return {
            'name': '%s' % name,
            'view_type': 'form',
            'view_mode': 'form',
            'view_id': view_id,
            'domain': domain,
            'res_model': 'stock.picking',
            'res_id': picking_id,
            'type': 'ir.actions.act_window',
        }

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>[FIX] open right view for picking in and picking out instead of internal picking view<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    Copyright 2013 Camptocamp
#    Copyright 2009-2013 Akretion,
#    Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau,
#            Benoît Guillot, Joel Grand-Guillaume
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, orm
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp import netsvc
from openerp.tools.translate import _
import time

class claim_make_picking(orm.TransientModel):

    _name = 'claim_make_picking.wizard'
    _description = 'Wizard to create pickings from claim lines'
    _columns = {
        'claim_line_source_location': fields.many2one(
            'stock.location',
            string='Source Location',
            help="Location where the returned products are from.",
            required=True),
        'claim_line_dest_location': fields.many2one(
            'stock.location',
            string='Dest. Location',
            help="Location where the system will stock the returned products.",
            required=True),
        'claim_line_ids': fields.many2many(
            'claim.line',
            'claim_line_picking',
            'claim_picking_id',
            'claim_line_id',
            string='Claim lines'),
    }

    def _get_claim_lines(self, cr, uid, context):
        #TODO use custom states to show buttons of this wizard or not instead
        # of raise an error
        if context is None:
            context = {}
        line_obj = self.pool.get('claim.line')
        if context.get('picking_type') == 'out':
            move_field = 'move_out_id'
        else:
            move_field = 'move_in_id'
        good_lines = []
        line_ids = line_obj.search(
            cr, uid,
            [('claim_id', '=', context['active_id'])],
            context=context)
        for line in line_obj.browse(cr, uid, line_ids, context=context):
            if not line[move_field] or line[move_field].state == 'cancel':
                good_lines.append(line.id)
        if not good_lines:
            raise orm.except_orm(
                _('Error'),
                _('A picking has already been created for this claim.'))
        return good_lines

    # Get default source location
    def _get_source_loc(self, cr, uid, context):
        loc_id = False
        if context is None:
            context = {}
        warehouse_obj = self.pool.get('stock.warehouse')
        warehouse_id = context.get('warehouse_id')
        if context.get('picking_type') == 'out':
            loc_id = warehouse_obj.read(
                cr, uid, warehouse_id,
                ['lot_stock_id'],
                context=context)['lot_stock_id'][0]
        elif context.get('partner_id'):
            loc_id = self.pool.get('res.partner').read(
                cr, uid, context['partner_id'],
                ['property_stock_customer'],
                context=context)['property_stock_customer']
        return loc_id

    def _get_common_dest_location_from_line(self, cr, uid, line_ids, context):
        """Return the ID of the common location between all lines. If no common
        destination was  found, return False"""
        loc_id = False
        line_obj = self.pool.get('claim.line')
        line_location = []
        for line in line_obj.browse(cr, uid, line_ids, context=context):
            if line.location_dest_id.id not in line_location:
                line_location.append(line.location_dest_id.id)
        if len(line_location) == 1:
            loc_id = line_location[0]
        return loc_id

    def _get_common_partner_from_line(self, cr, uid, line_ids, context):
        """Return the ID of the common partner between all lines. If no common
        partner was found, return False"""
        partner_id = False
        line_obj = self.pool.get('claim.line')
        line_partner = []
        for line in line_obj.browse(cr, uid, line_ids, context=context):
            if (line.warranty_return_partner
                    and line.warranty_return_partner.id
                    not in line_partner):
                line_partner.append(line.warranty_return_partner.id)
        if len(line_partner) == 1:
            partner_id = line_partner[0]
        return partner_id

    # Get default destination location
    def _get_dest_loc(self, cr, uid, context):
        """Return the location_id to use as destination.
        If it's an outoing shippment: take the customer stock property
        If it's an incomming shippment take the location_dest_id common to all
        lines, or if different, return None."""
        if context is None:
            context = {}
        loc_id = False
        if context.get('picking_type') == 'out' and context.get('partner_id'):
            loc_id = self.pool.get('res.partner').read(
                cr, uid, context.get('partner_id'),
                ['property_stock_customer'],
                context=context)['property_stock_customer'][0]
        elif context.get('picking_type') == 'in' and context.get('partner_id'):
            # Add the case of return to supplier !
            line_ids = self._get_claim_lines(cr, uid, context=context)
            loc_id = self._get_common_dest_location_from_line(cr, uid,
                                                              line_ids,
                                                              context=context)
        return loc_id

    _defaults = {
        'claim_line_source_location': _get_source_loc,
        'claim_line_dest_location': _get_dest_loc,
        'claim_line_ids': _get_claim_lines,
    }

    def action_cancel(self, cr, uid, ids, context=None):
        return {'type': 'ir.actions.act_window_close'}

    # If "Create" button pressed
    def action_create_picking(self, cr, uid, ids, context=None):
        picking_obj = self.pool.get('stock.picking')
        if context is None:
            context = {}
        view_obj = self.pool.get('ir.ui.view')
        name = 'RMA picking out'
        if context.get('picking_type') == 'out':
            p_type = 'out'
            write_field = 'move_out_id'
            note = 'RMA picking out'
            view_xml_id = 'stock_picking_form'
        else:
            p_type = 'in'
            write_field = 'move_in_id'
            if context.get('picking_type'):
                note = 'RMA picking ' + str(context.get('picking_type'))
                name = note
        model = 'stock.picking.' + p_type
        view_id = view_obj.search(cr, uid,
                                  [('model', '=', model),
                                   ('type', '=', 'form'),
                                   ],
                                  context=context)[0]
        wizard = self.browse(cr, uid, ids[0], context=context)
        claim = self.pool.get('crm.claim').browse(cr, uid,
                                                  context['active_id'],
                                                  context=context)
        partner_id = claim.delivery_address_id.id
        line_ids = [x.id for x in wizard.claim_line_ids]
        # In case of product return, we don't allow one picking for various
        # product if location are different
        # or if partner address is different
        if context.get('product_return'):
            common_dest_loc_id = self._get_common_dest_location_from_line(
                cr, uid, line_ids, context=context)
            if not common_dest_loc_id:
                raise orm.except_orm(
                    _('Error !'),
                    _('A product return cannot be created for various '
                      'destination locations, please choose line with a '
                      'same destination location.'))
            common_dest_partner_id = self._get_common_partner_from_line(
                cr, uid, line_ids, context=context)
            if not common_dest_partner_id:
                raise orm.except_orm(
                    _('Error !'),
                    _('A product return cannot be created for various '
                      'destination addresses, please choose line with a '
                      'same address.'))
            partner_id = common_dest_partner_id
        # create picking
        picking_id = picking_obj.create(
            cr, uid,
            {'origin': claim.number,
             'type': p_type,
             'move_type': 'one',  # direct
             'state': 'draft',
             'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
             'partner_id': partner_id,
             'invoice_state': "none",
             'company_id': claim.company_id.id,
             'location_id': wizard.claim_line_source_location.id,
             'location_dest_id': wizard.claim_line_dest_location.id,
             'note': note,
             'claim_id': claim.id,
             },
            context=context)
        # Create picking lines
        for wizard_claim_line in wizard.claim_line_ids:
            move_obj = self.pool.get('stock.move')
            move_id = move_obj.create(
                cr, uid,
                {'name': wizard_claim_line.product_id.name_template,
                 'priority': '0',
                 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                 'date_expected': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                 'product_id': wizard_claim_line.product_id.id,
                 'product_qty': wizard_claim_line.product_returned_quantity,
                 'product_uom': wizard_claim_line.product_id.uom_id.id,
                 'partner_id': partner_id,
                 'prodlot_id': wizard_claim_line.prodlot_id.id,
                 'picking_id': picking_id,
                 'state': 'draft',
                 'price_unit': wizard_claim_line.unit_sale_price,
                 'company_id': claim.company_id.id,
                 'location_id': wizard.claim_line_source_location.id,
                 'location_dest_id': wizard.claim_line_dest_location.id,
                 'note': note,
                 },
                context=context)
            self.pool.get('claim.line').write(
                cr, uid, wizard_claim_line.id,
                {write_field: move_id}, context=context)
        wf_service = netsvc.LocalService("workflow")
        if picking_id:
            wf_service.trg_validate(uid, 'stock.picking',
                                    picking_id, 'button_confirm', cr)
            picking_obj.action_assign(cr, uid, [picking_id])
        domain = ("[('type', '=', '%s'), ('partner_id', '=', %s)]" %
                  (p_type, partner_id))
        return {
            'name': '%s' % name,
            'view_type': 'form',
            'view_mode': 'form',
            'view_id': view_id,
            'domain': domain,
            'res_model': model,
            'res_id': picking_id,
            'type': 'ir.actions.act_window',
        }

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


<commit_before># coding=utf-8

import io
import logging
import math
import re
import rarfile

from bs4 import BeautifulSoup
from zipfile import ZipFile, is_zipfile
from rarfile import RarFile, is_rarfile
from babelfish import Language, language_converters
from requests import Session
from guessit import guessit
from subliminal_patch.providers import Provider
from subliminal_patch.subtitle import Subtitle
from subliminal_patch.utils import sanitize, fix_inconsistent_naming as _fix_inconsistent_naming
from subliminal.exceptions import ProviderError
from subliminal.score import get_equivalent_release_groups
from subliminal.utils import sanitize_release_group
from subliminal.subtitle import fix_line_ending, guess_matches
from subliminal.video import Episode, Movie

# parsing regex definitions
title_re = re.compile(r'(?P<title>(?:.+(?= [Aa][Kk][Aa] ))|.+)(?:(?:.+)(?P<altitle>(?<= [Aa][Kk][Aa] ).+))?')
lang_re = re.compile(r'(?<=flags/)(?P<lang>.{2})(?:.)(?P<script>c?)(?:.+)')
season_re = re.compile(r'Sezona (?P<season>\d+)')
episode_re = re.compile(r'Epizoda (?P<episode>\d+)')
year_re = re.compile(r'(?P<year>\d+)')
fps_re = re.compile(r'fps: (?P<fps>.+)')

def fix_inconsistent_naming(title):
    """Fix titles with inconsistent naming using dictionary and sanitize them.

    :param str title: original title.
    :return: new title.
    :rtype: str

    """
    return _fix_inconsistent_naming(title, {"DC's Legends of Tomorrow": "Legends of Tomorrow",
                                            "Marvel's Jessica Jones": "Jessica Jones"})

logger = logging.getLogger(__name__)

# Configure :mod:`rarfile` to use the same path separator as :mod:`zipfile`
rarfile.PATH_SEP = '/'

language_converters.register('titlovi = subliminal_patch.converters.titlovi:TitloviConverter')

class TitloviSubtitle(Subtitle):
    provider_name = 'titlovi'

    def __init__(self, language, page_link, download_link, sid, releases, title, altitle=None, season=None,
                 episode=None, year=None, fps=None):
        super(TitloviSubtitle, self).__init__(language, page_link=page_link)
        self.sid = sid
        self.releases = releases
        self.title = title
        self.altitle = altitle
        self.season = season
        self.episode = episode
        self.year = year
        self.download_link = download_link
        self.fps = fps

    @property
    def id(self):
        return self.sid

    def get_matches(self, video):
        matches = set()

        # handle movies and series separately
        if isinstance(video, Episode):
            # series
            if video.series and sanitize(self.title) == fix_inconsistent_naming(video.series) or sanitize(
                    self.altitle) == fix_inconsistent_naming(video.series):
                matches.add('series')
            # year
            if video.original_series and self.year is None or video.year and video.year == self.year:
                matches.add('year')
            # season
            if video.season and self.season == video.season:
                matches.add('season')
            # episode
            if video.episode and self.episode == video.episode:
                matches.add('episode')
        # movie
        elif isinstance(video, Movie):
            # title
            if video.title and sanitize(self.title) == fix_inconsistent_naming(video.title) or sanitize(
                    self.altitle) == fix_inconsistent_naming(video.title):
                matches.add('title')
            # year
            if video.year and self.year == video.year:
                matches.add('year')

        # rest is same for both groups

        # release_group
        if (video.release_group and self.releases and
                any(r in sanitize_release_group(self.releases)
                    for r in get_equivalent_release_groups(sanitize_release_group(video.release_group)))):
            matches.add('release_group')
        # resolution
        if video.resolution and self.releases and video.resolution in self.releases.lower():
            matches.add('resolution')
        # format
        if video.format and self.releases and video.format.lower() in self.releases.lower():
            matches.add('format')
        # other properties
        matches |= guess_matches(video, guessit(self.releases))

        return matches

class TitloviProvider(Provider):
    subtitle_class = TitloviSubtitle
    languages = {Language.fromtitlovi(l) for l in language_converters['titlovi'].codes} | {Language.fromietf('sr')}
    server_url = 'http://titlovi.com'
    search_url = server_url + '/titlovi/?'
    download_url = server_url + '/download/?type=1&mediaid='

    def initialize(self):
        self.session = Session()

    def terminate(self):
        self.session.close()

    def query(self, languages, title, season=None, episode=None, year=None):
        itemsperpage = 10
        currentpage = 1

        # convert list of languages into search string
        langs = '|'.join(
            map(str, [l.titlovi if l != Language.fromietf('sr') else 'cirilica' for l in languages]))
        # set query params
        params = {'prijevod': title, 'jezik': langs}
        is_episode = False
        if season and episode:
            is_episode = True
            params['s'] = season
            params['e'] = episode
        if year:
            params['g'] = year

        # loop through paginated results
        logger.info('Searching subtitles %r', params)
        subtitles = []

        while True:
            # query the server
            r = self.session.get(self.search_url, params=params, timeout=10)
            r.raise_for_status()

            soup = BeautifulSoup(r.content, 'lxml')

            # number of results
            resultcount = int(soup.select_one('.results_count b').string)

            # exit if no results
            if not resultcount:
                logger.debug('No subtitles found')
                break

            # number of pages with results
            pages = int(math.ceil(resultcount / float(itemsperpage)))

            # get current page
            if 'pg' in params:
                currentpage = params['pg']

            sublist = soup.select('section.titlovi > ul.titlovi > li')
            for sub in sublist:
                # subtitle id
                sid = sub.find(attrs={'data-id': True}).attrs['data-id']
                # get download link
                download_link = self.download_url + sid
                # title and alternate title
                match = title_re.search(sub.a.string)
                if match:
                    title = match.group('title')
                    altitle = match.group('altitle')
                # page link
                page_link = self.server_url + sub.a.attrs['href']
                # subtitle language
                match = lang_re.search(sub.select_one('.lang').attrs['src'])
                if match:
                    lang = Language.fromtitlovi(match.group('lang') + match.group('script'))
                # relase year or series start year
                match = year_re.search(sub.find(attrs={'data-id': True}).parent.i.string)
                if match:
                    year = int(match.group('year'))
                # fps
                match = fps_re.search(sub.select_one('.fps').string)
                if match:
                    fps = match.group('fps')
                # releases
                releases = sub.select_one('.fps').parent.contents[0].string

                # handle movies and series separately
                if is_episode:
                    # season and episode info
                    sxe = sub.select_one('.s0xe0y').string
                    if sxe:
                        match = season_re.search(sxe)
                        if match:
                            season = int(match.group('season'))
                        match = episode_re.search(sxe)
                        if match:
                            episode = int(match.group('episode'))
                    subtitle = self.subtitle_class(lang, page_link, download_link, sid, releases, title,
                                                   altitle=altitle, season=season, episode=episode, year=year,
                                                   fps=fps)
                else:
                    subtitle = self.subtitle_class(lang, page_link, download_link, sid, releases, title,
                                                   altitle=altitle, year=year, fps=fps)
                logger.debug('Found subtitle %r', subtitle)

                # add found subtitles
                subtitles.append(subtitle)

            soup.decompose()

            # stop on last page
            if currentpage >= pages:
                break

            # increment current page
            params['pg'] = currentpage + 1
            logger.debug('Getting page %d', params['pg'])

        return subtitles

    def list_subtitles(self, video, languages):
        season = episode = None
        if isinstance(video, Episode):
            title = video.series
            season = video.season
            episode = video.episode
        else:
            title = video.title

        return [s for s in
                self.query(languages, fix_inconsistent_naming(title), season=season, episode=episode, year=video.year)]

    def download_subtitle(self, subtitle):
        r = self.session.get(subtitle.download_link, timeout=10)
        r.raise_for_status()

        # open the archive
        archive_stream = io.BytesIO(r.content)
        if is_rarfile(archive_stream):
            logger.debug('Archive identified as rar')
            archive = RarFile(archive_stream)
        elif is_zipfile(archive_stream):
            logger.debug('Archive identified as zip')
            archive = ZipFile(archive_stream)
        else:
            raise ProviderError('Unidentified archive type')

        # extract subtitle's content
        use_name = None
        for name in archive.namelist():
            for ext in (".srt", ".sub", ".ssa", ".ass"):
                if name.endswith(ext):
                    use_name = name
                    break

            if use_name:
                break

        if not use_name:
            raise ProviderError("None of expected subtitle formats found in archive")
        subtitle.content = fix_line_ending(archive.read(use_name))
<commit_msg>#320 adapt titlovi, first attempt<commit_after># coding=utf-8

import io
import logging
import math
import re
import rarfile

from bs4 import BeautifulSoup
from zipfile import ZipFile, is_zipfile
from rarfile import RarFile, is_rarfile
from babelfish import Language, language_converters
from requests import Session
from guessit import guessit
from subliminal_patch.providers import Provider
from subliminal_patch.subtitle import Subtitle
from subliminal_patch.utils import sanitize, fix_inconsistent_naming as _fix_inconsistent_naming
from subliminal.exceptions import ProviderError
from subliminal.score import get_equivalent_release_groups
from subliminal.utils import sanitize_release_group
from subliminal.subtitle import fix_line_ending, guess_matches
from subliminal.video import Episode, Movie

# parsing regex definitions
title_re = re.compile(r'(?P<title>(?:.+(?= [Aa][Kk][Aa] ))|.+)(?:(?:.+)(?P<altitle>(?<= [Aa][Kk][Aa] ).+))?')
lang_re = re.compile(r'(?<=flags/)(?P<lang>.{2})(?:.)(?P<script>c?)(?:.+)')
season_re = re.compile(r'Sezona (?P<season>\d+)')
episode_re = re.compile(r'Epizoda (?P<episode>\d+)')
year_re = re.compile(r'(?P<year>\d+)')
fps_re = re.compile(r'fps: (?P<fps>.+)')

def fix_inconsistent_naming(title):
    """Fix titles with inconsistent naming using dictionary and sanitize them.

    :param str title: original title.
    :return: new title.
    :rtype: str

    """
    return _fix_inconsistent_naming(title, {"DC's Legends of Tomorrow": "Legends of Tomorrow",
                                            "Marvel's Jessica Jones": "Jessica Jones"})

logger = logging.getLogger(__name__)

# Configure :mod:`rarfile` to use the same path separator as :mod:`zipfile`
rarfile.PATH_SEP = '/'

language_converters.register('titlovi = subliminal_patch.converters.titlovi:TitloviConverter')

class TitloviSubtitle(Subtitle):
    provider_name = 'titlovi'

    def __init__(self, language, page_link, download_link, sid, releases, title, altitle=None, season=None,
                 episode=None, year=None, fps=None):
        super(TitloviSubtitle, self).__init__(language, page_link=page_link)
        self.sid = sid
        self.releases = releases
        self.title = title
        self.altitle = altitle
        self.season = season
        self.episode = episode
        self.year = year
        self.download_link = download_link
        self.fps = fps

    @property
    def id(self):
        return self.sid

    def get_matches(self, video):
        matches = set()

        # handle movies and series separately
        if isinstance(video, Episode):
            # series
            if video.series and sanitize(self.title) == fix_inconsistent_naming(video.series) or sanitize(
                    self.altitle) == fix_inconsistent_naming(video.series):
                matches.add('series')
            # year
            if video.original_series and self.year is None or video.year and video.year == self.year:
                matches.add('year')
            # season
            if video.season and self.season == video.season:
                matches.add('season')
            # episode
            if video.episode and self.episode == video.episode:
                matches.add('episode')
        # movie
        elif isinstance(video, Movie):
            # title
            if video.title and sanitize(self.title) == fix_inconsistent_naming(video.title) or sanitize(
                    self.altitle) == fix_inconsistent_naming(video.title):
                matches.add('title')
            # year
            if video.year and self.year == video.year:
                matches.add('year')

        # rest is same for both groups

        # release_group
        if (video.release_group and self.releases and
                any(r in sanitize_release_group(self.releases)
                    for r in get_equivalent_release_groups(sanitize_release_group(video.release_group)))):
            matches.add('release_group')
        # resolution
        if video.resolution and self.releases and video.resolution in self.releases.lower():
            matches.add('resolution')
        # format
        if video.format and self.releases and video.format.lower() in self.releases.lower():
            matches.add('format')
        # other properties
        matches |= guess_matches(video, guessit(self.releases))

        return matches

class TitloviProvider(Provider):
    subtitle_class = TitloviSubtitle
    languages = {Language.fromtitlovi(l) for l in language_converters['titlovi'].codes} | {Language.fromietf('sr')}
    server_url = 'http://titlovi.com'
    search_url = server_url + '/titlovi/?'
    download_url = server_url + '/download/?type=1&mediaid='

    def initialize(self):
        self.session = Session()

    def terminate(self):
        self.session.close()

    def query(self, languages, title, season=None, episode=None, year=None):
        items_per_page = 10
        current_page = 1

        # convert list of languages into search string
        langs = '|'.join(
            map(str, [l.titlovi if l != Language.fromietf('sr') else 'cirilica' for l in languages]))
        # set query params
        params = {'prijevod': title, 'jezik': langs}
        is_episode = False
        if season and episode:
            is_episode = True
            params['s'] = season
            params['e'] = episode
        if year:
            params['g'] = year

        # loop through paginated results
        logger.info('Searching subtitles %r', params)
        subtitles = []

        while True:
            # query the server
            try:
                r = self.session.get(self.search_url, params=params, timeout=10)
                r.raise_for_status()

                soup = BeautifulSoup(r.content, 'lxml')

                # number of results
                result_count = int(soup.select_one('.results_count b').string)
            except:
                result_count = None

            # exit if no results
            if not result_count:
                if not subtitles:
                    logger.debug('No subtitles found')
                else:
                    logger.debug("No more subtitles found")
                break

            # number of pages with results
            pages = int(math.ceil(result_count / float(items_per_page)))

            # get current page
            if 'pg' in params:
                current_page = params['pg']

            try:
                sublist = soup.select('section.titlovi > ul.titlovi > li')
                for sub in sublist:
                    # subtitle id
                    sid = sub.find(attrs={'data-id': True}).attrs['data-id']
                    # get download link
                    download_link = self.download_url + sid
                    # title and alternate title
                    match = title_re.search(sub.a.string)
                    if match:
                        title = match.group('title')
                        altitle = match.group('altitle')
                    # page link
                    page_link = self.server_url + sub.a.attrs['href']
                    # subtitle language
                    match = lang_re.search(sub.select_one('.lang').attrs['src'])
                    if match:
                        lang = Language.fromtitlovi(match.group('lang') + match.group('script'))
                    # relase year or series start year
                    match = year_re.search(sub.find(attrs={'data-id': True}).parent.i.string)
                    if match:
                        year = int(match.group('year'))
                    # fps
                    match = fps_re.search(sub.select_one('.fps').string)
                    if match:
                        fps = match.group('fps')
                    # releases
                    releases = sub.select_one('.fps').parent.contents[0].string

                    # handle movies and series separately
                    if is_episode:
                        # season and episode info
                        sxe = sub.select_one('.s0xe0y').string
                        if sxe:
                            match = season_re.search(sxe)
                            if match:
                                season = int(match.group('season'))
                            match = episode_re.search(sxe)
                            if match:
                                episode = int(match.group('episode'))
                        subtitle = self.subtitle_class(lang, page_link, download_link, sid, releases, title,
                                                       altitle=altitle, season=season, episode=episode, year=year,
                                                       fps=fps)
                    else:
                        subtitle = self.subtitle_class(lang, page_link, download_link, sid, releases, title,
                                                       altitle=altitle, year=year, fps=fps)
                    logger.debug('Found subtitle %r', subtitle)

                    # add found subtitles
                    subtitles.append(subtitle)
            finally:
                soup.decompose()

            # stop on last page
            if current_page >= pages:
                break

            # increment current page
            params['pg'] = current_page + 1
            logger.debug('Getting page %d', params['pg'])

        return subtitles

    def list_subtitles(self, video, languages):
        season = episode = None
        if isinstance(video, Episode):
            title = video.series
            season = video.season
            episode = video.episode
        else:
            title = video.title

        return [s for s in
                self.query(languages, fix_inconsistent_naming(title), season=season, episode=episode, year=video.year)]

    def download_subtitle(self, subtitle):
        r = self.session.get(subtitle.download_link, timeout=10)
        r.raise_for_status()

        # open the archive
        archive_stream = io.BytesIO(r.content)
        if is_rarfile(archive_stream):
            logger.debug('Archive identified as rar')
            archive = RarFile(archive_stream)
        elif is_zipfile(archive_stream):
            logger.debug('Archive identified as zip')
            archive = ZipFile(archive_stream)
        else:
            raise ProviderError('Unidentified archive type')

        # extract subtitle's content
        use_name = None
        for name in archive.namelist():
            for ext in (".srt", ".sub", ".ssa", ".ass"):
                if name.endswith(ext):
                    use_name = name
                    break

            if use_name:
                break

        if not use_name:
            raise ProviderError("None of expected subtitle formats found in archive")
        subtitle.content = fix_line_ending(archive.read(use_name))
<|endoftext|>


<commit_before>"""Graphics containers: :class:`GraphicsGroup` and :class:`GraphicsManager`.

---NODOC---

TODO:
 - make it possible for GM to have transparent BG (only if orig_sfc has alpha)
 - GG:
    - allow for transforms
    - internal layers
 - ignore off-screen things
 - if GM is fully dirty or GM.busy, draw everything without any rect checks (but still nothing under opaque)
 - GraphicsManager.offset to offset the viewing window (Surface.scroll is fast?)
    - supports parallax: set to {layer: ratio} or (function(layer) -> ratio) or set a Graphic property (make GraphicView have its own copy)
    - implementation:
        - in first loop, for each graphic, offset _rect by -offset
        - when using, offset old graphic dirty rects by -last_offset, current by -offset
        - after drawing, for each graphic, offset _rect by offset
 - do something with/like dispman

---NODOC---

"""

import sys

import pygame as pg

from ..util import ir, normalise_colour, blank_sfc, combine_drawn
try:
    from _gm import fastdraw
except ImportError:
    print >> sys.stderr, 'error: couldn\'t import _gm; did you remember to `make\'?'
    sys.exit(1)
from .graphic import Graphic, GraphicView

class GraphicsGroup (object):
    """Convenience wrapper for grouping a number of graphics in a simple way.

GraphicsGroup(x=0, y=0)

Arguments determine the group's position (:attr:`pos`); unlike for graphics,
this may be floating-point.

This is a ``{graphic: rel}`` mapping, where ``graphic`` is a
:class:`GraphicView <engine.gfx.graphic.GraphicView>` instance and ``rel`` is
the graphic's ``(x, y)`` position relative to this group.  Adding graphics is
possible with something like ``group[graphic] = rel`` (instead of using
:meth:`add`).

:attr:`graphic_attrs` contains some properties of this :class:`GraphicsGroup` which correspond to those of :class:`Graphic <engine.gfx.graphic.Graphic>`.
These can be set to apply to all contained graphics.

"""

    #: Attributes which are mapped to
    #: :class:`Graphic <engine.gfx.graphic.Graphic>` attributes.
    graphic_attrs = ('layer', 'visible', 'blit_flags', 'scale_fn', 'rotate_fn',
                     'rotate_threshold')

    def __init__ (self, x=0, y=0):
        self._pos = [x, y]
        #: {graphic: rel}
        self._graphics = {}
        self._manager = None

    def __nonzero__ (self):
        return bool(self._graphics)

    def __contains__ (self, graphic):
        return graphic in self._graphics

    def __iter__ (self):
        return iter(self._graphics)

    def __len__ (self):
        return len(self._graphics)

    def __getitem__ (self, graphic):
        return self._graphics[graphic]

    def __setitem__ (self, graphic, rel):
        self.add(graphic, *rel)

    def __delitem__ (self, graphic):
        self.rm(graphic)

    def __setattr__ (self, attr, val):
        if attr in self.graphic_attrs:
            for g in self:
                setattr(g, attr, val)
        else:
            object.__setattr__(self, attr, val)

    @property
    def rect (self):
        """The ``pygame.Rect`` covered by graphics in this group.

The top-left of this is not necessarily the same as :attr:`pos`.

"""
        graphics = dict.keys()
        if graphics:
            if len(graphics) == 1:
                return graphics[0]._rect
            else:
                return graphics[0]._rect.unionall(
                    [g._rect for g in graphics[1:]]
                )
        else:
            return pygame.Rect(0, 0, 0, 0)

    @property
    def x (self):
        """``x`` co-ordinate of the entity's top-left corner."""
        return self._pos[0]

    @x.setter
    def x (self, x):
        self.pos = (x, self._pos[1])

    @property
    def y (self):
        """``y`` co-ordinate of the entity's top-left corner."""
        return self._pos[1]

    @x.setter
    def y (self, y):
        self.pos = (self._pos[0], y)

    @property
    def pos (self):
        """``[``:attr:`x` ``,`` :attr:`y` ``]``."""
        return self._pos

    @pos.setter
    def pos (self, pos):
        x, y = pos
        self._pos = [x, y]
        # move graphics
        x = ir(x)
        y = ir(y)
        for g, (rel_x, rel_y) in self._graphics.iteritems():
            # rel_{x,y} are ints
            g.pos = (x + rel_x, y + rel_y)

    @property
    def w (self):
        """Width of :attr:`rect`."""
        return self.rect.width

    @property
    def h (self):
        """Height of :attr:`rect`."""
        return self.rect.height

    @property
    def size (self):
        """``(``:attr:`w` ``,`` :attr:`h` ``)``."""
        return self.rect.size

    def move_by (self, dx=0, dy=0):
        """Move by the given number of pixels."""
        self.pos = (self._pos[0] + dx, self._pos[1] + dy)

    def add (self, *graphics):
        """Add a graphic.

Call either as ``add(graphic, dx=0, dy=0)`` for a single graphic, or pass any
number of arguments which are ``(graphic, dx=0, dy=0)`` tuples or just
``graphic``.  In each case:

:arg graphic: :class:`Graphic <engine.gfx.graphic.Graphic>` instance or
              the ``img`` argument to
              :class:`Graphic <engine.gfx.graphic.Graphic>` to create one.
:arg dx: ``x`` co-ordinate relative to the group.
:arg dy: ``y`` co-ordinate relative to the group.

:return: a list of created
         :class:`GraphicView <engine.gfx.graphic.GraphicView>` instances that
         point to the given ``graphic`` arguments.

If any ``graphic`` was previously returned by this function, this call changes
its relative position (and unspecified ``dx`` and ``dy`` are unchanged, rather
that set to ``0``).

Note that graphics need not be added to a :class:`GraphicsManager`---set this
using :attr:`manager`.

"""
        if len(graphics) >= 2 and isinstance(graphics[1], (int, float)):
            graphics = (graphics,)
        rtn = []
        for graphic in graphics:
            # parse argument
            if isinstance(graphic, (Graphic, pg.Surface, basestring)):
                graphic = [graphic]
            else:
                graphic = list(graphic)
            if len(graphic) < 2:
                graphic.append(None)
            if len(graphic) < 3:
                graphic.append(None)
            graphic, dx, dy = graphic
            # determine new position for the graphic
            got = graphic in self._graphics
            if got:
                if dx is None:
                    dx = self._graphics[graphic][0]
                if dy is None:
                    dy = self._graphics[graphic][1]
            else:
                if dx is None:
                    dx = 0
                if dy is None:
                    dy = 0
            rel = (ir(dx), ir(dy))
            pos = (ir(self._pos[0]) + rel[0], ir(self._pos[1]) + rel[1])
            # create/wrap graphic
            if not isinstance(graphic, Graphic):
                graphic = Graphic(graphic, pos)
            if not got:
                # new graphic: create wrapper and add to graphics manager
                if isinstance(graphic, GraphicView):
                    graphic = graphic.graphic
                graphic = GraphicView(graphic)
                if self._manager is not None:
                    self._manager.add(graphic)
            # else already in graphics, in which case we still want to change its
            # relative position
            self._graphics[graphic] = rel
            graphic.pos = pos
            rtn.append(graphic)
        return rtn

    def rm (self, *graphics):
        """Remove a graphic previously added using :meth:`add`.

Raises ``KeyError`` for missing graphics.

"""
        gm = self._gm
        for g in graphics:
            del self._graphics[g]
            if gm is not None:
                gm.rm(g)

    @property
    def manager (self):
        """The :class:`GraphicsManager <engine.gfx.container.GraphicsManager>`
to put graphics in."""
        return self._manager

    @manager.setter
    def manager (self, manager):
        if manager is self._manager:
            return
        if self._manager is not None:
            self._manager.rm(*self._graphics)
        if manager is not None:
            manager.add(*self._graphics)
        self._manager = manager

class GraphicsManager (Graphic):
    """Draws things to a surface intelligently.
:class:`Graphic <engine.gfx.graphic.Graphic>` subclass.

GraphicsManager(scheduler[, sfc], pos = (0, 0), layer = 0, blit_flags = 0)

:arg scheduler: a :class:`sched.Scheduler <engine.sched.Scheduler>` instance
                this manager should use for timing.
:arg sfc: the surface to draw to; can be a ``(width, height)`` tuple to create
          a new transparent surface of this size.  If not given or ``None``,
          nothing is drawn.  This becomes :attr:`orig_sfc` and can be changed
          using this attribute.

Other arguments are as taken by :class:`Graphic <engine.gfx.graphic.Graphic>`.
Since this is a :class:`Graphic <engine.gfx.graphic.Graphic>` subclass, it can
be added to other :class:`GraphicsManager` instances and supports
transformations.  None of this can be done until the manager has a surface,
however, and transformations are only applied in
:attr:`Graphic.surface <engine.gfx.graphic.Graphic.surface>`, not in
:attr:`orig_sfc`.

"""

    def __init__ (self, scheduler, sfc = None, pos = (0, 0), layer = 0,
                  blit_flags = 0):
        #: The ``scheduler`` argument passed to the constructor.
        self.scheduler = scheduler
        self._init_as_graphic = False
        self._init_as_graphic_args = (pos, layer, blit_flags)
        self._orig_sfc = None
        self.orig_sfc = sfc # calls setter
        self._gm_dirty = False
        self._overlay = None
        self._fade_id = None
        #: ``{layer: graphics}`` dict, where ``graphics`` is a set of the
        #: graphics in layer ``layer``, each as taken by :meth:`add`.
        self.graphics = {}
        #: A list of layers that contain graphics, lowest first.
        self.layers = []

    @property
    def orig_sfc (self):
        """Like :attr:`Graphic.orig_sfc <engine.gfx.graphic.Graphic.orig_sfc>`.

This is the ``sfc`` argument passed to the constructor.  Retrieving this causes
all graphics to be drawn/updated first.

"""
        self.draw()
        return Graphic.orig_sfc.fget(self)

    @orig_sfc.setter
    def orig_sfc (self, sfc):
        if sfc is not None and not isinstance(sfc, pg.Surface):
            sfc = blank_sfc(sfc)
        if sfc is not self._orig_sfc:
            self._orig_sfc = sfc
            if sfc is not None:
                if self._init_as_graphic:
                    Graphic.orig_sfc.fset(self, sfc)
                else:
                    Graphic.__init__(self, sfc, *self._init_as_graphic_args)
                    self._init_as_graphic = True
                    del self._init_as_graphic_args

    @property
    def orig_sz (self):
        """The size of the surface before any transforms."""
        return self._orig_sfc.get_size()

    @property
    def overlay (self):
        """A :class:`Graphic <engine.gfx.graphic.Graphic>` which is always
drawn on top, or ``None``.

There may only ever be one overlay; changing this attribute removes any
previous overlay from the :class:`GraphicsManager`.

"""
        return self._overlay

    @overlay.setter
    def overlay (self, overlay):
        # remove any previous overlay
        if self._overlay is not None:
            self.rm(self._overlay)
        # set now since used in add()
        self._overlay = overlay
        if overlay is not None:
            # remove any current manager
            overlay.manager = None
            # put in the reserved layer None (sorts less than any other object)
            overlay._layer = None
            # add to this manager
            self.add(overlay)

    def add (self, *graphics):
        """Add graphics.

Takes any number of :class:`Graphic <engine.gfx.graphic.Graphic>` instances.

"""
        all_gs = self.graphics
        ls = set(self.layers)
        for g in graphics:
            l = g.layer
            if l is None and g is not self._overlay:
                raise ValueError('a graphic\'s layer must not be None')
            if l in ls:
                all_gs[l].add(g)
            else:
                all_gs[l] = set((g,))
                ls.add(l)
            g._manager = self
            # don't draw over any possible previous location
            g.was_visible = False
        self.layers = sorted(ls)

    def rm (self, *graphics):
        """Remove graphics.

Takes any number of :class:`Graphic <engine.gfx.graphic.Graphic>` instances.
Missing graphics are ignored.

"""
        all_graphics = self.graphics
        ls = set(self.layers)
        for g in graphics:
            l = g.layer
            if l in ls:
                all_gs = all_graphics[l]
                if g in all_gs:
                    # remove from graphics
                    all_gs.remove(g)
                    g._manager = None
                    # draw over previous location
                    if g.was_visible:
                        self.dirty(g._last_postrot_rect)
                    # remove layer
                    if not all_gs:
                        del all_graphics[l]
                        ls.remove(l)
            # else not added: fail silently
        self.layers = sorted(ls)

    def fade_to (self, colour, t):
        """Fade to a colour.

:arg colour: the ``(R, G, B[, A = 255])`` colour to fade to.
:arg t: how many seconds to take to reach ``colour``.

If already fading, the current colour is used as the initial colour; otherwise,
the initial colour is taken to be ``(R, G, B, 0)`` for the given value of
``colour``.  After fading, the overlay persists; set :attr:`overlay` to
``None`` to remove it.

"""
        colour = normalise_colour(colour)
        if self._fade_id is None:
            # doesn't already exist
            self.overlay = Colour(colour[:3] + (0,), ((0, 0), self._rect.size))
        else:
            self.cancel_fade()
        self._fade_id = self.scheduler.interp_simple(self._overlay, 'colour',
                                                     colour, t,
                                                     round_val = True)

    def cancel_fade (self):
        """Cancel any currently running fade and remove the overlay."""
        if self._fade_id is not None:
            self.scheduler.rm_timeout(self._fade_id)
            self._fade_id = None
            self.overlay = None

    def dirty (self, *rects):
        """:meth:`Graphic.dirty <engine.gfx.graphic.Graphic.dirty>`"""
        if self._surface is None:
            # nothing to mark as dirty
            return
        if not rects:
            rects = True
        self._gm_dirty = combine_drawn(self._gm_dirty, rects)

    def draw (self, handle_dirty = True):
        """Update the display (:attr:`orig_sfc`).

:arg handle_dirty: whether to propagate changed areas to the transformation
                   pipeline implemented by
                   :class:`Graphic <engine.gfx.graphic.Graphic>`.  Pass
                   ``False`` if you don't intend to use this manager as a
                   graphic.

Returns ``True`` if the entire surface changed, or a list of rects that cover
changed parts of the surface, or ``False`` if nothing changed.

"""
        layers = self.layers
        sfc = self._orig_sfc
        if not layers or sfc is None:
            return False
        graphics = self.graphics
        dirty = self._gm_dirty
        self._gm_dirty = []
        if dirty is True:
            dirty = [sfc.get_rect()]
        elif dirty is False:
            dirty = []
        dirty = fastdraw(layers, sfc, graphics, dirty)
        if dirty and handle_dirty:
            Graphic.dirty(self, *dirty)
        if self._orig_dirty:
            dirty = combine_drawn(dirty, self._orig_dirty)
            if not handle_dirty:
                self._orig_dirty = False
        return dirty

    def render (self):
        """:meth:`Graphic.render <engine.gfx.graphic.Graphic.render>`"""
        self.draw()
        Graphic.render(self)
<commit_msg>fix GraphicsGroup.rect<commit_after>"""Graphics containers: :class:`GraphicsGroup` and :class:`GraphicsManager`.

---NODOC---

TODO:
 - make it possible for GM to have transparent BG (only if orig_sfc has alpha)
 - GG:
    - allow for transforms
    - internal layers
 - ignore off-screen things
 - if GM is fully dirty or GM.busy, draw everything without any rect checks (but still nothing under opaque)
 - GraphicsManager.offset to offset the viewing window (Surface.scroll is fast?)
    - supports parallax: set to {layer: ratio} or (function(layer) -> ratio) or set a Graphic property (make GraphicView have its own copy)
    - implementation:
        - in first loop, for each graphic, offset _rect by -offset
        - when using, offset old graphic dirty rects by -last_offset, current by -offset
        - after drawing, for each graphic, offset _rect by offset
 - do something with/like dispman

---NODOC---

"""

import sys

import pygame as pg

from ..util import ir, normalise_colour, blank_sfc, combine_drawn
try:
    from _gm import fastdraw
except ImportError:
    print >> sys.stderr, 'error: couldn\'t import _gm; did you remember to `make\'?'
    sys.exit(1)
from .graphic import Graphic, GraphicView

class GraphicsGroup (object):
    """Convenience wrapper for grouping a number of graphics in a simple way.

GraphicsGroup(x=0, y=0)

Arguments determine the group's position (:attr:`pos`); unlike for graphics,
this may be floating-point.

This is a ``{graphic: rel}`` mapping, where ``graphic`` is a
:class:`GraphicView <engine.gfx.graphic.GraphicView>` instance and ``rel`` is
the graphic's ``(x, y)`` position relative to this group.  Adding graphics is
possible with something like ``group[graphic] = rel`` (instead of using
:meth:`add`).

:attr:`graphic_attrs` contains some properties of this :class:`GraphicsGroup` which correspond to those of :class:`Graphic <engine.gfx.graphic.Graphic>`.
These can be set to apply to all contained graphics.

"""

    #: Attributes which are mapped to
    #: :class:`Graphic <engine.gfx.graphic.Graphic>` attributes.
    graphic_attrs = ('layer', 'visible', 'blit_flags', 'scale_fn', 'rotate_fn',
                     'rotate_threshold')

    def __init__ (self, x=0, y=0):
        self._pos = [x, y]
        #: {graphic: rel}
        self._graphics = {}
        self._manager = None

    def __nonzero__ (self):
        return bool(self._graphics)

    def __contains__ (self, graphic):
        return graphic in self._graphics

    def __iter__ (self):
        return iter(self._graphics)

    def __len__ (self):
        return len(self._graphics)

    def __getitem__ (self, graphic):
        return self._graphics[graphic]

    def __setitem__ (self, graphic, rel):
        self.add(graphic, *rel)

    def __delitem__ (self, graphic):
        self.rm(graphic)

    def __setattr__ (self, attr, val):
        if attr in self.graphic_attrs:
            for g in self:
                setattr(g, attr, val)
        else:
            object.__setattr__(self, attr, val)

    @property
    def rect (self):
        """The ``pygame.Rect`` covered by graphics in this group.

The top-left of this is not necessarily the same as :attr:`pos`.

"""
        graphics = self._graphics.keys()
        if graphics:
            if len(graphics) == 1:
                return graphics[0]._rect
            else:
                return graphics[0]._rect.unionall(
                    [g._rect for g in graphics[1:]]
                )
        else:
            return pygame.Rect(0, 0, 0, 0)

    @property
    def x (self):
        """``x`` co-ordinate of the entity's top-left corner."""
        return self._pos[0]

    @x.setter
    def x (self, x):
        self.pos = (x, self._pos[1])

    @property
    def y (self):
        """``y`` co-ordinate of the entity's top-left corner."""
        return self._pos[1]

    @x.setter
    def y (self, y):
        self.pos = (self._pos[0], y)

    @property
    def pos (self):
        """``[``:attr:`x` ``,`` :attr:`y` ``]``."""
        return self._pos

    @pos.setter
    def pos (self, pos):
        x, y = pos
        self._pos = [x, y]
        # move graphics
        x = ir(x)
        y = ir(y)
        for g, (rel_x, rel_y) in self._graphics.iteritems():
            # rel_{x,y} are ints
            g.pos = (x + rel_x, y + rel_y)

    @property
    def w (self):
        """Width of :attr:`rect`."""
        return self.rect.width

    @property
    def h (self):
        """Height of :attr:`rect`."""
        return self.rect.height

    @property
    def size (self):
        """``(``:attr:`w` ``,`` :attr:`h` ``)``."""
        return self.rect.size

    def move_by (self, dx=0, dy=0):
        """Move by the given number of pixels."""
        self.pos = (self._pos[0] + dx, self._pos[1] + dy)

    def add (self, *graphics):
        """Add a graphic.

Call either as ``add(graphic, dx=0, dy=0)`` for a single graphic, or pass any
number of arguments which are ``(graphic, dx=0, dy=0)`` tuples or just
``graphic``.  In each case:

:arg graphic: :class:`Graphic <engine.gfx.graphic.Graphic>` instance or
              the ``img`` argument to
              :class:`Graphic <engine.gfx.graphic.Graphic>` to create one.
:arg dx: ``x`` co-ordinate relative to the group.
:arg dy: ``y`` co-ordinate relative to the group.

:return: a list of created
         :class:`GraphicView <engine.gfx.graphic.GraphicView>` instances that
         point to the given ``graphic`` arguments.

If any ``graphic`` was previously returned by this function, this call changes
its relative position (and unspecified ``dx`` and ``dy`` are unchanged, rather
that set to ``0``).

Note that graphics need not be added to a :class:`GraphicsManager`---set this
using :attr:`manager`.

"""
        if len(graphics) >= 2 and isinstance(graphics[1], (int, float)):
            graphics = (graphics,)
        rtn = []
        for graphic in graphics:
            # parse argument
            if isinstance(graphic, (Graphic, pg.Surface, basestring)):
                graphic = [graphic]
            else:
                graphic = list(graphic)
            if len(graphic) < 2:
                graphic.append(None)
            if len(graphic) < 3:
                graphic.append(None)
            graphic, dx, dy = graphic
            # determine new position for the graphic
            got = graphic in self._graphics
            if got:
                if dx is None:
                    dx = self._graphics[graphic][0]
                if dy is None:
                    dy = self._graphics[graphic][1]
            else:
                if dx is None:
                    dx = 0
                if dy is None:
                    dy = 0
            rel = (ir(dx), ir(dy))
            pos = (ir(self._pos[0]) + rel[0], ir(self._pos[1]) + rel[1])
            # create/wrap graphic
            if not isinstance(graphic, Graphic):
                graphic = Graphic(graphic, pos)
            if not got:
                # new graphic: create wrapper and add to graphics manager
                if isinstance(graphic, GraphicView):
                    graphic = graphic.graphic
                graphic = GraphicView(graphic)
                if self._manager is not None:
                    self._manager.add(graphic)
            # else already in graphics, in which case we still want to change its
            # relative position
            self._graphics[graphic] = rel
            graphic.pos = pos
            rtn.append(graphic)
        return rtn

    def rm (self, *graphics):
        """Remove a graphic previously added using :meth:`add`.

Raises ``KeyError`` for missing graphics.

"""
        gm = self._gm
        for g in graphics:
            del self._graphics[g]
            if gm is not None:
                gm.rm(g)

    @property
    def manager (self):
        """The :class:`GraphicsManager <engine.gfx.container.GraphicsManager>`
to put graphics in."""
        return self._manager

    @manager.setter
    def manager (self, manager):
        if manager is self._manager:
            return
        if self._manager is not None:
            self._manager.rm(*self._graphics)
        if manager is not None:
            manager.add(*self._graphics)
        self._manager = manager

class GraphicsManager (Graphic):
    """Draws things to a surface intelligently.
:class:`Graphic <engine.gfx.graphic.Graphic>` subclass.

GraphicsManager(scheduler[, sfc], pos = (0, 0), layer = 0, blit_flags = 0)

:arg scheduler: a :class:`sched.Scheduler <engine.sched.Scheduler>` instance
                this manager should use for timing.
:arg sfc: the surface to draw to; can be a ``(width, height)`` tuple to create
          a new transparent surface of this size.  If not given or ``None``,
          nothing is drawn.  This becomes :attr:`orig_sfc` and can be changed
          using this attribute.

Other arguments are as taken by :class:`Graphic <engine.gfx.graphic.Graphic>`.
Since this is a :class:`Graphic <engine.gfx.graphic.Graphic>` subclass, it can
be added to other :class:`GraphicsManager` instances and supports
transformations.  None of this can be done until the manager has a surface,
however, and transformations are only applied in
:attr:`Graphic.surface <engine.gfx.graphic.Graphic.surface>`, not in
:attr:`orig_sfc`.

"""

    def __init__ (self, scheduler, sfc = None, pos = (0, 0), layer = 0,
                  blit_flags = 0):
        #: The ``scheduler`` argument passed to the constructor.
        self.scheduler = scheduler
        self._init_as_graphic = False
        self._init_as_graphic_args = (pos, layer, blit_flags)
        self._orig_sfc = None
        self.orig_sfc = sfc # calls setter
        self._gm_dirty = False
        self._overlay = None
        self._fade_id = None
        #: ``{layer: graphics}`` dict, where ``graphics`` is a set of the
        #: graphics in layer ``layer``, each as taken by :meth:`add`.
        self.graphics = {}
        #: A list of layers that contain graphics, lowest first.
        self.layers = []

    @property
    def orig_sfc (self):
        """Like :attr:`Graphic.orig_sfc <engine.gfx.graphic.Graphic.orig_sfc>`.

This is the ``sfc`` argument passed to the constructor.  Retrieving this causes
all graphics to be drawn/updated first.

"""
        self.draw()
        return Graphic.orig_sfc.fget(self)

    @orig_sfc.setter
    def orig_sfc (self, sfc):
        if sfc is not None and not isinstance(sfc, pg.Surface):
            sfc = blank_sfc(sfc)
        if sfc is not self._orig_sfc:
            self._orig_sfc = sfc
            if sfc is not None:
                if self._init_as_graphic:
                    Graphic.orig_sfc.fset(self, sfc)
                else:
                    Graphic.__init__(self, sfc, *self._init_as_graphic_args)
                    self._init_as_graphic = True
                    del self._init_as_graphic_args

    @property
    def orig_sz (self):
        """The size of the surface before any transforms."""
        return self._orig_sfc.get_size()

    @property
    def overlay (self):
        """A :class:`Graphic <engine.gfx.graphic.Graphic>` which is always
drawn on top, or ``None``.

There may only ever be one overlay; changing this attribute removes any
previous overlay from the :class:`GraphicsManager`.

"""
        return self._overlay

    @overlay.setter
    def overlay (self, overlay):
        # remove any previous overlay
        if self._overlay is not None:
            self.rm(self._overlay)
        # set now since used in add()
        self._overlay = overlay
        if overlay is not None:
            # remove any current manager
            overlay.manager = None
            # put in the reserved layer None (sorts less than any other object)
            overlay._layer = None
            # add to this manager
            self.add(overlay)

    def add (self, *graphics):
        """Add graphics.

Takes any number of :class:`Graphic <engine.gfx.graphic.Graphic>` instances.

"""
        all_gs = self.graphics
        ls = set(self.layers)
        for g in graphics:
            l = g.layer
            if l is None and g is not self._overlay:
                raise ValueError('a graphic\'s layer must not be None')
            if l in ls:
                all_gs[l].add(g)
            else:
                all_gs[l] = set((g,))
                ls.add(l)
            g._manager = self
            # don't draw over any possible previous location
            g.was_visible = False
        self.layers = sorted(ls)

    def rm (self, *graphics):
        """Remove graphics.

Takes any number of :class:`Graphic <engine.gfx.graphic.Graphic>` instances.
Missing graphics are ignored.

"""
        all_graphics = self.graphics
        ls = set(self.layers)
        for g in graphics:
            l = g.layer
            if l in ls:
                all_gs = all_graphics[l]
                if g in all_gs:
                    # remove from graphics
                    all_gs.remove(g)
                    g._manager = None
                    # draw over previous location
                    if g.was_visible:
                        self.dirty(g._last_postrot_rect)
                    # remove layer
                    if not all_gs:
                        del all_graphics[l]
                        ls.remove(l)
            # else not added: fail silently
        self.layers = sorted(ls)

    def fade_to (self, colour, t):
        """Fade to a colour.

:arg colour: the ``(R, G, B[, A = 255])`` colour to fade to.
:arg t: how many seconds to take to reach ``colour``.

If already fading, the current colour is used as the initial colour; otherwise,
the initial colour is taken to be ``(R, G, B, 0)`` for the given value of
``colour``.  After fading, the overlay persists; set :attr:`overlay` to
``None`` to remove it.

"""
        colour = normalise_colour(colour)
        if self._fade_id is None:
            # doesn't already exist
            self.overlay = Colour(colour[:3] + (0,), ((0, 0), self._rect.size))
        else:
            self.cancel_fade()
        self._fade_id = self.scheduler.interp_simple(self._overlay, 'colour',
                                                     colour, t,
                                                     round_val = True)

    def cancel_fade (self):
        """Cancel any currently running fade and remove the overlay."""
        if self._fade_id is not None:
            self.scheduler.rm_timeout(self._fade_id)
            self._fade_id = None
            self.overlay = None

    def dirty (self, *rects):
        """:meth:`Graphic.dirty <engine.gfx.graphic.Graphic.dirty>`"""
        if self._surface is None:
            # nothing to mark as dirty
            return
        if not rects:
            rects = True
        self._gm_dirty = combine_drawn(self._gm_dirty, rects)

    def draw (self, handle_dirty = True):
        """Update the display (:attr:`orig_sfc`).

:arg handle_dirty: whether to propagate changed areas to the transformation
                   pipeline implemented by
                   :class:`Graphic <engine.gfx.graphic.Graphic>`.  Pass
                   ``False`` if you don't intend to use this manager as a
                   graphic.

Returns ``True`` if the entire surface changed, or a list of rects that cover
changed parts of the surface, or ``False`` if nothing changed.

"""
        layers = self.layers
        sfc = self._orig_sfc
        if not layers or sfc is None:
            return False
        graphics = self.graphics
        dirty = self._gm_dirty
        self._gm_dirty = []
        if dirty is True:
            dirty = [sfc.get_rect()]
        elif dirty is False:
            dirty = []
        dirty = fastdraw(layers, sfc, graphics, dirty)
        if dirty and handle_dirty:
            Graphic.dirty(self, *dirty)
        if self._orig_dirty:
            dirty = combine_drawn(dirty, self._orig_dirty)
            if not handle_dirty:
                self._orig_dirty = False
        return dirty

    def render (self):
        """:meth:`Graphic.render <engine.gfx.graphic.Graphic.render>`"""
        self.draw()
        Graphic.render(self)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Set Up:
    ::python2 or python 3(preferred) flavor of  (miniconda) installed
Usage:
    ::mal_file -> a file name containing malicious strings: 'xss.txt'
                  OR
               -> a list of values haing malicious strings : ["alert}>'><script>alert(<fin2000>)</script>", "<script>alert(<fin2000>)</script>", ...]
               Each malicious string is set for all the keys in the json structure and posted

Dependency: UtilsLib.py in the same directory as that of script
'''

import requests
import sys
import time

from  UtilsLibFuzzing_v1 import Utils
from requests.exceptions import ConnectionError
#####################################################################
requests.packages.urllib3.disable_warnings()  # supress https warnings
######################################################################
a = [{
      "partnerOrderId": "xxxxxx",
    "productType": "hujhuuu",
    "csr": "dgdghdfh",
    "serverType": "Apache",
    "validityPeriodDays": 365,
    "authType": "DNS",
    "domain": {
      "cn": "sfs.net",
      "sans": None
               },
    "org": {
      "orgName": "xxxxxx",
      "orgUnit": "Eng",
      "address": {
        "addressLine1": "4201 norwalk dr1",
        "addressLine2": "",
        "addressLine3": "",
        "phoneNumber": "",
        "city": "san jose",
        "state": "$california$",
        "country": "us",
        "zip": "95129"}

      }

  ,
       "certTransparency":{
        "ctLogging":False
     },

    "signatureAlgorithm": "sha256WithRSAEncryption",
    "certChainType": "MIXED",
    "locale": None
  }
]
######################################################################
u = Utils()  # create instance
#######################################################################
authType = 'Basic '
auth_token = 'ssgsgsgsdhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh'
api_url_enroll = 'https://ee-api1-test.ws.symclab.net/ssl/v1/enroll'
#mal_file = ["alert}>'><script>alert(<fin2000>)</script>"]
mal_file = 'test.txt'

########################################################################
time_i = str(time.ctime()).replace(' ', '-').replace(':', '-')
enroll_fail_file = 'Enroll_Fail_' + time_i + '.txt'
enroll_pass_file = enroll_fail_file.replace('Fail', 'Pass')
network_issues_file = enroll_fail_file.replace('Fail', 'network_issues')

open(enroll_fail_file, 'w').close()  # create the files if does not exist
open(enroll_pass_file, 'w').close()  # create teh file if does not exist
open(network_issues_file, 'w').close()

########################################################################
counter = 0

<<<<<<< HEAD
for postdata, value, key in u.postdata_generator_with_insecure_values_ee(a, mal_file):
=======
counter = 0
for postdata in u.generator_with_insecure_values_POST_req(a, mal_file):
>>>>>>> 0eb36333cadcc759558a55de10e91b590c2b2005
    counter+=1
    #print(value)

    try:
        resp = requests.post(api_url_enroll,
                             json=postdata,
                             verify=False,
                             headers={'Authorization': authType + auth_token})

        if resp.status_code in [200, 201]:
            outputfile = enroll_pass_file
        else:
            outputfile = enroll_fail_file
        print(resp.status_code)

        u.write_details_to_file_ee(outputfile,
                                   postdata,
                                   'POST response : Here is output::'+ resp.text,
                                   'Status Code::' + str(resp.status_code),
                                   '=END=' * 5)
    except ConnectionError:
        print('-' * 20 )
        print('----Connection Issues---')
        print('-' * 20 )
        #sys.exit(-1)
<commit_msg>minor<commit_after># -*- coding: utf-8 -*-
'''
Set Up:
    ::python2 or python 3(preferred) flavor of  (miniconda) installed
Usage:
    ::mal_file -> a file name containing malicious strings: 'xss.txt'
                  OR
               -> a list of values haing malicious strings : ["alert}>'><script>alert(<fin2000>)</script>", "<script>alert(<fin2000>)</script>", ...]
               Each malicious string is set for all the keys in the json structure and posted

Dependency: UtilsLib.py in the same directory as that of script
'''

import requests
import sys
import time

from  UtilsLibFuzzing_v1 import Utils
from requests.exceptions import ConnectionError
#####################################################################
requests.packages.urllib3.disable_warnings()  # supress https warnings
######################################################################
a = [{
      "partnerOrderId": "xxxxxx",
    "productType": "hujhuuu",
    "csr": "dgdghdfh",
    "serverType": "Apache",
    "validityPeriodDays": 365,
    "authType": "DNS",
    "domain": {
      "cn": "sfs.net",
      "sans": None
               },
    "org": {
      "orgName": "xxxxxx",
      "orgUnit": "Eng",
      "address": {
        "addressLine1": "4201 norwalk dr1",
        "addressLine2": "",
        "addressLine3": "",
        "phoneNumber": "",
        "city": "san jose",
        "state": "$california$",
        "country": "us",
        "zip": "95129"}

      }

  ,
       "certTransparency":{
        "ctLogging":False
     },

    "signatureAlgorithm": "sha256WithRSAEncryption",
    "certChainType": "MIXED",
    "locale": None
  }
]
######################################################################
u = Utils()  # create instance
#######################################################################
authType = 'Basic '
auth_token = 'ssgsgsgsdhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh'
api_url_enroll = 'https://ee-api1-test.ws.symclab.net/ssl/v1/enroll'
#mal_file = ["alert}>'><script>alert(<fin2000>)</script>"]
mal_file = 'test.txt'

########################################################################
time_i = str(time.ctime()).replace(' ', '-').replace(':', '-')
enroll_fail_file = 'Enroll_Fail_' + time_i + '.txt'
enroll_pass_file = enroll_fail_file.replace('Fail', 'Pass')
network_issues_file = enroll_fail_file.replace('Fail', 'network_issues')

open(enroll_fail_file, 'w').close()  # create the files if does not exist
open(enroll_pass_file, 'w').close()  # create teh file if does not exist
open(network_issues_file, 'w').close()

########################################################################

counter = 0
for postdata in u.generator_with_insecure_values_POST_req(a, mal_file):

    counter+=1
    #print(value)

    try:
        resp = requests.post(api_url_enroll,
                             json=postdata,
                             verify=False,
                             headers={'Authorization': authType + auth_token})

        if resp.status_code in [200, 201]:
            outputfile = enroll_pass_file
        else:
            outputfile = enroll_fail_file
        print(resp.status_code)

        u.write_details_to_file_ee(outputfile,
                                   postdata,
                                   'POST response : Here is output::'+ resp.text,
                                   'Status Code::' + str(resp.status_code),
                                   '=END=' * 5)
    except ConnectionError:
        print('-' * 20 )
        print('----Connection Issues---')
        print('-' * 20 )
        #sys.exit(-1)
<|endoftext|>


<commit_before><commit_msg>docker_collector.py: support docker python module<commit_after><|endoftext|>