<commit_before># -*- encoding: utf-8 -*-

#
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
#
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
###############################################################################

import netsvc
import tools
from dav_fs import openerp_dav_handler
from tools.config import config
from DAV.WebDAVServer import DAVRequestHandler
from service.websrv_lib import HTTPDir, FixSendError, HttpOptions
from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse
import urllib
from string import atoi,split
from DAV.errors import *
# from DAV.constants import DAV_VERSION_1, DAV_VERSION_2

def OpenDAVConfig(**kw):
    class OpenDAV:
        def __init__(self, **kw):
            self.__dict__.update(**kw)

        def getboolean(self, word):
            return self.__dict__.get(word, False)

    class Config:
        DAV = OpenDAV(**kw)

    return Config()

class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler):
    verbose = False
    protocol_version = 'HTTP/1.1'
    _HTTP_OPTIONS= { 'DAV' : ['1', '2'],
                    'Allow' : [ 'GET', 'HEAD', 'COPY', 'MOVE', 'POST', 'PUT',
                            'PROPFIND', 'PROPPATCH', 'OPTIONS', 'MKCOL',
                            'DELETE', 'TRACE', 'REPORT', ]
                    }

    def get_userinfo(self,user,pw):
        return False
    def _log(self, message):
        netsvc.Logger().notifyChannel("webdav",netsvc.LOG_DEBUG,message)

    def handle(self):
        self._init_buffer()

    def finish(self):
        pass

    def get_db_from_path(self, uri):
        # interface class will handle all cases.
        res =  self.IFACE_CLASS.get_db(uri, allow_last=True)
        return res

    def setup(self):
        self.davpath = '/'+config.get_misc('webdav','vdir','webdav')
        addr, port = self.server.server_name, self.server.server_port
        server_proto = getattr(self.server,'proto', 'http').lower()
        try:
            if hasattr(self.request, 'getsockname'):
                addr, port = self.request.getsockname()
        except Exception, e:
            self.log_error("Cannot calculate own address: %s" , e)
        # Too early here to use self.headers
        self.baseuri = "%s://%s:%d/"% (server_proto, addr, port)
        self.IFACE_CLASS  = openerp_dav_handler(self, self.verbose)

    def copymove(self, CLASS):
        """ Our uri scheme removes the /webdav/ component from there, so we
        need to mangle the header, too.
        """
        dest = self.headers['Destination']
        up = urlparse.urlparse(urllib.unquote(self.headers['Destination']))
        if up.path.startswith(self.davpath):
            self.headers['Destination'] = up.path[len(self.davpath):]
        else:
            raise DAV_Forbidden("Not allowed to copy/move outside webdav path")
        DAVRequestHandler.copymove(self, CLASS)

    def get_davpath(self):
        return self.davpath

    def log_message(self, format, *args):
        netsvc.Logger().notifyChannel('webdav', netsvc.LOG_DEBUG_RPC, format % args)

    def log_error(self, format, *args):
        netsvc.Logger().notifyChannel('xmlrpc', netsvc.LOG_WARNING, format % args)

    def _prep_OPTIONS(self, opts):
        ret = opts
        dc=self.IFACE_CLASS
        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
        uri=urllib.unquote(uri)
        try:
            ret = dc.prep_http_options(uri, opts)
        except DAV_Error, (ec,dd):
            pass
        except Exception,e:
            self.log_error("Error at options: %s", str(e))
            raise
        return ret

    def send_response(self, code, message=None):
        # the BufferingHttpServer will send Connection: close , while
        # the BaseHTTPRequestHandler will only accept int code.
        # workaround both of them.
        BaseHTTPRequestHandler.send_response(self, int(code), message)

    def send_header(self, key, value):
        if key == 'Connection' and value == 'close':
            self.close_connection = 1
        DAVRequestHandler.send_header(self, key, value)

    def send_body(self, DATA, code = None, msg = None, desc = None, ctype='application/octet-stream', headers=None):
        if headers and 'Connection' in headers:
            pass
        elif self.request_version in ('HTTP/1.0', 'HTTP/0.9'):
            pass
        elif self.close_connection == 1: # close header already sent
            pass
        else:
            if headers is None:
                headers = {}
            if self.headers.get('Connection',False) == 'Keep-Alive':
                headers['Connection'] = 'keep-alive'

        DAVRequestHandler.send_body(self, DATA, code=code, msg=msg, desc=desc,
                    ctype=ctype, headers=headers)

    def do_PUT(self):
        dc=self.IFACE_CLASS
        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
        uri=urllib.unquote(uri)
        # Handle If-Match
        if self.headers.has_key('If-Match'):
            test = False
            etag = None

            for match in self.headers['If-Match'].split(','):
                if match == '*':
                    if dc.exists(uri):
                        test = True
                        break
                else:
                    if dc.match_prop(uri, match, "DAV:", "getetag"):
                        test = True
                        break
            if not test:
                self._get_body()
                self.send_status(412)
                return

        # Handle If-None-Match
        if self.headers.has_key('If-None-Match'):
            test = True
            etag = None
            for match in self.headers['If-None-Match'].split(','):
                if match == '*':
                    if dc.exists(uri):
                        test = False
                        break
                else:
                    if dc.match_prop(uri, match, "DAV:", "getetag"):
                        test = False
                        break
            if not test:
                self._get_body()
                self.send_status(412)
                return

        # Handle expect
        expect = self.headers.get('Expect', '')
        if (expect.lower() == '100-continue' and
                self.protocol_version >= 'HTTP/1.1' and
                self.request_version >= 'HTTP/1.1'):
            self.send_status(100)
            self._flush()

        # read the body
        body=self._get_body()

        # locked resources are not allowed to be overwritten
        if self._l_isLocked(uri):
            return self.send_body(None, '423', 'Locked', 'Locked')

        ct=None
        if self.headers.has_key("Content-Type"):
            ct=self.headers['Content-Type']
        try:
            location = dc.put(uri, body, ct)
        except DAV_Error, (ec,dd):
            self._logger.warning("Cannot PUT to %s: %s", uri, dd, exc_info=True)
            return self.send_status(ec)

        headers = {}
        etag = None
        if location and isinstance(location, tuple):
            etag = location[1]
            location = location[0]
            # note that we have allowed for > 2 elems
        if location:
            headers['Location'] = location
        else:
            try:
                if not etag:
                    etag = dc.get_prop(location or uri, "DAV:", "getetag")
                if etag:
                    headers['ETag'] = str(etag)
            except Exception:
                pass

        self.send_body(None, '201', 'Created', '', headers=headers)

    def _get_body(self):
        body = None
        if self.headers.has_key("Content-Length"):
            l=self.headers['Content-Length']
            body=self.rfile.read(atoi(l))
        return body

    def do_DELETE(self):
        try:
            DAVRequestHandler.do_DELETE(self)
        except DAV_Error, (ec, dd):
            return self.send_status(ec)

from service.http_server import reg_http_service,OpenERPAuthProvider

class DAVAuthProvider(OpenERPAuthProvider):
    def authenticate(self, db, user, passwd, client_address):
        """ authenticate, but also allow the False db, meaning to skip
            authentication when no db is specified.
        """
        if db is False:
            return True
        return OpenERPAuthProvider.authenticate(self, db, user, passwd, client_address)

try:

    if (config.get_misc('webdav','enable',True)):
        directory = '/'+config.get_misc('webdav','vdir','webdav')
        handler = DAVHandler
        verbose = config.get_misc('webdav','verbose',True)
        handler.debug = config.get_misc('webdav','debug',True)
        _dc = { 'verbose' : verbose,
                'directory' : directory,
                'lockemulation' : True,
                }

        conf = OpenDAVConfig(**_dc)
        handler._config = conf
        reg_http_service(HTTPDir(directory,DAVHandler,DAVAuthProvider()))
        netsvc.Logger().notifyChannel('webdav', netsvc.LOG_INFO, "WebDAV service registered at path: %s/ "% directory)
except Exception, e:
    logger = netsvc.Logger()
    logger.notifyChannel('webdav', netsvc.LOG_ERROR, 'Cannot launch webdav: %s' % e)

#eof

<commit_msg>doc webdav: workaround bug in KHTML's PUT method.<commit_after># -*- encoding: utf-8 -*-

#
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
#
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
###############################################################################

import netsvc
import tools
from dav_fs import openerp_dav_handler
from tools.config import config
from DAV.WebDAVServer import DAVRequestHandler
from service.websrv_lib import HTTPDir, FixSendError, HttpOptions
from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse
import urllib
import re
from string import atoi,split
from DAV.errors import *
# from DAV.constants import DAV_VERSION_1, DAV_VERSION_2

khtml_re = re.compile(r' KHTML/([0-9\.]+) ')

def OpenDAVConfig(**kw):
    class OpenDAV:
        def __init__(self, **kw):
            self.__dict__.update(**kw)

        def getboolean(self, word):
            return self.__dict__.get(word, False)

    class Config:
        DAV = OpenDAV(**kw)

    return Config()

class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler):
    verbose = False
    protocol_version = 'HTTP/1.1'
    _HTTP_OPTIONS= { 'DAV' : ['1', '2'],
                    'Allow' : [ 'GET', 'HEAD', 'COPY', 'MOVE', 'POST', 'PUT',
                            'PROPFIND', 'PROPPATCH', 'OPTIONS', 'MKCOL',
                            'DELETE', 'TRACE', 'REPORT', ]
                    }

    def get_userinfo(self,user,pw):
        return False
    def _log(self, message):
        netsvc.Logger().notifyChannel("webdav",netsvc.LOG_DEBUG,message)

    def handle(self):
        self._init_buffer()

    def finish(self):
        pass

    def get_db_from_path(self, uri):
        # interface class will handle all cases.
        res =  self.IFACE_CLASS.get_db(uri, allow_last=True)
        return res

    def setup(self):
        self.davpath = '/'+config.get_misc('webdav','vdir','webdav')
        addr, port = self.server.server_name, self.server.server_port
        server_proto = getattr(self.server,'proto', 'http').lower()
        try:
            if hasattr(self.request, 'getsockname'):
                addr, port = self.request.getsockname()
        except Exception, e:
            self.log_error("Cannot calculate own address: %s" , e)
        # Too early here to use self.headers
        self.baseuri = "%s://%s:%d/"% (server_proto, addr, port)
        self.IFACE_CLASS  = openerp_dav_handler(self, self.verbose)

    def copymove(self, CLASS):
        """ Our uri scheme removes the /webdav/ component from there, so we
        need to mangle the header, too.
        """
        dest = self.headers['Destination']
        up = urlparse.urlparse(urllib.unquote(self.headers['Destination']))
        if up.path.startswith(self.davpath):
            self.headers['Destination'] = up.path[len(self.davpath):]
        else:
            raise DAV_Forbidden("Not allowed to copy/move outside webdav path")
        DAVRequestHandler.copymove(self, CLASS)

    def get_davpath(self):
        return self.davpath

    def log_message(self, format, *args):
        netsvc.Logger().notifyChannel('webdav', netsvc.LOG_DEBUG_RPC, format % args)

    def log_error(self, format, *args):
        netsvc.Logger().notifyChannel('xmlrpc', netsvc.LOG_WARNING, format % args)

    def _prep_OPTIONS(self, opts):
        ret = opts
        dc=self.IFACE_CLASS
        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
        uri=urllib.unquote(uri)
        try:
            ret = dc.prep_http_options(uri, opts)
        except DAV_Error, (ec,dd):
            pass
        except Exception,e:
            self.log_error("Error at options: %s", str(e))
            raise
        return ret

    def send_response(self, code, message=None):
        # the BufferingHttpServer will send Connection: close , while
        # the BaseHTTPRequestHandler will only accept int code.
        # workaround both of them.
        if self.command == 'PROPFIND' and int(code) == 404:
            kh = khtml_re.search(self.headers.get('User-Agent',''))
            if kh and (kh.group(1) < '4.5'):
                # There is an ugly bug in all khtml < 4.5.x, where the 404
                # response is treated as an immediate error, which would even
                # break the flow of a subsequent PUT request. At the same time,
                # the 200 response  (rather than 207 with content) is treated
                # as "path not exist", so we send this instead
                # https://bugs.kde.org/show_bug.cgi?id=166081
                code = 200
        BaseHTTPRequestHandler.send_response(self, int(code), message)

    def send_header(self, key, value):
        if key == 'Connection' and value == 'close':
            self.close_connection = 1
        DAVRequestHandler.send_header(self, key, value)

    def send_body(self, DATA, code = None, msg = None, desc = None, ctype='application/octet-stream', headers=None):
        if headers and 'Connection' in headers:
            pass
        elif self.request_version in ('HTTP/1.0', 'HTTP/0.9'):
            pass
        elif self.close_connection == 1: # close header already sent
            pass
        else:
            if headers is None:
                headers = {}
            if self.headers.get('Connection',False) == 'Keep-Alive':
                headers['Connection'] = 'keep-alive'

        DAVRequestHandler.send_body(self, DATA, code=code, msg=msg, desc=desc,
                    ctype=ctype, headers=headers)

    def do_PUT(self):
        dc=self.IFACE_CLASS
        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
        uri=urllib.unquote(uri)
        # Handle If-Match
        if self.headers.has_key('If-Match'):
            test = False
            etag = None

            for match in self.headers['If-Match'].split(','):
                if match == '*':
                    if dc.exists(uri):
                        test = True
                        break
                else:
                    if dc.match_prop(uri, match, "DAV:", "getetag"):
                        test = True
                        break
            if not test:
                self._get_body()
                self.send_status(412)
                return

        # Handle If-None-Match
        if self.headers.has_key('If-None-Match'):
            test = True
            etag = None
            for match in self.headers['If-None-Match'].split(','):
                if match == '*':
                    if dc.exists(uri):
                        test = False
                        break
                else:
                    if dc.match_prop(uri, match, "DAV:", "getetag"):
                        test = False
                        break
            if not test:
                self._get_body()
                self.send_status(412)
                return

        # Handle expect
        expect = self.headers.get('Expect', '')
        if (expect.lower() == '100-continue' and
                self.protocol_version >= 'HTTP/1.1' and
                self.request_version >= 'HTTP/1.1'):
            self.send_status(100)
            self._flush()

        # read the body
        body=self._get_body()

        # locked resources are not allowed to be overwritten
        if self._l_isLocked(uri):
            return self.send_body(None, '423', 'Locked', 'Locked')

        ct=None
        if self.headers.has_key("Content-Type"):
            ct=self.headers['Content-Type']
        try:
            location = dc.put(uri, body, ct)
        except DAV_Error, (ec,dd):
            self._logger.warning("Cannot PUT to %s: %s", uri, dd, exc_info=True)
            return self.send_status(ec)

        headers = {}
        etag = None
        if location and isinstance(location, tuple):
            etag = location[1]
            location = location[0]
            # note that we have allowed for > 2 elems
        if location:
            headers['Location'] = location
        else:
            try:
                if not etag:
                    etag = dc.get_prop(location or uri, "DAV:", "getetag")
                if etag:
                    headers['ETag'] = str(etag)
            except Exception:
                pass

        self.send_body(None, '201', 'Created', '', headers=headers)

    def _get_body(self):
        body = None
        if self.headers.has_key("Content-Length"):
            l=self.headers['Content-Length']
            body=self.rfile.read(atoi(l))
        return body

    def do_DELETE(self):
        try:
            DAVRequestHandler.do_DELETE(self)
        except DAV_Error, (ec, dd):
            return self.send_status(ec)

from service.http_server import reg_http_service,OpenERPAuthProvider

class DAVAuthProvider(OpenERPAuthProvider):
    def authenticate(self, db, user, passwd, client_address):
        """ authenticate, but also allow the False db, meaning to skip
            authentication when no db is specified.
        """
        if db is False:
            return True
        return OpenERPAuthProvider.authenticate(self, db, user, passwd, client_address)

try:

    if (config.get_misc('webdav','enable',True)):
        directory = '/'+config.get_misc('webdav','vdir','webdav')
        handler = DAVHandler
        verbose = config.get_misc('webdav','verbose',True)
        handler.debug = config.get_misc('webdav','debug',True)
        _dc = { 'verbose' : verbose,
                'directory' : directory,
                'lockemulation' : True,
                }

        conf = OpenDAVConfig(**_dc)
        handler._config = conf
        reg_http_service(HTTPDir(directory,DAVHandler,DAVAuthProvider()))
        netsvc.Logger().notifyChannel('webdav', netsvc.LOG_INFO, "WebDAV service registered at path: %s/ "% directory)
except Exception, e:
    logger = netsvc.Logger()
    logger.notifyChannel('webdav', netsvc.LOG_ERROR, 'Cannot launch webdav: %s' % e)

#eof

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2013 OpenERP SA (<http://www.openerp.com>)
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>
#
##############################################################################

from openerp.osv import osv
from challenge import MAX_VISIBILITY_RANKING

class res_users_gamification_group(osv.Model):
    """ Update of res.users class
        - if adding groups to an user, check gamification.challenge linked to
        this group, and the user. This is done by overriding the write method.
    """
    _name = 'res.users'
    _inherit = ['res.users']

    def write(self, cr, uid, ids, vals, context=None):
        """Overwrite to autosubscribe users if added to a group marked as autojoin, user will be added to challenge"""
        write_res = super(res_users_gamification_group, self).write(cr, uid, ids, vals, context=context)
        if vals.get('groups_id'):
            # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
            user_group_ids = [command[1] for command in vals['groups_id'] if command[0] == 4]
            user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]

            challenge_obj = self.pool.get('gamification.challenge')
            challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context)
            if challenge_ids:
                challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context)
                challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context)
        return write_res

    def create(self, cr, uid, vals, context=None):
        """Overwrite to autosubscribe users if added to a group marked as autojoin, user will be added to challenge"""
        write_res = super(res_users_gamification_group, self).create(cr, uid, vals, context=context)
        if vals.get('groups_id'):
            # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
            user_group_ids = [command[1] for command in vals['groups_id'] if command[0] == 4]
            user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]

            challenge_obj = self.pool.get('gamification.challenge')
            challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context)
            if challenge_ids:
                challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, write_res)]}, context=context)
                challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context)
        return write_res

    # def get_goals_todo_info(self, cr, uid, context=None):

    def get_serialised_gamification_summary(self, cr, uid, context=None):
        return self._serialised_goals_summary(cr, uid, user_id=uid, context=context)

    def _serialised_goals_summary(self, cr, uid, user_id, context=None):
        """Return a serialised list of goals assigned to the user, grouped by challenge

        [
            {
                'id': <gamification.challenge id>,
                'name': <gamification.challenge name>,
                'visibility_mode': <visibility {ranking,personal}>,
                'currency': <res.currency id>,
                'lines': [(see gamification_challenge._get_serialized_challenge_lines() format)]
            },
        ]
        """
        all_goals_info = []
        challenge_obj = self.pool.get('gamification.challenge')

        user = self.browse(cr, uid, uid, context=context)
        challenge_ids = challenge_obj.search(cr, uid, [('user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
        for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context):
            # serialize goals info to be able to use it in javascript
            lines = challenge_obj._get_serialized_challenge_lines(cr, uid, challenge, user_id, restrict_top=MAX_VISIBILITY_RANKING, context=context)
            if lines:
                all_goals_info.append({
                    'id': challenge.id,
                    'name': challenge.name,
                    'visibility_mode': challenge.visibility_mode,
                    'currency': user.company_id.currency_id.id,
                    'lines': lines,
                })

        return all_goals_info

    def get_challenge_suggestions(self, cr, uid, context=None):
        """Return the list of challenges suggested to the user"""
        challenge_info = []
        challenge_obj = self.pool.get('gamification.challenge')
        challenge_ids = challenge_obj.search(cr, uid, [('invited_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
        for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context):
            values = {
                'id': challenge.id,
                'name': challenge.name,
                'description': challenge.description,
            }
            challenge_info.append(values)
        return challenge_info

class res_groups_gamification_group(osv.Model):
    """ Update of res.groups class
        - if adding users from a group, check gamification.challenge linked to
        this group, and the user. This is done by overriding the write method.
    """
    _name = 'res.groups'
    _inherit = 'res.groups'

    # No need to overwrite create as very unlikely to be the value in the autojoin_group_id field
    def write(self, cr, uid, ids, vals, context=None):
        """Overwrite to autosubscribe users if add users to a group marked as autojoin, these will be added to the challenge"""
        write_res = super(res_groups_gamification_group, self).write(cr, uid, ids, vals, context=context)
        if vals.get('users'):
            # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
            user_ids = [command[1] for command in vals['users'] if command[0] == 4]
            user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]]

            challenge_obj = self.pool.get('gamification.challenge')
            challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', ids)], context=context)
            if challenge_ids:
                challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context)
                challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context)
        return write_res
<commit_msg>[FIX] gamification: when auto-subscribing users to challenges, do it as superuser<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2013 OpenERP SA (<http://www.openerp.com>)
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>
#
##############################################################################

from openerp import SUPERUSER_ID
from openerp.osv import osv
from challenge import MAX_VISIBILITY_RANKING

class res_users_gamification_group(osv.Model):
    """ Update of res.users class
        - if adding groups to an user, check gamification.challenge linked to
        this group, and the user. This is done by overriding the write method.
    """
    _name = 'res.users'
    _inherit = ['res.users']

    def write(self, cr, uid, ids, vals, context=None):
        """Overwrite to autosubscribe users if added to a group marked as autojoin, user will be added to challenge"""
        write_res = super(res_users_gamification_group, self).write(cr, uid, ids, vals, context=context)
        if vals.get('groups_id'):
            # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
            user_group_ids = [command[1] for command in vals['groups_id'] if command[0] == 4]
            user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]

            challenge_obj = self.pool.get('gamification.challenge')
            challenge_ids = challenge_obj.search(cr, SUPERUSER_ID, [('autojoin_group_id', 'in', user_group_ids)], context=context)
            if challenge_ids:
                challenge_obj.write(cr, SUPERUSER_ID, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context)
                challenge_obj.generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
        return write_res

    def create(self, cr, uid, vals, context=None):
        """Overwrite to autosubscribe users if added to a group marked as autojoin, user will be added to challenge"""
        write_res = super(res_users_gamification_group, self).create(cr, uid, vals, context=context)
        if vals.get('groups_id'):
            # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
            user_group_ids = [command[1] for command in vals['groups_id'] if command[0] == 4]
            user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]

            challenge_obj = self.pool.get('gamification.challenge')
            challenge_ids = challenge_obj.search(cr, SUPERUSER_ID, [('autojoin_group_id', 'in', user_group_ids)], context=context)
            if challenge_ids:
                challenge_obj.write(cr, SUPERUSER_ID, challenge_ids, {'user_ids': [(4, write_res)]}, context=context)
                challenge_obj.generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
        return write_res

    # def get_goals_todo_info(self, cr, uid, context=None):

    def get_serialised_gamification_summary(self, cr, uid, context=None):
        return self._serialised_goals_summary(cr, uid, user_id=uid, context=context)

    def _serialised_goals_summary(self, cr, uid, user_id, context=None):
        """Return a serialised list of goals assigned to the user, grouped by challenge

        [
            {
                'id': <gamification.challenge id>,
                'name': <gamification.challenge name>,
                'visibility_mode': <visibility {ranking,personal}>,
                'currency': <res.currency id>,
                'lines': [(see gamification_challenge._get_serialized_challenge_lines() format)]
            },
        ]
        """
        all_goals_info = []
        challenge_obj = self.pool.get('gamification.challenge')

        user = self.browse(cr, uid, uid, context=context)
        challenge_ids = challenge_obj.search(cr, uid, [('user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
        for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context):
            # serialize goals info to be able to use it in javascript
            lines = challenge_obj._get_serialized_challenge_lines(cr, uid, challenge, user_id, restrict_top=MAX_VISIBILITY_RANKING, context=context)
            if lines:
                all_goals_info.append({
                    'id': challenge.id,
                    'name': challenge.name,
                    'visibility_mode': challenge.visibility_mode,
                    'currency': user.company_id.currency_id.id,
                    'lines': lines,
                })

        return all_goals_info

    def get_challenge_suggestions(self, cr, uid, context=None):
        """Return the list of challenges suggested to the user"""
        challenge_info = []
        challenge_obj = self.pool.get('gamification.challenge')
        challenge_ids = challenge_obj.search(cr, uid, [('invited_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
        for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context):
            values = {
                'id': challenge.id,
                'name': challenge.name,
                'description': challenge.description,
            }
            challenge_info.append(values)
        return challenge_info

class res_groups_gamification_group(osv.Model):
    """ Update of res.groups class
        - if adding users from a group, check gamification.challenge linked to
        this group, and the user. This is done by overriding the write method.
    """
    _name = 'res.groups'
    _inherit = 'res.groups'

    # No need to overwrite create as very unlikely to be the value in the autojoin_group_id field
    def write(self, cr, uid, ids, vals, context=None):
        """Overwrite to autosubscribe users if add users to a group marked as autojoin, these will be added to the challenge"""
        write_res = super(res_groups_gamification_group, self).write(cr, uid, ids, vals, context=context)
        if vals.get('users'):
            # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
            user_ids = [command[1] for command in vals['users'] if command[0] == 4]
            user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]]

            challenge_obj = self.pool.get('gamification.challenge')
            challenge_ids = challenge_obj.search(cr, SUPERUSER_ID, [('autojoin_group_id', 'in', ids)], context=context)
            if challenge_ids:
                challenge_obj.write(cr, SUPERUSER_ID, challenge_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context)
                challenge_obj.generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
        return write_res
<|endoftext|>


<commit_before>#!/usr/bin/python -u

# $Id$

from fcntl import fcntl,F_SETFL
from select import select
from socket import socket,has_ipv6,SHUT_RDWR,AF_INET,AF_INET6,SOCK_STREAM,IPPROTO_TCP,SOL_SOCKET,SO_REUSEADDR,error as socket_error
from os import O_NONBLOCK,WNOHANG,fork,waitpid,getpid,getppid
from sys import argv,exit

# this is the limit of how many times we try to do a reconnect
# actually the OS also tries to do reconnects, by default it
# will try 3 times per one our try
limit = 10

if len(argv)!=5:
  print "Usage: "+argv[0]+" listening_addr listening_port conn_to_addr conn_to_port"
  exit()

phost = argv[1]
pport = int(argv[2])
dhost  = argv[3]
dport  = int(argv[4])

def plog(msg, childpid = 0):

  if childpid == 0:
    print "["+str(mainpid)+"] "+str(msg)
  else:
    print "["+str(mainpid)+"->"+str(childpid)+"] "+str(msg)

##### main crap

mainpid = getpid()

s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind((phost, pport))
s.listen(1)
s.setblocking(False)
plog("bound to socket - "+str(phost)+":"+str(pport))

plog("listening for connections")

try:
  while 1:
    accept,[],[] = select([s],[],[],30);

    if s in accept:
      inc,addr = s.accept()

      try:
        waitpid(0,WNOHANG)
      except OSError:
        pass

      pid = fork()

      if pid == 0:

        chpid = getpid()

        out = socket(AF_INET, SOCK_STREAM)
        try:
          out.connect((dhost, dport))
        except socket_error:
          out.close()
          inc.close()
          exit()

        plog("connected to "+str(dhost)+":"+str(dport)+" from "+str(addr[0])+":"+str(addr[1]),chpid)

        fcntl(out, F_SETFL, O_NONBLOCK)

        plog("going into exchange between "+str(dhost)+":"+str(dport)+" and "+str(addr[0])+":"+str(addr[1]),chpid)

        while 1:
          toread,[],[] = select([out,inc],[],[],30)
          [],towrite,[] = select([],[inc],[],30)

          if inc in toread:
            data = inc.recv(4096)
            if len(data) == 0:
              inc.shutdown(SHUT_RDWR)
              break
            else:
              # try very hard to send this data ;)
              ok=0
              tries=0
              while ok==0 or tries<=limit:
                try:
                  out.send(data)
                  ok=1
                except:
                  pass
                tries+=1
                if ok==0: # if sending fails, lets try to reconnect
                  out = socket(AF_INET, SOCK_STREAM)
                  try:
                    out.connect((dhost, dport))
                  except:
                    pass
              if ok==0:
                plog("could not reconnect in "+str(limit)+" tries",chpid)
                exit()

          elif out in toread:
            data = out.recv(4096)
            if len(data) == 0:
              try:
                out.shutdown(SHUT_RDWR)
              except:
                pass
            else:
              inc.send(data)
        exit()
      else:
        plog("child forked "+str(pid))
except KeyboardInterrupt:
  plog("Ctrl+C pressed, exiting")
<commit_msg>tcpkeeper.py: few more corrections<commit_after>#!/usr/bin/python -u

# $Id$

from fcntl import fcntl,F_SETFL
from select import select
from socket import socket,has_ipv6,SHUT_RDWR,AF_INET,AF_INET6,SOCK_STREAM,IPPROTO_TCP,SOL_SOCKET,SO_REUSEADDR,error as socket_error
from os import O_NONBLOCK,WNOHANG,fork,waitpid,getpid,getppid
from sys import argv,exit

# this is the limit of how many times we try to do a reconnect
# actually the OS also tries to do reconnects, by default it
# will try 3 times per one our try
limit = 10

if len(argv)!=5:
  print "Usage: "+argv[0]+" listening_addr listening_port conn_to_addr conn_to_port"
  exit()

phost = argv[1]
pport = int(argv[2])
dhost  = argv[3]
dport  = int(argv[4])

def plog(msg, childpid = 0):

  if childpid == 0:
    print "["+str(mainpid)+"] "+str(msg)
  else:
    print "["+str(mainpid)+"->"+str(childpid)+"] "+str(msg)

##### main crap

mainpid = getpid()

s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind((phost, pport))
s.listen(1)
s.setblocking(False)
plog("bound to socket - "+str(phost)+":"+str(pport))

plog("listening for connections")

try:
  while 1:
    accept,[],[] = select([s],[],[],30);

    if s in accept:
      inc,addr = s.accept()

      try:
        waitpid(0,WNOHANG)
      except OSError:
        pass

      pid = fork()

      if pid == 0:

        chpid = getpid()

        out = socket(AF_INET, SOCK_STREAM)
        try:
          out.connect((dhost, dport))
        except socket_error:
          out.close()
          inc.close()
          exit()

        plog("connected to "+str(dhost)+":"+str(dport)+" from "+str(addr[0])+":"+str(addr[1]),chpid)

        fcntl(out, F_SETFL, O_NONBLOCK)

        plog("going into exchange between "+str(dhost)+":"+str(dport)+" and "+str(addr[0])+":"+str(addr[1]),chpid)

        while 1:
          toread,[],[] = select([out,inc],[],[],30)
          [],towrite,[] = select([],[inc],[],30)

          if inc in toread:
            data = inc.recv(4096)
            if len(data) == 0:
              inc.shutdown(SHUT_RDWR)
              break
            else:
              # try very hard to send this data ;)
              ok=0
              tries=0
              while ok==0 and tries<=limit:
                try:
                  out.send(data)
                  ok=1
                except:
                  pass
                tries+=1
                if ok==0: # if sending fails, lets try to reconnect
                  out = socket(AF_INET, SOCK_STREAM)
                  try:
                    out.connect((dhost, dport))
                  except:
                    pass
              if ok==0:
                plog("could not reconnect in "+str(limit)+" tries",chpid)
                exit()

          elif out in toread:
            data = out.recv(4096)
            if len(data) == 0:
              try:
                out.shutdown(SHUT_RDWR)
              except:
                pass
            else:
              inc.send(data)
        exit()
      else:
        plog("child forked "+str(pid))
except KeyboardInterrupt:
  plog("Ctrl+C pressed, exiting")
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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, osv
import os
from openerp.tools.translate import _
import logging
_logger = logging.getLogger(__name__)

class wizard_multi_charts_accounts(osv.osv_memory):
    """
    Change wizard that a new account chart for a company.
        * Add option to install languages during the setup
        * Copy translations for COA, Tax, Tax Code and Fiscal Position from templates to target objects.
    """
    _inherit = 'wizard.multi.charts.accounts'

    def process_translations(self, cr, uid, langs, in_obj, in_field, in_ids, out_obj, out_ids, force_write=False, context=None):
        """
        This method copies translations values of templates into new Accounts/Taxes/Journals for languages selected

        :param cr: A database cursor
        :param uid: ID of the user currently logged in
        :param langs: List of languages to load for new records
        :param in_field: Name of the translatable field of source templates
        :param in_obj: Name of source object of templates.
        :param in_ids: List of ids of source object
        :param out_obj: Destination object for which translation is to be copied
        :param out_ids: List of ids of destination object
        :param force_write: boolean that depicts if we need to create a translation OR simply replace the actual value
            with the translation in the uid's language by doing a write (in case it's TRUE)
        :param context: usual context information. May contain the key 'lang', which is the language of the user running
            the wizard, that will be used if force_write is True

        :return: True
        """
        if context is None:
            context = {}
        src = {}
        xlat_obj = self.pool.get('ir.translation')
        #find the source from Account Template
        for x in in_obj.browse(cr, uid, in_ids):
            src.update({x.id: x.name})
        for lang in langs:
            #find the value from Translation
            value = xlat_obj._get_ids(cr, uid, in_obj._name + ',' + in_field, 'model', lang, in_ids)
            for j in range(len(in_ids)):
                in_id = in_ids[j]
                if value[in_id]:
                    if not force_write:
                        #copy Translation from Source to Destination object
                        xlat_obj.create(cr, uid, {
                          'name': out_obj._name + ',' + in_field,
                          'type': 'model',
                          'res_id': out_ids[j],
                          'lang': lang,
                          'src': src[in_id],
                          'value': value[in_id],
                    })
                    else:
                        #replace the value in the destination object only if it's the user lang
                        if context.get('lang') == lang:
                            self.pool.get(out_obj._name).write(cr, uid, out_ids[j], {in_field: value[in_id]})
                else:
                    _logger.info('Language: %s. Translation from template: there is no translation available for %s!' %(lang,  src[in_id]))#out_obj._name))
        return True

    def execute(self, cr, uid, ids, context=None):
        res = super(wizard_multi_charts_accounts, self).execute(cr, uid, ids, context=context)

        obj_multi = self.browse(cr, uid, ids[0], context=context)
        company_id = obj_multi.company_id.id

        # load languages
        langs = []
        res_lang_obj = self.pool.get('res.lang')
        installed_lang_ids = res_lang_obj.search(cr, uid, [])
        installed_langs = [x.code for x in res_lang_obj.browse(cr, uid, installed_lang_ids, context=context)]
        if obj_multi.chart_template_id.spoken_languages:
            for lang in obj_multi.chart_template_id.spoken_languages.split(';'):
                if lang not in installed_langs:
                    # the language is not installed, so we don't need to load its translations
                    continue
                else:
                    # the language was already installed, so the po files have been loaded at the installation time
                    # and now we need to copy the translations of templates to the right objects
                    langs.append(lang)
        if langs:
            # write account.account translations in the real COA
            self._process_accounts_translations(cr, uid, obj_multi, company_id, langs, 'name', context=context)

            # copy account.tax.code translations
            self._process_tax_codes_translations(cr, uid, obj_multi, company_id, langs, 'name', context=context)

            # copy account.tax translations
            self._process_taxes_translations(cr, uid, obj_multi, company_id, langs, 'name', context=context)

            # copy account.fiscal.position translations
            self._process_fiscal_pos_translations(cr, uid, obj_multi, company_id, langs, 'name', context=context)

        return res

    def _process_accounts_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None):
        obj_acc_template = self.pool.get('account.account.template')
        obj_acc = self.pool.get('account.account')
        acc_template_root_id = obj_multi.chart_template_id.account_root_id.id
        acc_root_id = obj_acc.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', None)])[0]
        in_ids = obj_acc_template.search(cr, uid, [('id', 'child_of', [acc_template_root_id])], order='id')[1:]
        out_ids = obj_acc.search(cr, uid, [('id', 'child_of', [acc_root_id])], order='id')[1:]
        return self.process_translations(cr, uid, langs, obj_acc_template, field, in_ids, obj_acc, out_ids, force_write=True, context=context)

    def _process_tax_codes_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None):
        obj_tax_code_template = self.pool.get('account.tax.code.template')
        obj_tax_code = self.pool.get('account.tax.code')
        tax_code_template_root_id = obj_multi.chart_template_id.tax_code_root_id.id
        tax_code_root_id = obj_tax_code.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', None)])[0]
        in_ids = obj_tax_code_template.search(cr, uid, [('id', 'child_of', [tax_code_template_root_id])], order='id')[1:]
        out_ids = obj_tax_code.search(cr, uid, [('id', 'child_of', [tax_code_root_id])], order='id')[1:]
        return self.process_translations(cr, uid, langs, obj_tax_code_template, field, in_ids, obj_tax_code, out_ids, force_write=False, context=context)

    def _process_taxes_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None):
        obj_tax_template = self.pool.get('account.tax.template')
        obj_tax = self.pool.get('account.tax')
        in_ids = sorted([x.id for x in obj_multi.chart_template_id.tax_template_ids])
        out_ids = obj_tax.search(cr, uid, [('company_id', '=', company_id)], order='id')
        return self.process_translations(cr, uid, langs, obj_tax_template, field, in_ids, obj_tax, out_ids, force_write=False, context=context)

    def _process_fiscal_pos_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None):
        obj_fiscal_position_template = self.pool.get('account.fiscal.position.template')
        obj_fiscal_position = self.pool.get('account.fiscal.position')
        in_ids = obj_fiscal_position_template.search(cr, uid, [('chart_template_id', '=', obj_multi.chart_template_id.id)], order='id')
        out_ids = obj_fiscal_position.search(cr, uid, [('company_id', '=', company_id)], order='id')
        return self.process_translations(cr, uid, langs, obj_fiscal_position_template, field, in_ids, obj_fiscal_position, out_ids, force_write=False, context=context)

wizard_multi_charts_accounts()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>[FIX] l10n_multilang: remove force_write attribute in process_translations When processing a translation, the module used to replace the original term by the translated value for accounts (only object with force_write=True). Now getting real multilang feature. Generates the accounts with no language to get the untranslated value<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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, osv
import os
from openerp.tools.translate import _
import logging
_logger = logging.getLogger(__name__)

class wizard_multi_charts_accounts(osv.osv_memory):
    """
    Change wizard that a new account chart for a company.
        * Add option to install languages during the setup
        * Copy translations for COA, Tax, Tax Code and Fiscal Position from templates to target objects.
    """
    _inherit = 'wizard.multi.charts.accounts'

    def process_translations(self, cr, uid, langs, in_obj, in_field, in_ids, out_obj, out_ids, force_write=False, context=None):
        """
        This method copies translations values of templates into new Accounts/Taxes/Journals for languages selected

        :param cr: A database cursor
        :param uid: ID of the user currently logged in
        :param langs: List of languages to load for new records
        :param in_field: Name of the translatable field of source templates
        :param in_obj: Name of source object of templates.
        :param in_ids: List of ids of source object
        :param out_obj: Destination object for which translation is to be copied
        :param out_ids: List of ids of destination object
        :param force_write: Deprecated as of 7.0, do not use
        :param context: usual context information. May contain the key 'lang', which is the language of the user running
            the wizard, that will be used if force_write is True

        :return: True
        """
        if context is None:
            context = {}
        src = {}
        xlat_obj = self.pool.get('ir.translation')
        #find the source from Account Template
        for x in in_obj.browse(cr, uid, in_ids):
            src.update({x.id: x.name})
        for lang in langs:
            #find the value from Translation
            value = xlat_obj._get_ids(cr, uid, in_obj._name + ',' + in_field, 'model', lang, in_ids)
            for j in range(len(in_ids)):
                in_id = in_ids[j]
                if value[in_id]:
                    #copy Translation from Source to Destination object
                    xlat_obj.create(cr, uid, {
                        'name': out_obj._name + ',' + in_field,
                        'type': 'model',
                        'res_id': out_ids[j],
                        'lang': lang,
                        'src': src[in_id],
                        'value': value[in_id],
                    })
                else:
                    _logger.info('Language: %s. Translation from template: there is no translation available for %s!' %(lang,  src[in_id]))#out_obj._name))
        return True

    def execute(self, cr, uid, ids, context=None):
        if not context:
            context = {}
        # remove the lang to get the untranslated value
        ctx = dict(context, lang=None)
        res = super(wizard_multi_charts_accounts, self).execute(cr, uid, ids, context=ctx)

        obj_multi = self.browse(cr, uid, ids[0], context=context)
        company_id = obj_multi.company_id.id

        # load languages
        langs = []
        res_lang_obj = self.pool.get('res.lang')
        installed_lang_ids = res_lang_obj.search(cr, uid, [])
        installed_langs = [x.code for x in res_lang_obj.browse(cr, uid, installed_lang_ids, context=context)]
        if obj_multi.chart_template_id.spoken_languages:
            for lang in obj_multi.chart_template_id.spoken_languages.split(';'):
                if lang not in installed_langs:
                    # the language is not installed, so we don't need to load its translations
                    continue
                else:
                    # the language was already installed, so the po files have been loaded at the installation time
                    # and now we need to copy the translations of templates to the right objects
                    langs.append(lang)
        if langs:
            # write account.account translations in the real COA
            self._process_accounts_translations(cr, uid, obj_multi, company_id, langs, 'name', context=context)

            # copy account.tax.code translations
            self._process_tax_codes_translations(cr, uid, obj_multi, company_id, langs, 'name', context=context)

            # copy account.tax translations
            self._process_taxes_translations(cr, uid, obj_multi, company_id, langs, 'name', context=context)

            # copy account.fiscal.position translations
            self._process_fiscal_pos_translations(cr, uid, obj_multi, company_id, langs, 'name', context=context)

        return res

    def _process_accounts_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None):
        obj_acc_template = self.pool.get('account.account.template')
        obj_acc = self.pool.get('account.account')
        acc_template_root_id = obj_multi.chart_template_id.account_root_id.id
        acc_root_id = obj_acc.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', None)])[0]
        in_ids = obj_acc_template.search(cr, uid, [('id', 'child_of', [acc_template_root_id])], order='id')[1:]
        out_ids = obj_acc.search(cr, uid, [('id', 'child_of', [acc_root_id])], order='id')[1:]
        return self.process_translations(cr, uid, langs, obj_acc_template, field, in_ids, obj_acc, out_ids, context=context)

    def _process_tax_codes_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None):
        obj_tax_code_template = self.pool.get('account.tax.code.template')
        obj_tax_code = self.pool.get('account.tax.code')
        tax_code_template_root_id = obj_multi.chart_template_id.tax_code_root_id.id
        tax_code_root_id = obj_tax_code.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', None)])[0]
        in_ids = obj_tax_code_template.search(cr, uid, [('id', 'child_of', [tax_code_template_root_id])], order='id')[1:]
        out_ids = obj_tax_code.search(cr, uid, [('id', 'child_of', [tax_code_root_id])], order='id')[1:]
        return self.process_translations(cr, uid, langs, obj_tax_code_template, field, in_ids, obj_tax_code, out_ids, context=context)

    def _process_taxes_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None):
        obj_tax_template = self.pool.get('account.tax.template')
        obj_tax = self.pool.get('account.tax')
        in_ids = sorted([x.id for x in obj_multi.chart_template_id.tax_template_ids])
        out_ids = obj_tax.search(cr, uid, [('company_id', '=', company_id)], order='id')
        return self.process_translations(cr, uid, langs, obj_tax_template, field, in_ids, obj_tax, out_ids, context=context)

    def _process_fiscal_pos_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None):
        obj_fiscal_position_template = self.pool.get('account.fiscal.position.template')
        obj_fiscal_position = self.pool.get('account.fiscal.position')
        in_ids = obj_fiscal_position_template.search(cr, uid, [('chart_template_id', '=', obj_multi.chart_template_id.id)], order='id')
        out_ids = obj_fiscal_position.search(cr, uid, [('company_id', '=', company_id)], order='id')
        return self.process_translations(cr, uid, langs, obj_fiscal_position_template, field, in_ids, obj_fiscal_position, out_ids, context=context)

wizard_multi_charts_accounts()

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


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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 osv import fields,osv
import tools

class report_project_task_user(osv.osv):
    _name = "report.project.task.user"
    _description = "Tasks by user and project"
    _auto = False
    _columns = {
        'name': fields.char('Task Summary', size=128, readonly=True),
        'day': fields.char('Day', size=128, readonly=True),
        'year': fields.char('Year', size=64, required=False, readonly=True),
        'user_id': fields.many2one('res.users', 'Assigned To', readonly=True),
        'date_start': fields.date('Starting Date',readonly=True),
        'no_of_days': fields.integer('# of Days', size=128, readonly=True),
        'description': fields.text('Description', readonly=True),
        'date_end': fields.date('Ending Date', readonly=True),
        'date_deadline': fields.date('Deadline', readonly=True),
        'project_id': fields.many2one('project.project', 'Project', readonly=True),
        'hours_planned': fields.float('Planned Hours', readonly=True),
        'hours_effective': fields.float('Effective Hours', readonly=True),
        'hours_delay': fields.float('Avg. Plan.-Eff.', readonly=True),
        'remaining_hours': fields.float('Remaining Hours', readonly=True),
        'progress': fields.float('Progress', readonly=True),
        'total_hours': fields.float('Total Hours', readonly=True),
        'closing_days': fields.float('Days to Close', digits=(16,2), readonly=True, group_operator="avg",
                                       help="Number of Days to close the task"),
        'opening_days': fields.float('Days to Open', digits=(16,2), readonly=True, group_operator="avg",
                                       help="Number of Days to Open the task"),
        'delay_endings_days': fields.float('Overpassed Deadline', digits=(16,2), readonly=True),
        'nbr': fields.integer('# of tasks', readonly=True),
        'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'),
('0','Very urgent')], 'Importance', readonly=True),
        'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month', readonly=True),
        'state': fields.selection([('draft', 'Draft'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')],'State', readonly=True),
        'company_id': fields.many2one('res.company', 'Company', readonly=True, groups="base.group_multi_company"),
        'partner_id': fields.many2one('res.partner', 'Partner', readonly=True),
        'type_id': fields.many2one('project.task.type', 'Stage', readonly=True),
    }
    _order = 'name desc, project_id'

    def init(self, cr):
        tools.sql.drop_view_if_exists(cr, 'report_project_task_user')
        cr.execute("""
            CREATE view report_project_task_user as
              SELECT
                    (select 1 ) AS nbr,
                    t.id as id,
                    to_char(date_start, 'YYYY') as year,
                    to_char(date_start, 'MM') as month,
                    to_char(date_start, 'YYYY-MM-DD') as day,
                    date_trunc('day',t.date_start) as date_start,
                    date_trunc('day',t.date_end) as date_end,
                    to_date(to_char(t.date_deadline, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_deadline,
--                    sum(cast(to_char(date_trunc('day',t.date_end) - date_trunc('day',t.date_start),'DD') as int)) as no_of_days,
                    (extract('epoch' from (t.date_end-t.date_start)))/(3600*24)  as no_of_days,
                    t.user_id,
                    progress as progress,
                    t.project_id,
                    t.state,
                    t.effective_hours as hours_effective,
                    t.priority,
                    t.name as name,
                    t.company_id,
                    t.partner_id,
                    t.type_id,
                    remaining_hours as remaining_hours,
                    total_hours as total_hours,
                    t.delay_hours as hours_delay,
                    planned_hours as hours_planned,
                    (extract('epoch' from (t.date_end-t.create_date)))/(3600*24)  as closing_days,
                    (extract('epoch' from (t.date_start-t.create_date)))/(3600*24)  as opening_days,
                    (extract('epoch' from (t.date_deadline-t.date_end)))/(3600*24)  as delay_endings_days
              FROM project_task t

                GROUP BY
                    t.id,
                    remaining_hours,
                    t.effective_hours,
                    progress,
                    total_hours,
                    planned_hours,
                    hours_delay,
                    year,
                    month,
                    day,
                    create_date,
                    date_start,
                    date_end,
                    date_deadline,
                    t.user_id,
                    t.project_id,
                    t.state,
                    t.priority,
                    name,
                    t.company_id,
                    t.partner_id,
                    t.type_id

        """)

report_project_task_user()

#This class is generated for project deshboard purpose
class project_vs_hours(osv.osv):
    _name = "project.vs.hours"
    _description = " Project vs  hours"
    _auto = False
    _columns = {
        'project': fields.char('Project', size=128, required=True),
        'remaining_hours': fields.float('Remaining Hours', readonly=True),
        'planned_hours': fields.float('Planned Hours', readonly=True),
        'total_hours': fields.float('Total Hours', readonly=True),
        'state': fields.selection([('draft','Draft'),('open','Open'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Close'),('template', 'Template')], 'State', required=True, readonly=True)
    }
    _order = 'project desc'

    def init(self, cr):
        tools.sql.drop_view_if_exists(cr, 'project_vs_hours')
        cr.execute("""
            CREATE or REPLACE view project_vs_hours as (
                select
                      min(pt.id) as id,
                      aaa.user_id as uid,
                      aaa.name as project,
                      aaa.state,
                      sum(pt.remaining_hours) as remaining_hours,
                      sum(pt.planned_hours) as planned_hours,
                      sum(pt.total_hours) as total_hours
                 FROM project_project as pp,
                       account_analytic_account as aaa,
                       project_task as pt
                 WHERE aaa.id=pp.analytic_account_id and pt.project_id=pp.id and pp.analytic_account_id=aaa.id
                 GROUP BY aaa.user_id,aaa.state,aaa.name
                 UNION All
                 SELECT
                      min(pt.id) as id,
                      pur.uid as uid,
                      aaa.name as project,
                      aaa.state,
                      sum(pt.remaining_hours) as remaining_hours,
                      sum(pt.planned_hours) as planned_hours,
                      sum(pt.total_hours) as total_hours
                 FROM project_project as pp,
                      project_user_rel as pur,
                      account_analytic_account as aaa,
                      project_task as pt
                 WHERE pur.project_id=pp.id and pt.project_id=pp.id and pp.analytic_account_id=aaa.id AND pur.uid != aaa.user_id
                 GROUP BY pur.uid,aaa.state,aaa.name
            )
        """)

project_vs_hours()

class task_by_days(osv.osv):
    _name = "task.by.days"
    _description = "Task By Days"
    _auto = False
    _columns = {
        'day': fields.char('Day', size=128, required=True),
        'state': fields.selection([('draft', 'Draft'),('open', 'In Progress'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True),
        'total_task': fields.float('Total tasks', readonly=True),
        'project_id':fields.many2one('project.project', 'Project')
     }
    _order = 'day desc'

    def init(self, cr):
        tools.sql.drop_view_if_exists(cr, 'task_by_days')
        cr.execute("""
            CREATE or REPLACE view task_by_days as (
                SELECT
                    min(pt.id) as id,
                    to_char(pt.create_date, 'YYYY-MM-DD') as day,
                    count(*) as total_task,
                    pt.state as state,
                    pt.project_id
                FROM
                    project_task as pt
                GROUP BY
                    to_char(pt.create_date, 'YYYY-MM-DD'),pt.state,pt.project_id
            )
        """)

task_by_days()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<commit_msg>[FIX] project: Add a group_operator flag on the progress field of the project report<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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 osv import fields,osv
import tools

class report_project_task_user(osv.osv):
    _name = "report.project.task.user"
    _description = "Tasks by user and project"
    _auto = False
    _columns = {
        'name': fields.char('Task Summary', size=128, readonly=True),
        'day': fields.char('Day', size=128, readonly=True),
        'year': fields.char('Year', size=64, required=False, readonly=True),
        'user_id': fields.many2one('res.users', 'Assigned To', readonly=True),
        'date_start': fields.date('Starting Date',readonly=True),
        'no_of_days': fields.integer('# of Days', size=128, readonly=True),
        'description': fields.text('Description', readonly=True),
        'date_end': fields.date('Ending Date', readonly=True),
        'date_deadline': fields.date('Deadline', readonly=True),
        'project_id': fields.many2one('project.project', 'Project', readonly=True),
        'hours_planned': fields.float('Planned Hours', readonly=True),
        'hours_effective': fields.float('Effective Hours', readonly=True),
        'hours_delay': fields.float('Avg. Plan.-Eff.', readonly=True),
        'remaining_hours': fields.float('Remaining Hours', readonly=True),
        'progress': fields.float('Progress', readonly=True, group_operator='avg'),
        'total_hours': fields.float('Total Hours', readonly=True),
        'closing_days': fields.float('Days to Close', digits=(16,2), readonly=True, group_operator="avg",
                                       help="Number of Days to close the task"),
        'opening_days': fields.float('Days to Open', digits=(16,2), readonly=True, group_operator="avg",
                                       help="Number of Days to Open the task"),
        'delay_endings_days': fields.float('Overpassed Deadline', digits=(16,2), readonly=True),
        'nbr': fields.integer('# of tasks', readonly=True),
        'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'),
('0','Very urgent')], 'Importance', readonly=True),
        'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month', readonly=True),
        'state': fields.selection([('draft', 'Draft'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')],'State', readonly=True),
        'company_id': fields.many2one('res.company', 'Company', readonly=True, groups="base.group_multi_company"),
        'partner_id': fields.many2one('res.partner', 'Partner', readonly=True),
        'type_id': fields.many2one('project.task.type', 'Stage', readonly=True),
    }
    _order = 'name desc, project_id'

    def init(self, cr):
        tools.sql.drop_view_if_exists(cr, 'report_project_task_user')
        cr.execute("""
            CREATE view report_project_task_user as
              SELECT
                    (select 1 ) AS nbr,
                    t.id as id,
                    to_char(date_start, 'YYYY') as year,
                    to_char(date_start, 'MM') as month,
                    to_char(date_start, 'YYYY-MM-DD') as day,
                    date_trunc('day',t.date_start) as date_start,
                    date_trunc('day',t.date_end) as date_end,
                    to_date(to_char(t.date_deadline, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_deadline,
--                    sum(cast(to_char(date_trunc('day',t.date_end) - date_trunc('day',t.date_start),'DD') as int)) as no_of_days,
                    (extract('epoch' from (t.date_end-t.date_start)))/(3600*24)  as no_of_days,
                    t.user_id,
                    progress as progress,
                    t.project_id,
                    t.state,
                    t.effective_hours as hours_effective,
                    t.priority,
                    t.name as name,
                    t.company_id,
                    t.partner_id,
                    t.type_id,
                    remaining_hours as remaining_hours,
                    total_hours as total_hours,
                    t.delay_hours as hours_delay,
                    planned_hours as hours_planned,
                    (extract('epoch' from (t.date_end-t.create_date)))/(3600*24)  as closing_days,
                    (extract('epoch' from (t.date_start-t.create_date)))/(3600*24)  as opening_days,
                    (extract('epoch' from (t.date_deadline-t.date_end)))/(3600*24)  as delay_endings_days
              FROM project_task t

                GROUP BY
                    t.id,
                    remaining_hours,
                    t.effective_hours,
                    progress,
                    total_hours,
                    planned_hours,
                    hours_delay,
                    year,
                    month,
                    day,
                    create_date,
                    date_start,
                    date_end,
                    date_deadline,
                    t.user_id,
                    t.project_id,
                    t.state,
                    t.priority,
                    name,
                    t.company_id,
                    t.partner_id,
                    t.type_id

        """)

report_project_task_user()

#This class is generated for project deshboard purpose
class project_vs_hours(osv.osv):
    _name = "project.vs.hours"
    _description = " Project vs  hours"
    _auto = False
    _columns = {
        'project': fields.char('Project', size=128, required=True),
        'remaining_hours': fields.float('Remaining Hours', readonly=True),
        'planned_hours': fields.float('Planned Hours', readonly=True),
        'total_hours': fields.float('Total Hours', readonly=True),
        'state': fields.selection([('draft','Draft'),('open','Open'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Close'),('template', 'Template')], 'State', required=True, readonly=True)
    }
    _order = 'project desc'

    def init(self, cr):
        tools.sql.drop_view_if_exists(cr, 'project_vs_hours')
        cr.execute("""
            CREATE or REPLACE view project_vs_hours as (
                select
                      min(pt.id) as id,
                      aaa.user_id as uid,
                      aaa.name as project,
                      aaa.state,
                      sum(pt.remaining_hours) as remaining_hours,
                      sum(pt.planned_hours) as planned_hours,
                      sum(pt.total_hours) as total_hours
                 FROM project_project as pp,
                       account_analytic_account as aaa,
                       project_task as pt
                 WHERE aaa.id=pp.analytic_account_id and pt.project_id=pp.id and pp.analytic_account_id=aaa.id
                 GROUP BY aaa.user_id,aaa.state,aaa.name
                 UNION All
                 SELECT
                      min(pt.id) as id,
                      pur.uid as uid,
                      aaa.name as project,
                      aaa.state,
                      sum(pt.remaining_hours) as remaining_hours,
                      sum(pt.planned_hours) as planned_hours,
                      sum(pt.total_hours) as total_hours
                 FROM project_project as pp,
                      project_user_rel as pur,
                      account_analytic_account as aaa,
                      project_task as pt
                 WHERE pur.project_id=pp.id and pt.project_id=pp.id and pp.analytic_account_id=aaa.id AND pur.uid != aaa.user_id
                 GROUP BY pur.uid,aaa.state,aaa.name
            )
        """)

project_vs_hours()

class task_by_days(osv.osv):
    _name = "task.by.days"
    _description = "Task By Days"
    _auto = False
    _columns = {
        'day': fields.char('Day', size=128, required=True),
        'state': fields.selection([('draft', 'Draft'),('open', 'In Progress'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True),
        'total_task': fields.float('Total tasks', readonly=True),
        'project_id':fields.many2one('project.project', 'Project')
     }
    _order = 'day desc'

    def init(self, cr):
        tools.sql.drop_view_if_exists(cr, 'task_by_days')
        cr.execute("""
            CREATE or REPLACE view task_by_days as (
                SELECT
                    min(pt.id) as id,
                    to_char(pt.create_date, 'YYYY-MM-DD') as day,
                    count(*) as total_task,
                    pt.state as state,
                    pt.project_id
                FROM
                    project_task as pt
                GROUP BY
                    to_char(pt.create_date, 'YYYY-MM-DD'),pt.state,pt.project_id
            )
        """)

task_by_days()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<|endoftext|>


<commit_before>import time
from collections import namedtuple
from pprint import PrettyPrinter
import inspect

class WrappedFunc(object):
    def __init__(self, func, owner):
        """
        Args:
            func(function): The function to wrap
            owner(WrappedObject): The containing instance wrapping this function
        """
        super(WrappedFunc, self).__init__()
        self._owner = owner
        self._orig_func = func
        self._is_bound = True if inspect.ismethod(func) else False
        self._wrapped_data = {
            'call_count': 0,
            'calls': []
        }
        self._return_value = None

    def __call__(self, *args, **kwargs):
        start_time = time.time()
        self._wrapped_data['last_call'] = {
            'args': args,
            'kwargs': kwargs,
        }
        self._wrapped_data['call_count'] += 1
        try:
            if self._return_value:
                self._owner._print_padded_message("Faking {} value.".format(self._orig_func.__name__))
                if callable(self._return_value):
                    result = self._return_value(*args, **kwargs)
                else:
                    result = self._return_value
            else:
                # If the function is method and the WrappedObject is set to burrow deep...
                if self._is_bound and self._owner._burrow_deep:
                    # Use the WrappedObject as the value of "self" rather than the
                    # encapsulated object the method is bound to
                    result = self._orig_func.im_func(self._owner, *args, **kwargs)
                else:
                    # Otherwise, call the function regularly
                    result = self._orig_func(*args, **kwargs)
            self._wrapped_data['last_call']['return_value'] = result
            execution_time = (time.time() - start_time)
        except Exception as e:
            self._wrapped_data['exception'] = e
            execution_time = (time.time() - start_time)
        finally:
            if 'result' not in locals() and 'e' in locals():
                result = e
            call_info = {
                'args': args,
                'kwargs': kwargs,
                'result': result,
                'execution_time': execution_time
            }

            self._owner._wrapped_calls[self._orig_func.__name__].append(call_info)
            self._wrapped_data['calls'].append(call_info)
            self._owner._access_log.append(
                "->{0}() called. For details see {0}._wrapped_data".format(self._orig_func.__name__)
            )
            if 'e' in locals():
                self._owner._print_padded_message('Exception stored. Call get_wrapper_info() for info.')
                raise e
            else:
                self._owner._print_padded_message(
                    'Call to {} finished in {} seconds. \nCall print_wrapper_info() for more info.'.format(
                        self._orig_func.__name__,
                        execution_time))
            return result

    def fake_return_value(self, value):
        self._return_value = value

    def reset_return_value(self):
        self._return_value = None

    def get_call_args(self):
        return inspect.getcallargs(self._orig_func)

    def print_call_data(self):
        printer = PrettyPrinter(indent=2)
        print("-" * 80)
        print("Call data for " + self._orig_func.__name__ + ":")
        print("Access call data on _wrapped_data.")
        print('\n')
        cleaned_data = []
        for index, call in enumerate(self._wrapped_data['calls']):
            call_data = {}
            for key, val in call.iteritems():
                newVal = val if len(str(val)) < 100 \
                    else "VALUE TOO LONG. See _wrapped_data[{}]['{}'] for value.".format(index, key)
                call_data[key] = newVal
        printer.pprint(cleaned_data)
        print('-' * 80)

class AttributeDescriptor(object):
    def __init__(self, name, obj):
        super(AttributeDescriptor, self).__init__()
        self.__name = name
        self.__obj = obj

    def log(self, message, instance):
        instance._access_log.append(
            '--' + message
        )

    def __get__(self, instance, owner):
        return self.__obj

    def __set__(self, instance, value):
        new_val_str = (str(value) if len(str(value)) < 100 else str(value)[:75] + '...')
        old_val_str = (str(self.__obj) if len(str(self.__obj)) < 100 else str(self.__obj)[:75] + '...')
        self.log("{} set to value: {}. Previous value was: {}".format(
            self.__name, new_val_str, old_val_str, instance), instance)
        self.__obj = value

    def __delete__(self, instance):
        self.log(self.__name + ' deleted.', instance)
        del self.__obj

class WrappedObject(object):
    def __init__(self, wrapped, burrow_deep=False):
        super(WrappedObject, self).__init__()
        self._wrapped_calls = {}
        self._access_log = []
        self._burrow_deep = burrow_deep
        self.__class__ = type('Wrapped_' + type(wrapped).__name__, (WrappedObject,), {})

        for attr_name in dir(wrapped):
            if not attr_name.startswith('__'):
                attr = getattr(wrapped, attr_name)
                if callable(attr):
                    self._wrapped_calls[attr_name] = []
                    setattr(self, attr_name, WrappedFunc(attr, self))
                elif attr_name not in ['_access_log', '_wrapped_calls']:
                    self._access_log.append(
                        attr_name +
                        ' initialized with value: '
                        + (str(attr) if len(str(attr)) < 100 else "VALUE TOO LONG.")
                    )
                    setattr(self.__class__, attr_name, AttributeDescriptor(attr_name, attr))
                else:
                    setattr(self, attr_name, attr)
        self._access_log.append(('-' * 25) + 'INSTANTIATION COMPLETE' + ('-' * 25))
        self._print_padded_message(type(wrapped).__name__ + " wrapped.", closed=not self._burrow_deep)
        if self._burrow_deep:
            self._print_padded_message("Currently burrowing deep (self is exchanged and all calls will be logged.)" +
                                       "\nCall burrow_deep(False) to disable.", opened=False, closed=False)
            self._access_log.append('-' * 80)

    def print_wrapper_info(self):
        print('*' * 80)
        print('Wrapper info: ')
        print('\nMethod Calls:')
        printer = PrettyPrinter(indent=2)
        dict_to_report = {}
        for method, calls in self._wrapped_calls.iteritems():
            dict_to_report[method] = []
            if not calls:
                continue
            for index, call in enumerate(calls):
                dict_to_report[method].append([])
                dict_to_report[method][index] = {}
                for key, val in call.iteritems():
                    new_val = val if len(str(
                        val)) < 100 else "VALUE TOO LONG. See _wrapped_calls['{method}'][{index}]['{item}'] for actual data.".format(
                        method=method, index=index, item=key)
                    dict_to_report[method][index][key] = new_val
        printer.pprint(dict_to_report)
        print('\nAccess log:')
        for l in self._access_log:
            if ('\n' in l):
                for line in l.split('\n'):
                    print ('\t' + line)
            else:
                print('\t' + l)
        print('\nCall data can be accessed from self._wrapped_calls')
        print('Last call information can be found on the _wrapped_data attribute on individual methods.')
        print('*' * 80)

    def _print_padded_message(self, message, closed=True, opened=True):
        self._access_log.append(message.split('\n')[0])
        if opened:
            message = ('*' * 80) + '\n' + message
        if closed:
            message += '\n' + ('*' * 80)
        print(message)

    def clear_log(self):
        del self._access_log[:]

    def burrow_deep(self, verbose=True):
        self._burrow_deep = verbose

def jwrap(cls_or_obj, burrow_deep=False, *args, **kwargs):
    if inspect.isclass(cls_or_obj):
        new_object = cls_or_obj(*args, **kwargs)
    else:
        new_object = cls_or_obj
    return WrappedObject(new_object, burrow_deep)
<commit_msg>Enhancements<commit_after>from __future__ import print_function
import time
from collections import namedtuple
from pprint import PrettyPrinter
import inspect

class Printer(object):
    _printing_progress = False
    _printer = PrettyPrinter(indent=1)

    @classmethod
    def stop_printing_progress(cls):
        if cls._printing_progress:
            print('\n')
            cls._printing_progress = False

    @classmethod
    def print_progress(cls):
        cls._printing_progress = True
        print('.', end='')

    @classmethod
    def print_padded_message(cls, message, closed=True, opened=True):
        cls.stop_printing_progress()
        to_log = message.split('\n')[0]
        print((('*' * 80) + '\n' if opened else "") + message + ('\n' + ('*' * 80) if closed else ""))
        return to_log

    @classmethod
    def print_message(cls, message):
        cls.stop_printing_progress()
        cls._printer.pprint(message)

class WrappedFunc(object):
    def __init__(self, func, owner):
        """
        Args:
            func(function): The function to wrap
            owner(WrappedObject): The containing instance wrapping this function
        """
        super(WrappedFunc, self).__init__()
        self._owner = owner
        self._orig_func = func
        self._is_bound = True if inspect.ismethod(func) else False
        self._wrapped_data = {
            'call_count': 0,
            'calls': []
        }
        self._return_value = None
        self._alert = False

    def __call__(self, *args, **kwargs):
        Printer.print_progress()
        if self._alert:
            Printer.print_padded_message("!!! {} was called.".format(self._orig_func.__name__))
        start_time = time.time()
        self._wrapped_data['last_call'] = {
            'args': args,
            'kwargs': kwargs,
        }
        self._wrapped_data['call_count'] += 1
        try:
            if self._return_value:
                self._owner._access_log.append(
                    Printer.print_padded_message("Faking {} value.".format(self._orig_func.__name__)))
                if callable(self._return_value):
                    result = self._return_value(*args, **kwargs)
                else:
                    result = self._return_value
            else:
                # If the function is method and the WrappedObject is set to burrow deep...
                if self._is_bound and self._owner._burrow_deep:
                    # Use the WrappedObject as the value of "self" rather than the
                    # encapsulated object the method is bound to
                    result = self._orig_func.im_func(self._owner, *args, **kwargs)
                else:
                    # Otherwise, call the function regularly
                    result = self._orig_func(*args, **kwargs)
            self._wrapped_data['last_call']['return_value'] = result
            execution_time = (time.time() - start_time)
        except Exception as e:
            self._wrapped_data['exception'] = e
            execution_time = (time.time() - start_time)
        finally:
            if 'result' not in locals() and 'e' in locals():
                result = e
            call_info = {
                'args': args,
                'kwargs': kwargs,
                'result': result,
                'execution_time': execution_time
            }

            self._owner._wrapped_calls[self._orig_func.__name__].append(call_info)
            self._wrapped_data['calls'].append(call_info)
            self._owner._access_log.append(
                "-> {0}() called. For details see {0}._wrapped_data".format(self._orig_func.__name__)
            )
            if 'e' in locals():
                self._owner._access_log.append(
                    Printer.print_padded_message('Exception stored. Call get_wrapper_info() for info.'))
                raise e
            else:
                self._owner._access_log.append(Printer.print_padded_message(
                    '\tCall to {} finished in {} seconds. \nCall print_wrapper_info() for more info.'.format(
                        self._orig_func.__name__,
                        execution_time)))
            return result

    def fake_return_value(self, value):
        self._return_value = value

    def reset_return_value(self):
        self._return_value = None

    def get_call_args(self):
        return inspect.getcallargs(self._orig_func)

    def print_call_data(self):
        Printer.print_message("-" * 80)
        Printer.print_message("Call data for " + self._orig_func.__name__ + ":")
        Printer.print_message("Access call data on _wrapped_data.")
        Printer.print_message('\n')
        cleaned_data = []
        for index, call in enumerate(self._wrapped_data['calls']):
            call_data = {}
            for key, val in call.iteritems():
                newVal = val if len(str(val)) < 100 \
                    else "VALUE TOO LONG. See _wrapped_data[{}]['{}'] for value.".format(index, key)
                call_data[key] = newVal
            cleaned_data.append(call_data)
        Printer.print_message(cleaned_data)
        Printer.print_message('-' * 80)

    def set_alert(self, on=True):
        self._alert = on

class WrappedAttribute(object):
    def __init__(self, name, obj):
        super(WrappedAttribute, self).__init__()
        self.__name = name
        self.__obj = obj

    def log(self, message, instance):
        instance._access_log.append(
            '-- ' + message
        )

    def __get__(self, instance, owner):
        Printer.print_progress()
        return self.__obj

    def __set__(self, instance, value):
        Printer.print_progress()
        new_val_str = (str(value) if len(str(value)) < 100 else str(value)[:75] + '...')
        old_val_str = (str(self.__obj) if len(str(self.__obj)) < 100 else str(self.__obj)[:75] + '...')
        self.log("{} set to value: {}. Previous value was: {}".format(
            self.__name, new_val_str, old_val_str, instance), instance)
        self.__obj = value

    def __delete__(self, instance):
        Printer.print_progress()
        self.log(self.__name + ' deleted.', instance)
        del self.__obj

class WrappedObject(object):
    def __init__(self, wrapped, burrow_deep=False):
        super(WrappedObject, self).__init__()
        self._wrapped_calls = {}
        self._access_log = []
        self._burrow_deep = burrow_deep
        self.__class__ = type('Wrapped_' + type(wrapped).__name__, (WrappedObject,), {})

        for attr_name in dir(wrapped):
            if not attr_name.startswith('__'):
                attr = getattr(wrapped, attr_name)
                if callable(attr):
                    self._wrapped_calls[attr_name] = []
                    setattr(self, attr_name, WrappedFunc(attr, self))
                elif attr_name not in ['_access_log', '_wrapped_calls']:
                    self._access_log.append(
                        attr_name +
                        ' initialized with value: '
                        + (str(attr) if len(str(attr)) < 100 else "VALUE TOO LONG.")
                    )
                    setattr(self.__class__, attr_name, WrappedAttribute(attr_name, attr))
                else:
                    setattr(self, attr_name, attr)
        self._access_log.append(('-' * 25) + 'INSTANTIATION COMPLETE' + ('-' * 25))
        self._access_log.append(
            Printer.print_padded_message(type(wrapped).__name__ + " wrapped.", closed=not self._burrow_deep))
        if self._burrow_deep:
            self._access_log.append(Printer.print_padded_message(
                "Currently burrowing deep (self is exchanged and all calls will be logged.)" +
                '\nCall burrow_deep(False) to disable.', opened=False, closed=False))
            self._access_log.append('-' * 80)

    def print_wrapper_info(self):
        Printer.print_message('*' * 80)
        Printer.print_message('Wrapper info: ')
        Printer.print('Method Calls:')
        printer = PrettyPrinter(indent=2)
        dict_to_report = {}
        for method, calls in self._wrapped_calls.iteritems():
            if not calls:
                continue
            dict_to_report[method] = []
            for index, call in enumerate(calls):
                dict_to_report[method].append([])
                dict_to_report[method][index] = {}
                for key, val in call.iteritems():
                    new_val = val if len(str(
                        val)) < 100 else "VALUE TOO LONG. See _wrapped_calls['{method}'][{index}]['{item}'] for actual data.".format(
                        method=method, index=index, item=key)
                    dict_to_report[method][index][key] = new_val
        Printer.print_message(dict_to_report)
        Printer.print_message('\nAccess log:')
        for l in self._access_log:
            if ('\n' in l):
                for line in l.split('\n'):
                    print('\t' + line)
            else:
                print('\t' + l)
        print('\nCall data can be accessed from self._wrapped_calls')
        print(
            'Last call information can be found on the _wrapped_data attribute on individual methods.')
        print('*' * 80)

    def clear_log(self):
        del self._access_log[:]

    def burrow_deep(self, activated=True):
        self._burrow_deep = activated

def jwrap(cls_or_obj, burrow_deep=False, *args, **kwargs):
    if inspect.isclass(cls_or_obj):
        new_object = cls_or_obj(*args, **kwargs)
    else:
        new_object = cls_or_obj
    return WrappedObject(new_object, burrow_deep)
<|endoftext|>