<commit_before>import weakref
import threading
from base64 import b64encode, b64decode
from idiokit.xmlcore import Element

from . import rules

class NameAlreadyRegistered(Exception):
    pass

class UnregisteredType(Exception):
    pass

class UnregisteredName(Exception):
    pass

class Serializer(object):
    def __init__(self, register_common=True):
        self._lock = threading.Lock()
        self._types = []
        self._names = dict()
        self._cache = weakref.WeakKeyDictionary()

        if register_common:
            self.register("d", Dict(dict))
            self.register("l", List(list, tuple, set, frozenset))
            self.register("i", Int(int, long))
            self.register("f", Float(float))
            self.register("n", Nil(type(None)))
            self.register("s", Str(unicode))
            self.register("b", Bytes(str))
            self.register("t", Bool(bool))

            self.register("ra", Rule(rules.And))
            self.register("ro", Rule(rules.Or))
            self.register("rn", Rule(rules.No))
            self.register("rm", Rule(rules.Match))
            self.register("rc", Rule(rules.NonMatch))
            self.register("rf", Rule(rules.Fuzzy))

            self.register("rx", Rule(rules.RegExp))
            self.register("rs", Rule(rules.String))
            self.register("ri", Rule(rules.IP))

    def register(self, name, serializer):
        with self._lock:
            if name in self._names:
                raise NameAlreadyRegistered(name)

            self._types.append((name, serializer))
            self._names[name] = serializer
            self._cache.clear()

    def _find_serializer(self, obj):
        obj_type = type(obj)

        with self._lock:
            if obj_type not in self._cache:
                info = None
                for name, serializer in reversed(self._types):
                    if serializer.isinstance(obj):
                        info = name, serializer
                        break
                self._cache[obj_type] = info

        info = self._cache[obj_type]
        if info is None:
            raise UnregisteredType(obj_type)

        return info

    def dump(self, obj):
        name, serializer = self._find_serializer(obj)
        return serializer.dump(obj, name, self)

    def normalize(self, obj):
        _, serializer = self._find_serializer(obj)
        return serializer.normalize(obj, self)

    def load(self, element):
        name = element.name

        with self._lock:
            if name not in self._names:
                raise UnregisteredName(name)
            serializer = self._names[name]

        return serializer.load(element, self)

class SubSerializer(object):
    def __init__(self, *types):
        self._types = types

    def isinstance(self, obj):
        return isinstance(obj, self._types)

    def dump(self, obj, name, context):
        raise NotImplementedError()

    def load(self, obj, context):
        raise NotImplementedError()

    def normalize(self, obj, context):
        return self.load(self.dump(obj, context), context)

class List(SubSerializer):
    def dump(self, obj, name, context):
        element = Element(name)
        for item in obj:
            element.add(context.dump(item))
        return element

    def load(self, element, context):
        return tuple(load(item) for item in element.children())

    def normalize(self, obj, context):
        return tuple(context.normalize(x) for x in obj)

class Dict(SubSerializer):
    _list = List()

    def _from_dict(self, obj):
        dumped = []
        for key, value in obj.iteritems():
            dumped.append(key)
            dumped.append(value)
        return dumped

    def _to_dict(self, obj):
        loaded = {}
        for index in xrange(0, len(obj), 2):
            key = obj[index]
            value = obj[index + 1]
            loaded[key] = value
        return loaded

    def dump(self, obj, name, context):
        return self._list.dump(self._from_dict(obj), name, context)

    def load(self, element, context):
        return self._to_dict(self._list.load(element, context))

    def normalize(self, obj):
        return self._to_dict(self._list.normalize(self._from_dict(obj)))

class Int(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=unicode(obj))

    def load(self, element, context):
        return int(element.text)

    def normalize(self, obj, context):
        return obj

class Float(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=repr(obj))

    def load(self, element, context):
        return float(element.text)

    def normalize(self, obj, context):
        return obj

class Nil(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name)

    def load(self, element, context):
        return None

    def normalize(self, obj, context):
        return None

class Str(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=b64encode(obj.encode("utf-8")))

    def load(self, element, context):
        return b64decode(element.text).decode("utf-8")

    def normalize(self, obj, context):
        return obj

class Bytes(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=b64encode(obj))

    def load(self, element, context):
        return b64decode(element.text)

    def normalize(self, obj, context):
        return obj

class Bool(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=unicode(int(bool(obj))))

    def load(self, element, context):
        return bool(int(element.text))

    def normalize(self, obj, context):
        return obj

class Rule(SubSerializer):
    def __init__(self, rule_type):
        SubSerializer.__init__(self, rule_type)

        self._rule_type = rule_type

    def dump(self, obj, name, context):
        element = Element(name)
        element.add(context.dump(obj.dump()))
        return element

    def load(self, element, context):
        for child in element.children():
            return self._rule_type.load(context.load(child))
        raise ValueError("element has no child elements")

    def normalize(self, obj, context):
        return obj.load(context.normalize(obj.dump()))

global_serializer = Serializer()
register = global_serializer.register
dump = global_serializer.dump
load = global_serializer.load
normalize = global_serializer.normalize
<commit_msg>abusehelper.core.serialize: Fix for dict normalization.<commit_after>import weakref
import threading
from base64 import b64encode, b64decode
from idiokit.xmlcore import Element

from . import rules

class NameAlreadyRegistered(Exception):
    pass

class UnregisteredType(Exception):
    pass

class UnregisteredName(Exception):
    pass

class Serializer(object):
    def __init__(self, register_common=True):
        self._lock = threading.Lock()
        self._types = []
        self._names = dict()
        self._cache = weakref.WeakKeyDictionary()

        if register_common:
            self.register("d", Dict(dict))
            self.register("l", List(list, tuple, set, frozenset))
            self.register("i", Int(int, long))
            self.register("f", Float(float))
            self.register("n", Nil(type(None)))
            self.register("s", Str(unicode))
            self.register("b", Bytes(str))
            self.register("t", Bool(bool))

            self.register("ra", Rule(rules.And))
            self.register("ro", Rule(rules.Or))
            self.register("rn", Rule(rules.No))
            self.register("rm", Rule(rules.Match))
            self.register("rc", Rule(rules.NonMatch))
            self.register("rf", Rule(rules.Fuzzy))

            self.register("rx", Rule(rules.RegExp))
            self.register("rs", Rule(rules.String))
            self.register("ri", Rule(rules.IP))

    def register(self, name, serializer):
        with self._lock:
            if name in self._names:
                raise NameAlreadyRegistered(name)

            self._types.append((name, serializer))
            self._names[name] = serializer
            self._cache.clear()

    def _find_serializer(self, obj):
        obj_type = type(obj)

        with self._lock:
            if obj_type not in self._cache:
                info = None
                for name, serializer in reversed(self._types):
                    if serializer.isinstance(obj):
                        info = name, serializer
                        break
                self._cache[obj_type] = info

        info = self._cache[obj_type]
        if info is None:
            raise UnregisteredType(obj_type)

        return info

    def dump(self, obj):
        name, serializer = self._find_serializer(obj)
        return serializer.dump(obj, name, self)

    def normalize(self, obj):
        _, serializer = self._find_serializer(obj)
        return serializer.normalize(obj, self)

    def load(self, element):
        name = element.name

        with self._lock:
            if name not in self._names:
                raise UnregisteredName(name)
            serializer = self._names[name]

        return serializer.load(element, self)

class SubSerializer(object):
    def __init__(self, *types):
        self._types = types

    def isinstance(self, obj):
        return isinstance(obj, self._types)

    def dump(self, obj, name, context):
        raise NotImplementedError()

    def load(self, obj, context):
        raise NotImplementedError()

    def normalize(self, obj, context):
        return self.load(self.dump(obj, context), context)

class List(SubSerializer):
    def dump(self, obj, name, context):
        element = Element(name)
        for item in obj:
            element.add(context.dump(item))
        return element

    def load(self, element, context):
        return tuple(load(item) for item in element.children())

    def normalize(self, obj, context):
        return tuple(context.normalize(x) for x in obj)

class Dict(SubSerializer):
    _list = List()

    def _from_dict(self, obj):
        dumped = []
        for key, value in obj.iteritems():
            dumped.append(key)
            dumped.append(value)
        return dumped

    def _to_dict(self, obj):
        loaded = {}
        for index in xrange(0, len(obj), 2):
            key = obj[index]
            value = obj[index + 1]
            loaded[key] = value
        return loaded

    def dump(self, obj, name, context):
        return self._list.dump(self._from_dict(obj), name, context)

    def load(self, element, context):
        return self._to_dict(self._list.load(element, context))

    def normalize(self, obj, context):
        return self._to_dict(self._list.normalize(self._from_dict(obj), context))

class Int(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=unicode(obj))

    def load(self, element, context):
        return int(element.text)

    def normalize(self, obj, context):
        return obj

class Float(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=repr(obj))

    def load(self, element, context):
        return float(element.text)

    def normalize(self, obj, context):
        return obj

class Nil(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name)

    def load(self, element, context):
        return None

    def normalize(self, obj, context):
        return None

class Str(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=b64encode(obj.encode("utf-8")))

    def load(self, element, context):
        return b64decode(element.text).decode("utf-8")

    def normalize(self, obj, context):
        return obj

class Bytes(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=b64encode(obj))

    def load(self, element, context):
        return b64decode(element.text)

    def normalize(self, obj, context):
        return obj

class Bool(SubSerializer):
    def dump(self, obj, name, context):
        return Element(name, _text=unicode(int(bool(obj))))

    def load(self, element, context):
        return bool(int(element.text))

    def normalize(self, obj, context):
        return obj

class Rule(SubSerializer):
    def __init__(self, rule_type):
        SubSerializer.__init__(self, rule_type)

        self._rule_type = rule_type

    def dump(self, obj, name, context):
        element = Element(name)
        element.add(context.dump(obj.dump()))
        return element

    def load(self, element, context):
        for child in element.children():
            return self._rule_type.load(context.load(child))
        raise ValueError("element has no child elements")

    def normalize(self, obj, context):
        return obj.load(context.normalize(obj.dump()))

global_serializer = Serializer()
register = global_serializer.register
dump = global_serializer.dump
load = global_serializer.load
normalize = global_serializer.normalize
<|endoftext|>


<commit_before>import unittest
import os, shutil
from os.path import abspath

class ContextApiTests(unittest.TestCase):

    def setUp(self):
        self.dbpath = os.path.expanduser(os.path.join('~','provenance_test.json'))
        if os.path.exists(self.dbpath):
            os.remove(self.dbpath)
        os.mkdir('temp')
        from niprov import Context
        self.provenance = Context()
        self.provenance.config.database_type = 'file'
        self.provenance.config.database_url = self.dbpath

    def tearDown(self):
        shutil.rmtree('temp')

    def touch(self, path):
        with open(path,'w') as tempfile:
            tempfile.write('0')

    def test_Log(self):
        self.provenance.discover('testdata')
        newfile = 'temp/smoothed.test'
        self.touch(newfile)
        parent = os.path.abspath('testdata/eeg/stub.cnt')
        self.provenance.log(newfile, 'test', parent)
        testfpath = os.path.abspath(newfile)
        img = self.provenance.get(forFile=testfpath)
        self.assertEqual(img.provenance['subject'], 'Jane Doe')
        self.assertEqual(img.provenance['size'], os.path.getsize(newfile))

    def test_Record_with_user(self):
        self.provenance.discover('testdata')
        newfile = 'temp/recorded.test'
        self.touch(newfile)
        parent = os.path.abspath('testdata/eeg/stub.cnt')
        self.provenance.record('echo hallo', newfile, parent, user='007')
        testfpath = os.path.abspath(newfile)
        img = self.provenance.get(forFile=testfpath)
        self.assertEqual(img.provenance['user'], '007')

    def test_Export_Import(self):
        from niprov.exceptions import UnknownFileError
        self.provenance.discover('testdata')
        discoveredFile = os.path.abspath('testdata/eeg/stub.cnt')
        self.assertIsNotNone(self.provenance.get(forFile=discoveredFile))
        backupFilepath = self.provenance.backup()
        os.remove(self.dbpath) # get rid of existing data.
        with self.assertRaises(UnknownFileError):
            self.provenance.get(forFile=discoveredFile)
        self.provenance.importp(backupFilepath)
        self.assertIsNotNone(self.provenance.get(forFile=discoveredFile))

    @unittest.skip("Doesn't work on Travis right now.")
    def test_Attach_provenance_string_in_file_based_on_config(self):
        import nibabel
        self.provenance.config.attach = True
        newfile = os.path.abspath('temp/fileX.nii.gz')
        shutil.copy('testdata/nifti/fieldmap.nii.gz', newfile)
        self.provenance.add(newfile)
        img = nibabel.load(newfile)
        self.assertEqual(img.get_header().extensions.count('comment'), 1)
        self.assertEqual(img.get_header().extensions[0].get_code(), 6)
        content = img.get_header().extensions[0].get_content()
        self.assertIn('"path": "{0}"'.format(newfile), content)

    def test_Approval(self):
        self.provenance.discover('testdata')
        self.provenance.markForApproval([os.path.abspath('testdata/dicom/T1.dcm'),
                                os.path.abspath('testdata/dicom/T2.dcm')])
        imgs = self.provenance.markedForApproval()
        self.provenance.approve(os.path.abspath('testdata/dicom/T1.dcm'))
        imgs = self.provenance.markedForApproval()

    def test_Comparison(self):
        # Given two PARREC images' provenance records
        par1 = self.provenance.add(abspath('testdata/parrec/T1.PAR'))[0]
        par2 = self.provenance.add(abspath('testdata/parrec/T2.PAR'))[0]
        # Comparing them returns a Diff object with methods testing equality
        self.assertFalse(self.provenance.compare(par1, par2).areEqual())
        # Compare() can also be called as a method on the objects themselves,
        # and the Diff object has assert..() methods that raise AssertionErrors
        msgRegExp = ".*echo-time.*2\.08.*80\.0.*"
        with self.assertRaisesRegexp(AssertionError, msgRegExp):
            par1.compare(par2).assertEqualProtocol()

if __name__ == '__main__':
    unittest.main()

<commit_msg>Added acceptance test for log() with relative paths.<commit_after>import unittest
import os, shutil
from os.path import abspath

class ContextApiTests(unittest.TestCase):

    def setUp(self):
        self.dbpath = os.path.expanduser(os.path.join('~','provenance_test.json'))
        if os.path.exists(self.dbpath):
            os.remove(self.dbpath)
        os.mkdir('temp')
        from niprov import Context
        self.provenance = Context()
        self.provenance.config.database_type = 'file'
        self.provenance.config.database_url = self.dbpath

    def tearDown(self):
        shutil.rmtree('temp')

    def touch(self, path):
        with open(path,'w') as tempfile:
            tempfile.write('0')

    def test_Relative_paths(self):
        self.provenance.discover('testdata')
        newfile = 'temp/smoothed.test'
        self.touch(newfile)
        self.provenance.log(newfile, 'test', 'testdata/eeg/stub.cnt')
        img = self.provenance.get(forFile=newfile)

    def test_Log(self):
        self.provenance.discover('testdata')
        newfile = 'temp/smoothed.test'
        self.touch(newfile)
        parent = os.path.abspath('testdata/eeg/stub.cnt')
        self.provenance.log(newfile, 'test', parent)
        testfpath = os.path.abspath(newfile)
        img = self.provenance.get(forFile=testfpath)
        self.assertEqual(img.provenance['subject'], 'Jane Doe')
        self.assertEqual(img.provenance['size'], os.path.getsize(newfile))

    def test_Record_with_user(self):
        self.provenance.discover('testdata')
        newfile = 'temp/recorded.test'
        self.touch(newfile)
        parent = os.path.abspath('testdata/eeg/stub.cnt')
        self.provenance.record('echo hallo', newfile, parent, user='007')
        testfpath = os.path.abspath(newfile)
        img = self.provenance.get(forFile=testfpath)
        self.assertEqual(img.provenance['user'], '007')

    def test_Export_Import(self):
        from niprov.exceptions import UnknownFileError
        self.provenance.discover('testdata')
        discoveredFile = os.path.abspath('testdata/eeg/stub.cnt')
        self.assertIsNotNone(self.provenance.get(forFile=discoveredFile))
        backupFilepath = self.provenance.backup()
        os.remove(self.dbpath) # get rid of existing data.
        with self.assertRaises(UnknownFileError):
            self.provenance.get(forFile=discoveredFile)
        self.provenance.importp(backupFilepath)
        self.assertIsNotNone(self.provenance.get(forFile=discoveredFile))

    @unittest.skip("Doesn't work on Travis right now.")
    def test_Attach_provenance_string_in_file_based_on_config(self):
        import nibabel
        self.provenance.config.attach = True
        newfile = os.path.abspath('temp/fileX.nii.gz')
        shutil.copy('testdata/nifti/fieldmap.nii.gz', newfile)
        self.provenance.add(newfile)
        img = nibabel.load(newfile)
        self.assertEqual(img.get_header().extensions.count('comment'), 1)
        self.assertEqual(img.get_header().extensions[0].get_code(), 6)
        content = img.get_header().extensions[0].get_content()
        self.assertIn('"path": "{0}"'.format(newfile), content)

    def test_Approval(self):
        self.provenance.discover('testdata')
        self.provenance.markForApproval([os.path.abspath('testdata/dicom/T1.dcm'),
                                os.path.abspath('testdata/dicom/T2.dcm')])
        imgs = self.provenance.markedForApproval()
        self.provenance.approve(os.path.abspath('testdata/dicom/T1.dcm'))
        imgs = self.provenance.markedForApproval()

    def test_Comparison(self):
        # Given two PARREC images' provenance records
        par1 = self.provenance.add(abspath('testdata/parrec/T1.PAR'))[0]
        par2 = self.provenance.add(abspath('testdata/parrec/T2.PAR'))[0]
        # Comparing them returns a Diff object with methods testing equality
        self.assertFalse(self.provenance.compare(par1, par2).areEqual())
        # Compare() can also be called as a method on the objects themselves,
        # and the Diff object has assert..() methods that raise AssertionErrors
        msgRegExp = ".*echo-time.*2\.08.*80\.0.*"
        with self.assertRaisesRegexp(AssertionError, msgRegExp):
            par1.compare(par2).assertEqualProtocol()

if __name__ == '__main__':
    unittest.main()

<|endoftext|>


<commit_before># Copyright 2014,  Jeff Buttars,  A10 Networks.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
from __future__ import absolute_import
from __future__ import unicode_literals
import six

from acos_client.v30 import base
from acos_client.v30.slb.port import Port

class Server(base.BaseV30):

    url_prefix = '/slb/server/'

    def get(self, name, **kwargs):
        return self._get(self.url_prefix + name, **kwargs)

    def _set(self, name, ip_address, status=1, server_templates=None, **kwargs):
        params = {
            "server": {
                "name": name,
                "action": 'enable' if status else 'disable',
                "conn-resume": kwargs.get("conn_resume"),
                "conn-limit": kwargs.get("conn_limit"),
                "health-check": kwargs.get("health_check")
            }
        }

        if self._is_ipv6(ip_address):
            params['server']['server-ipv6-addr'] = ip_address
        else:
            params['server']['host'] = ip_address

        if server_templates:
            server_templates = {k: v for k, v in server_templates.items() if v}
            params['server']['template-server'] = server_templates.get('template-server')

        config_defaults = kwargs.get("config_defaults")

        if config_defaults:
            for k, v in six.iteritems(config_defaults):
                params['server'][k] = v

        return params

    def create(self, name, ip_address, status=1, server_templates=None, **kwargs):
        params = self._set(name, ip_address, status=status,
                           server_templates=server_templates, **kwargs)
        return self._post(self.url_prefix, params, **kwargs)

    def update(self, name, ip_address, status=1, server_templates=None, **kwargs):
        params = self._set(name, ip_address, status=status,
                           server_templates=server_templates, **kwargs)
        return self._post(self.url_prefix + name, params, **kwargs)

    def replace(self, name, ip_address, status=1, server_templates=None, **kwargs):
        params = self._set(name, ip_address, status=status,
                           server_templates=server_templates, **kwargs)
        return self._put(self.url_prefix + name, params, **kwargs)

    def delete(self, name):
        return self._delete(self.url_prefix + name)

    @property
    def port(self):
        return Port(self.client)
<commit_msg>Added port-list refrence object attribute to methods for servers<commit_after># Copyright 2014,  Jeff Buttars,  A10 Networks.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
from __future__ import absolute_import
from __future__ import unicode_literals
import six

from acos_client.v30 import base
from acos_client.v30.slb.port import Port

class Server(base.BaseV30):

    url_prefix = '/slb/server/'

    def get(self, name, **kwargs):
        return self._get(self.url_prefix + name, **kwargs)

    def _set(self, name, ip_address, status=1, server_templates=None, port_list=None, **kwargs):
        params = {
            "server": {
                "name": name,
                "action": 'enable' if status else 'disable',
                "conn-resume": kwargs.get("conn_resume"),
                "conn-limit": kwargs.get("conn_limit"),
                "health-check": kwargs.get("health_check"),
            }
        }

        if port_list:
            params['server']['port-list'] = port_list

        if self._is_ipv6(ip_address):
            params['server']['server-ipv6-addr'] = ip_address
        else:
            params['server']['host'] = ip_address

        if server_templates:
            server_templates = {k: v for k, v in server_templates.items() if v}
            params['server']['template-server'] = server_templates.get('template-server')

        config_defaults = kwargs.get("config_defaults")

        if config_defaults:
            for k, v in six.iteritems(config_defaults):
                params['server'][k] = v

        return params

    def create(self, name, ip_address, status=1, server_templates=None,
               port_list=None, **kwargs):
        params = self._set(name, ip_address, status=status, port_list=port_list,
                           server_templates=server_templates, **kwargs)
        return self._post(self.url_prefix, params, **kwargs)

    def update(self, name, ip_address, status=1, server_templates=None,
               port_list=None, **kwargs):
        params = self._set(name, ip_address, status=status, port_list=port_list,
                           server_templates=server_templates, **kwargs)
        return self._post(self.url_prefix + name, params, **kwargs)

    def replace(self, name, ip_address, status=1, server_templates=None,
                port_list=None, **kwargs):
        params = self._set(name, ip_address, status=status, port_list=port_list,
                           server_templates=server_templates, **kwargs)
        return self._put(self.url_prefix + name, params, **kwargs)

    def delete(self, name):
        return self._delete(self.url_prefix + name)

    @property
    def port(self):
        return Port(self.client)
<|endoftext|>


<commit_before>import os
import sys
import json
from ansible import constants as C
from ansible.plugins.action import ActionBase
from ansible.errors import AnsibleError

try:
    from __main__ import display
except ImportError:
    from ansible.utils.display import Display
    display = Display()

# pkg_manager: command=get_info config={{packages_file}} host={{hostname_id}}
class ActionModule(ActionBase):
    # COLOR_HIGHLIGHT = 'white'
    # COLOR_VERBOSE = 'blue'
    # COLOR_WARN = 'bright purple'
    # COLOR_ERROR = 'red'
    # COLOR_DEBUG = 'dark gray'
    # COLOR_DEPRECATE = 'purple'
    # COLOR_SKIP = 'cyan'
    # COLOR_UNREACHABLE = 'bright red'
    # COLOR_OK = 'green'
    # COLOR_CHANGED = 'yellow'
    def _install_pkg(self, pkg_name):
        module_args = {"command": "install", "name": pkg_name}
        module_return = self._execute_module(module_name="pkg_manager",
                                             module_args=module_args)

        if not module_return.get("failed"):
            display.display(module_return.get("msg"), color=C.COLOR_WARN)

        return module_return

    def _get_command(self):
        command = self._task.args.get("command", None)
        if command is None or command.strip() == "":
            raise AnsibleError("Not found required param 'command'.")

        return command.strip()

    def _get_host(self, command):
        host = self._task.args.get("host", None)
        if host is None or host.strip() == "":
            raise AnsibleError("Not found required param 'host' for command '{}'.".format(command))

        return host.strip()

    def _get_config_value(self, command, host):
        config = self._task.args.get("config", None)
        if config is None or config.strip() == "":
            raise AnsibleError("Not found required param 'config' for command '{}'.".format(command))

        config = config.strip()
        if not os.path.exists(config):
            raise AnsibleError("Config file '{}' not found".format(config))

        gvars = {"host": host}
        try:
            if sys.version_info[0] == 3:
                exec(open(config).read(), gvars)
            else:
                execfile(config, gvars)

            packages = [it.strip() for it in gvars["packages"]]
            groups = [it.strip() for it in gvars["groups"]]
            return {"packages": packages, "groups": groups}
        except:
            e = sys.exc_info()[1]
            raise AnsibleError(e)

    def _print_section(self, text, values):
        if len(values) == 0:
            return

        display.display("{}:".format(text), color=C.COLOR_UNREACHABLE)
        for name in values:
            display.display(" {}".format(name), color=C.COLOR_VERBOSE)

    def _get_info(self, packages, groups):
        module_args = {"command": "get_info", "packages": packages, "groups": groups}
        module_return = self._execute_module(module_name="pkg_manager",
                                             module_args=module_args)
        if module_return.get("failed"):
            raise AnsibleError("Module 'pkg_manager' finished with error: {}.".format(module_return.get("msg")))

        self._print_section("group, name wrong", module_return.get("groups_name_wrong"))
        self._print_section("package, name wrong", module_return.get("packages_name_wrong"))
        self._print_section("package, not installed", module_return.get("packages_not_installed"))
        self._print_section("package, not explicit", module_return.get("packages_not_explicit"))
        self._print_section("package, new", module_return.get("packages_new"))
        self._print_section("package, aur", module_return.get("packages_aur"))
        self._print_section("package, in group", module_return.get("packages_in_group"))
        self._print_section("package, not installed in group", module_return.get("packages_not_installed_in_group"))

    def run(self, tmp=None, task_vars=None):
        result = super(ActionModule, self).run(tmp, task_vars)

        command = self._get_command()
        if command == "get_info":
            host = self._get_host(command)
            config = self._get_config_value(command, host)
            self._get_info(config["packages"], config["groups"])
            result['changed'] = False
        else:
            raise AnsibleError("Param 'command' has unexpected value '{}'.".format(command))

        return result
         # module_args['command'] = 'install'
         # module_args['name'] = "asd"
         # display.display("in params: {}".format(json.dumps(module_args)), color=C.COLOR_WARN)
         # module_return = self._install_pkg("pkg1")
         # display.display('err' if module_return.get('failed') else 'success', color=C.COLOR_WARN)

         # return module_return
<commit_msg>refactoring action_plugins/pkg_manager.py<commit_after>import os
from traceback import format_exc
from ansible import constants as C
from ansible.plugins.action import ActionBase

try:
    from __main__ import display
except ImportError:
    from ansible.utils.display import Display
    display = Display()

class StrError(RuntimeError):
    pass

# pkg_manager: command=get_info config={{packages_file}} host={{hostname_id}}
class ActionModule(ActionBase):
    # COLOR_HIGHLIGHT = 'white'
    # COLOR_VERBOSE = 'blue'
    # COLOR_WARN = 'bright purple'
    # COLOR_ERROR = 'red'
    # COLOR_DEBUG = 'dark gray'
    # COLOR_DEPRECATE = 'purple'
    # COLOR_SKIP = 'cyan'
    # COLOR_UNREACHABLE = 'bright red'
    # COLOR_OK = 'green'
    # COLOR_CHANGED = 'yellow'
    # def _install_pkg(self, pkg_name):
    #     module_args = {"command": "install", "name": pkg_name}
    #     module_return = self._execute_module(module_name="pkg_manager",
    #                                          module_args=module_args)

    #     if not module_return.get("failed"):
    #         display.display(module_return.get("msg"), color=C.COLOR_WARN)

    #     return module_return

    def _get_param_command(self):
        command = self._task.args.get("command", None)
        if command is None or command.strip() == "":
            raise StrError("Not found required param 'command'.")

        return command.strip()

    def _get_param_host(self, command):
        host = self._task.args.get("host", None)
        if host is None or host.strip() == "":
            raise StrError("Not found required param 'host' for command '{}'.".format(command))

        return host.strip()

    def _get_param_config_value(self, command):
        config = self._task.args.get("config", None)
        if config is None or config.strip() == "":
            raise StrError("Not found required param 'config' for command '{}'.".format(command))

        config = config.strip()
        if not os.path.exists(config):
            raise StrError("Config file '{}' not found".format(config))

        gvars = {"host": self._get_param_host(command)}
        exec(open(config).read(), gvars)
        packages = [it.strip() for it in gvars["packages"]]
        groups = [it.strip() for it in gvars["groups"]]
        return {"packages": packages, "groups": groups}

    @staticmethod
    def _print_section(text, values):
        if len(values) == 0:
            return

        display.display("{}:".format(text), color=C.COLOR_UNREACHABLE)
        for name in values:
            display.display(" {}".format(name), color=C.COLOR_VERBOSE)

    @staticmethod
    def _print_exception(text):
        display.error(text, wrap_text=False)

    def _call_module(self, name, args):
        result = self._execute_module(module_name=name, module_args=args)
        if result.get("failed"):
            exception = result.get("exception", None)
            if exception is not None:
                display.error(exception, wrap_text=False)

            raise StrError(result.get("msg", "Unknown error in module {}".format(name)))

        return result

    def _get_info(self, packages, groups):
        args = {"command": "get_info", "packages": packages, "groups": groups}
        result = self._call_module(name="pkg_manager", args=args)

        ActionModule._print_section("group, name wrong", result.get("groups_name_wrong"))
        ActionModule._print_section("package, name wrong", result.get("packages_name_wrong"))
        ActionModule._print_section("package, not installed", result.get("packages_not_installed"))
        ActionModule._print_section("package, not explicit", result.get("packages_not_explicit"))
        ActionModule._print_section("package, new", result.get("packages_new"))
        ActionModule._print_section("package, aur", result.get("packages_aur"))
        ActionModule._print_section("package, in group", result.get("packages_in_group"))
        ActionModule._print_section("package, not installed in group", result.get("packages_not_installed_in_group"))

        return result

    def _run(self):
        command = self._get_param_command()
        if command == "get_info":
            config = self._get_param_config_value(command)
            return self._get_info(config["packages"], config["groups"])
        else:
            raise StrError("Param 'command' has unexpected value '{}'.".format(command))

    def run(self, tmp=None, task_vars=None):
        result = super(ActionModule, self).run(tmp, task_vars)
        try:
            result.update(self._run())
        except StrError as e:
            result['failed'] = True
            result['msg'] = "Error in action_plugin/pkg_manager" + str(e)
        except Exception:
            ActionModule._print_exception(format_exc())
            result['failed'] = True
            result['msg'] = "Error in action_plugin/pkg_manager"

        return result
<|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

class res_partner(osv.osv):

    """ Inherits partner and adds Tasks information in the partner form """
    _inherit = 'res.partner'
    _columns = {
        'task_ids': fields.one2many('project.task', 'partner_id', 'Tasks'),
    }

res_partner()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>[FIX] project - do not copy task_ids of res.partner<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

class res_partner(osv.osv):

    """ Inherits partner and adds Tasks information in the partner form """
    _inherit = 'res.partner'
    _columns = {
        'task_ids': fields.one2many('project.task', 'partner_id', 'Tasks'),
    }

    def copy(self, cr, uid, record_id, default=None, context=None):
        if default is None:
            default = {}

        default.update({'task_ids': []})
        return super(res_partner, self).copy(cr, uid, record_id, default, context)

res_partner()

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


<commit_before>#!/usr/bin/python
from __future__ import with_statement

import functools
import logging
import os
import pprint
import sys
import traceback
import uuid
import xmlrpclib

import simplejson
import werkzeug.datastructures
import werkzeug.exceptions
import werkzeug.utils
import werkzeug.wrappers
import werkzeug.wsgi

import ast
import nonliterals
import http
import session
import openerplib

__all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller',
           'WebRequest', 'JsonRequest', 'HttpRequest']

_logger = logging.getLogger(__name__)

#-----------------------------------------------------------
# Globals (wont move into a pool)
#-----------------------------------------------------------

addons_module = {}
addons_manifest = {}
controllers_class = {}
controllers_object = {}
controllers_path = {}

#----------------------------------------------------------
# OpenERP Web RequestHandler
#----------------------------------------------------------
class WebRequest(object):
    """ Parent class for all OpenERP Web request types, mostly deals with
    initialization and setup of the request object (the dispatching itself has
    to be handled by the subclasses)

    :param request: a wrapped werkzeug Request object
    :type request: :class:`werkzeug.wrappers.BaseRequest`
    :param config: configuration object

    .. attribute:: httprequest

        the original :class:`werkzeug.wrappers.Request` object provided to the
        request

    .. attribute:: httpsession

        a :class:`~collections.Mapping` holding the HTTP session data for the
        current http session

    .. attribute:: config

        config parameter provided to the request object

    .. attribute:: params

        :class:`~collections.Mapping` of request parameters, not generally
        useful as they're provided directly to the handler method as keyword
        arguments

    .. attribute:: session_id

        opaque identifier for the :class:`session.OpenERPSession` instance of
        the current request

    .. attribute:: session

        :class:`~session.OpenERPSession` instance for the current request

    .. attribute:: context

        :class:`~collections.Mapping` of context values for the current request

    .. attribute:: debug

        ``bool``, indicates whether the debug mode is active on the client
    """
    def __init__(self, request, config):
        self.httprequest = request
        self.httpresponse = None
        self.httpsession = request.session
        self.config = config

    def init(self, params):
        self.params = dict(params)
        # OpenERP session setup
        self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
        self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession())
        self.session.config = self.config
        self.context = self.params.pop('context', None)
        self.debug = self.params.pop('debug', False) != False

class JsonRequest(WebRequest):
    """ JSON-RPC2 over HTTP.

    Sucessful request::

      --> {"jsonrpc": "2.0",
           "method": "call",
           "params": {"session_id": "SID",
                      "context": {},
                      "arg1": "val1" },
           "id": null}

      <-- {"jsonrpc": "2.0",
           "result": { "res1": "val1" },
           "id": null}

    Request producing a error::

      --> {"jsonrpc": "2.0",
           "method": "call",
           "params": {"session_id": "SID",
                      "context": {},
                      "arg1": "val1" },
           "id": null}

      <-- {"jsonrpc": "2.0",
           "error": {"code": 1,
                     "message": "End user error message.",
                     "data": {"code": "codestring",
                              "debug": "traceback" } },
           "id": null}

    """

    def dispatch(self, controller, method, requestf=None, request=None):
        """ Calls the method asked for by the JSON-RPC2 request

        :param controller: the instance of the controller which received the request
        :param method: the method which received the request
        :param requestf: a file-like object containing an encoded JSON-RPC2 request
        :param request: a JSON-RPC2 request

        :returns: an utf8 encoded JSON-RPC2 reply
        """
        response = {"jsonrpc": "2.0" }
        error = None
        try:
            # Read POST content or POST Form Data named "request"
            if requestf:
                self.jsonrequest = simplejson.load(requestf, object_hook=nonliterals.non_literal_decoder)
            else:
                self.jsonrequest = simplejson.loads(request, object_hook=nonliterals.non_literal_decoder)
            self.init(self.jsonrequest.get("params", {}))
            if _logger.isEnabledFor(logging.DEBUG):
                _logger.debug("--> %s.%s\n%s", controller.__class__.__name__, method.__name__, pprint.pformat(self.jsonrequest))
            response['id'] = self.jsonrequest.get('id')
            response["result"] = method(controller, self, **self.params)
        except openerplib.AuthenticationError:
            error = {
                'code': 100,
                'message': "OpenERP Session Invalid",
                'data': {
                    'type': 'session_invalid',
                    'debug': traceback.format_exc()
                }
            }
        except xmlrpclib.Fault, e:
            error = {
                'code': 200,
                'message': "OpenERP Server Error",
                'data': {
                    'type': 'server_exception',
                    'fault_code': e.faultCode,
                    'debug': "Client %s\nServer %s" % (
                    "".join(traceback.format_exception("", None, sys.exc_traceback)), e.faultString)
                }
            }
        except Exception:
            logging.getLogger(__name__ + '.JSONRequest.dispatch').exception\
                ("An error occured while handling a json request")
            error = {
                'code': 300,
                'message': "OpenERP WebClient Error",
                'data': {
                    'type': 'client_exception',
                    'debug': "Client %s" % traceback.format_exc()
                }
            }
        if error:
            response["error"] = error

        if _logger.isEnabledFor(logging.DEBUG):
            _logger.debug("<--\n%s", pprint.pformat(response))
        content = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder)
        return werkzeug.wrappers.Response(
            content, headers=[('Content-Type', 'application/json'),
                              ('Content-Length', len(content))])

def jsonrequest(f):
    """ Decorator marking the decorated method as being a handler for a
    JSON-RPC request (the exact request path is specified via the
    ``$(Controller._cp_path)/$methodname`` combination.

    If the method is called, it will be provided with a :class:`JsonRequest`
    instance and all ``params`` sent during the JSON-RPC request, apart from
    the ``session_id``, ``context`` and ``debug`` keys (which are stripped out
    beforehand)
    """
    @functools.wraps(f)
    def json_handler(controller, request, config):
        return JsonRequest(request, config).dispatch(
            controller, f, requestf=request.stream)
    json_handler.exposed = True
    return json_handler

class HttpRequest(WebRequest):
    """ Regular GET/POST request
    """
    def dispatch(self, controller, method):
        params = dict(self.httprequest.args)
        params.update(self.httprequest.form)
        params.update(self.httprequest.files)
        self.init(params)
        akw = {}
        for key, value in self.httprequest.args.iteritems():
            if isinstance(value, basestring) and len(value) < 1024:
                akw[key] = value
            else:
                akw[key] = type(value)
        _logger.debug("%s --> %s.%s %r", self.httprequest.method, controller.__class__.__name__, method.__name__, akw)
        r = method(controller, self, **self.params)
        if self.debug or 1:
            if isinstance(r, werkzeug.wrappers.BaseResponse):
                _logger.debug('<-- %s', r)
            else:
                _logger.debug("<-- size: %s", len(r))
        return r

    def make_response(self, data, headers=None, cookies=None):
        """ Helper for non-HTML responses, or HTML responses with custom
        response headers or cookies.

        While handlers can just return the HTML markup of a page they want to
        send as a string if non-HTML data is returned they need to create a
        complete response object, or the returned data will not be correctly
        interpreted by the clients.

        :param basestring data: response body
        :param headers: HTTP headers to set on the response
        :type headers: ``[(name, value)]``
        :param collections.Mapping cookies: cookies to set on the client
        """
        response = werkzeug.wrappers.Response(data, headers=headers)
        if cookies:
            for k, v in cookies.iteritems():
                response.set_cookie(k, v)
        return response

    def not_found(self, description=None):
        """ Helper for 404 response, return its result from the method
        """
        return werkzeug.exceptions.NotFound(description)

def httprequest(f):
    """ Decorator marking the decorated method as being a handler for a
    normal HTTP request (the exact request path is specified via the
    ``$(Controller._cp_path)/$methodname`` combination.

    If the method is called, it will be provided with a :class:`HttpRequest`
    instance and all ``params`` sent during the request (``GET`` and ``POST``
    merged in the same dictionary), apart from the ``session_id``, ``context``
    and ``debug`` keys (which are stripped out beforehand)
    """
    @functools.wraps(f)
    def http_handler(controller, request, config):
        return HttpRequest(request, config).dispatch(controller, f)
    http_handler.exposed = True
    return http_handler

class ControllerType(type):
    def __init__(cls, name, bases, attrs):
        super(ControllerType, cls).__init__(name, bases, attrs)
        controllers_class["%s.%s" % (cls.__module__, cls.__name__)] = cls

class Controller(object):
    __metaclass__ = ControllerType

class Root(object):
    """Root WSGI application for the OpenERP Web Client.

    :param options: mandatory initialization options object, must provide
                    the following attributes:

                    ``server_host`` (``str``)
                      hostname of the OpenERP server to dispatch RPC to
                    ``server_port`` (``int``)
                      RPC port of the OpenERP server
                    ``serve_static`` (``bool | None``)
                      whether this application should serve the various
                      addons's static files
                    ``storage_path`` (``str``)
                      filesystem path where HTTP session data will be stored
                    ``dbfilter`` (``str``)
                      only used in case the list of databases is requested
                      by the server, will be filtered by this pattern
    """
    def __init__(self, options):
        self.root = '/web/webclient/home?debug=1'
        self.config = options

        if self.config.backend == 'local':
            conn = openerplib.get_connector(protocol='local')
        else:
            conn = openerplib.get_connector(hostname=self.config.server_host,
                   port=self.config.server_port)
        self.config.connector = conn

        self.session_cookie = 'sessionid'
        self.addons = {}

        static_dirs = self._load_addons()
        if options.serve_static:
            self.dispatch = werkzeug.wsgi.SharedDataMiddleware(
                self.dispatch, static_dirs)

        if options.session_storage:
            if not os.path.exists(options.session_storage):
                os.mkdir(options.session_storage, 0700)
            self.session_storage = options.session_storage

    def __call__(self, environ, start_response):
        """ Handle a WSGI request
        """
        return self.dispatch(environ, start_response)

    def dispatch(self, environ, start_response):
        """
        Performs the actual WSGI dispatching for the application, may be
        wrapped during the initialization of the object.

        Call the object directly.
        """
        request = werkzeug.wrappers.Request(environ)
        request.parameter_storage_class = werkzeug.datastructures.ImmutableDict

        if request.path == '/':
            return werkzeug.utils.redirect(self.root, 301)(environ, start_response)
        elif request.path == '/mobile':
            return werkzeug.utils.redirect(
                '/web_mobile/static/src/web_mobile.html', 301)(environ, start_response)

        handler = self.find_handler(*(request.path.split('/')[1:]))

        if not handler:
            response = werkzeug.exceptions.NotFound()
        else:
            with http.session(request, self.session_storage, self.session_cookie) as session:
                result = handler(
                    request, self.config)

                if isinstance(result, basestring):
                    response = werkzeug.wrappers.Response(
                        result, headers=[('Content-Type', 'text/html; charset=utf-8'),
                                         ('Content-Length', len(result))])
                else:
                    response = result

                response.set_cookie(self.session_cookie, session.sid)

        return response(environ, start_response)

    def _load_addons(self):
        """
        Loads all addons at the specified addons path, returns a mapping of
        static URLs to the corresponding directories
        """
        statics = {}
        for addons_path in self.config.addons_path:
            if addons_path not in sys.path:
                sys.path.insert(0, addons_path)
            for module in os.listdir(addons_path):
                if module not in addons_module:
                    manifest_path = os.path.join(addons_path, module, '__openerp__.py')
                    path_static = os.path.join(addons_path, module, 'static')
                    if os.path.isfile(manifest_path) and os.path.isdir(path_static):
                        manifest = ast.literal_eval(open(manifest_path).read())
                        manifest['addons_path'] = addons_path
                        _logger.info("Loading %s", module)
                        m = __import__(module)
                        addons_module[module] = m
                        addons_manifest[module] = manifest
                        statics['/%s/static' % module] = path_static
        for k, v in controllers_class.items():
            if k not in controllers_object:
                o = v()
                controllers_object[k] = o
                if hasattr(o, '_cp_path'):
                    controllers_path[o._cp_path] = o
        return statics

    def find_handler(self, *l):
        """
        Tries to discover the controller handling the request for the path
        specified by the provided parameters

        :param l: path sections to a controller or controller method
        :returns: a callable matching the path sections, or ``None``
        :rtype: ``Controller | None``
        """
        if len(l) > 1:
            for i in range(len(l), 1, -1):
                ps = "/" + "/".join(l[0:i])
                if ps in controllers_path:
                    c = controllers_path[ps]
                    rest = l[i:] or ['index']
                    meth = rest[0]
                    m = getattr(c, meth)
                    if getattr(m, 'exposed', False):
                        _logger.debug("Dispatching to %s %s %s", ps, c, meth)
                        return m
        return None
<commit_msg>[FIX] al hacks<commit_after>#!/usr/bin/python
from __future__ import with_statement

import functools
import logging
import urllib
import os
import pprint
import sys
import traceback
import uuid
import xmlrpclib

import simplejson
import werkzeug.datastructures
import werkzeug.exceptions
import werkzeug.utils
import werkzeug.wrappers
import werkzeug.wsgi

import ast
import nonliterals
import http
import session
import openerplib

__all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller',
           'WebRequest', 'JsonRequest', 'HttpRequest']

_logger = logging.getLogger(__name__)

#-----------------------------------------------------------
# Globals (wont move into a pool)
#-----------------------------------------------------------

addons_module = {}
addons_manifest = {}
controllers_class = {}
controllers_object = {}
controllers_path = {}

#----------------------------------------------------------
# OpenERP Web RequestHandler
#----------------------------------------------------------
class WebRequest(object):
    """ Parent class for all OpenERP Web request types, mostly deals with
    initialization and setup of the request object (the dispatching itself has
    to be handled by the subclasses)

    :param request: a wrapped werkzeug Request object
    :type request: :class:`werkzeug.wrappers.BaseRequest`
    :param config: configuration object

    .. attribute:: httprequest

        the original :class:`werkzeug.wrappers.Request` object provided to the
        request

    .. attribute:: httpsession

        a :class:`~collections.Mapping` holding the HTTP session data for the
        current http session

    .. attribute:: config

        config parameter provided to the request object

    .. attribute:: params

        :class:`~collections.Mapping` of request parameters, not generally
        useful as they're provided directly to the handler method as keyword
        arguments

    .. attribute:: session_id

        opaque identifier for the :class:`session.OpenERPSession` instance of
        the current request

    .. attribute:: session

        :class:`~session.OpenERPSession` instance for the current request

    .. attribute:: context

        :class:`~collections.Mapping` of context values for the current request

    .. attribute:: debug

        ``bool``, indicates whether the debug mode is active on the client
    """
    def __init__(self, request, config):
        self.httprequest = request
        self.httpresponse = None
        self.httpsession = request.session
        self.config = config

    def init(self, params):
        self.params = dict(params)
        # OpenERP session setup
        self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
        self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession())
        self.session.config = self.config
        self.context = self.params.pop('context', None)
        self.debug = self.params.pop('debug', False) != False

class JsonRequest(WebRequest):
    """ JSON-RPC2 over HTTP.

    Sucessful request::

      --> {"jsonrpc": "2.0",
           "method": "call",
           "params": {"session_id": "SID",
                      "context": {},
                      "arg1": "val1" },
           "id": null}

      <-- {"jsonrpc": "2.0",
           "result": { "res1": "val1" },
           "id": null}

    Request producing a error::

      --> {"jsonrpc": "2.0",
           "method": "call",
           "params": {"session_id": "SID",
                      "context": {},
                      "arg1": "val1" },
           "id": null}

      <-- {"jsonrpc": "2.0",
           "error": {"code": 1,
                     "message": "End user error message.",
                     "data": {"code": "codestring",
                              "debug": "traceback" } },
           "id": null}

    """

    def dispatch(self, controller, method, requestf=None, request=None):
        """ Calls the method asked for by the JSON-RPC2 request

        :param controller: the instance of the controller which received the request
        :param method: the method which received the request
        :param requestf: a file-like object containing an encoded JSON-RPC2 request
        :param request: a JSON-RPC2 request

        :returns: an utf8 encoded JSON-RPC2 reply
        """
        response = {"jsonrpc": "2.0" }
        error = None
        try:
            # Read POST content or POST Form Data named "request"
            if requestf:
                self.jsonrequest = simplejson.load(requestf, object_hook=nonliterals.non_literal_decoder)
            else:
                self.jsonrequest = simplejson.loads(request, object_hook=nonliterals.non_literal_decoder)
            self.init(self.jsonrequest.get("params", {}))
            if _logger.isEnabledFor(logging.DEBUG):
                _logger.debug("--> %s.%s\n%s", controller.__class__.__name__, method.__name__, pprint.pformat(self.jsonrequest))
            response['id'] = self.jsonrequest.get('id')
            response["result"] = method(controller, self, **self.params)
        except openerplib.AuthenticationError:
            error = {
                'code': 100,
                'message': "OpenERP Session Invalid",
                'data': {
                    'type': 'session_invalid',
                    'debug': traceback.format_exc()
                }
            }
        except xmlrpclib.Fault, e:
            error = {
                'code': 200,
                'message': "OpenERP Server Error",
                'data': {
                    'type': 'server_exception',
                    'fault_code': e.faultCode,
                    'debug': "Client %s\nServer %s" % (
                    "".join(traceback.format_exception("", None, sys.exc_traceback)), e.faultString)
                }
            }
        except Exception:
            logging.getLogger(__name__ + '.JSONRequest.dispatch').exception\
                ("An error occured while handling a json request")
            error = {
                'code': 300,
                'message': "OpenERP WebClient Error",
                'data': {
                    'type': 'client_exception',
                    'debug': "Client %s" % traceback.format_exc()
                }
            }
        if error:
            response["error"] = error

        if _logger.isEnabledFor(logging.DEBUG):
            _logger.debug("<--\n%s", pprint.pformat(response))
        content = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder)
        return werkzeug.wrappers.Response(
            content, headers=[('Content-Type', 'application/json'),
                              ('Content-Length', len(content))])

def jsonrequest(f):
    """ Decorator marking the decorated method as being a handler for a
    JSON-RPC request (the exact request path is specified via the
    ``$(Controller._cp_path)/$methodname`` combination.

    If the method is called, it will be provided with a :class:`JsonRequest`
    instance and all ``params`` sent during the JSON-RPC request, apart from
    the ``session_id``, ``context`` and ``debug`` keys (which are stripped out
    beforehand)
    """
    @functools.wraps(f)
    def json_handler(controller, request, config):
        return JsonRequest(request, config).dispatch(
            controller, f, requestf=request.stream)
    json_handler.exposed = True
    return json_handler

class HttpRequest(WebRequest):
    """ Regular GET/POST request
    """
    def dispatch(self, controller, method):
        params = dict(self.httprequest.args)
        params.update(self.httprequest.form)
        params.update(self.httprequest.files)
        self.init(params)
        akw = {}
        for key, value in self.httprequest.args.iteritems():
            if isinstance(value, basestring) and len(value) < 1024:
                akw[key] = value
            else:
                akw[key] = type(value)
        _logger.debug("%s --> %s.%s %r", self.httprequest.method, controller.__class__.__name__, method.__name__, akw)
        r = method(controller, self, **self.params)
        if self.debug or 1:
            if isinstance(r, werkzeug.wrappers.BaseResponse):
                _logger.debug('<-- %s', r)
            else:
                _logger.debug("<-- size: %s", len(r))
        return r

    def make_response(self, data, headers=None, cookies=None):
        """ Helper for non-HTML responses, or HTML responses with custom
        response headers or cookies.

        While handlers can just return the HTML markup of a page they want to
        send as a string if non-HTML data is returned they need to create a
        complete response object, or the returned data will not be correctly
        interpreted by the clients.

        :param basestring data: response body
        :param headers: HTTP headers to set on the response
        :type headers: ``[(name, value)]``
        :param collections.Mapping cookies: cookies to set on the client
        """
        response = werkzeug.wrappers.Response(data, headers=headers)
        if cookies:
            for k, v in cookies.iteritems():
                response.set_cookie(k, v)
        return response

    def not_found(self, description=None):
        """ Helper for 404 response, return its result from the method
        """
        return werkzeug.exceptions.NotFound(description)

def httprequest(f):
    """ Decorator marking the decorated method as being a handler for a
    normal HTTP request (the exact request path is specified via the
    ``$(Controller._cp_path)/$methodname`` combination.

    If the method is called, it will be provided with a :class:`HttpRequest`
    instance and all ``params`` sent during the request (``GET`` and ``POST``
    merged in the same dictionary), apart from the ``session_id``, ``context``
    and ``debug`` keys (which are stripped out beforehand)
    """
    @functools.wraps(f)
    def http_handler(controller, request, config):
        return HttpRequest(request, config).dispatch(controller, f)
    http_handler.exposed = True
    return http_handler

class ControllerType(type):
    def __init__(cls, name, bases, attrs):
        super(ControllerType, cls).__init__(name, bases, attrs)
        controllers_class["%s.%s" % (cls.__module__, cls.__name__)] = cls

class Controller(object):
    __metaclass__ = ControllerType

class Root(object):
    """Root WSGI application for the OpenERP Web Client.

    :param options: mandatory initialization options object, must provide
                    the following attributes:

                    ``server_host`` (``str``)
                      hostname of the OpenERP server to dispatch RPC to
                    ``server_port`` (``int``)
                      RPC port of the OpenERP server
                    ``serve_static`` (``bool | None``)
                      whether this application should serve the various
                      addons's static files
                    ``storage_path`` (``str``)
                      filesystem path where HTTP session data will be stored
                    ``dbfilter`` (``str``)
                      only used in case the list of databases is requested
                      by the server, will be filtered by this pattern
    """
    def __init__(self, options):
        self.root = '/web/webclient/home'
        self.config = options

        if self.config.backend == 'local':
            conn = openerplib.get_connector(protocol='local')
        else:
            conn = openerplib.get_connector(hostname=self.config.server_host,
                   port=self.config.server_port)
        self.config.connector = conn

        self.session_cookie = 'sessionid'
        self.addons = {}

        static_dirs = self._load_addons()
        if options.serve_static:
            self.dispatch = werkzeug.wsgi.SharedDataMiddleware(
                self.dispatch, static_dirs)

        if options.session_storage:
            if not os.path.exists(options.session_storage):
                os.mkdir(options.session_storage, 0700)
            self.session_storage = options.session_storage

    def __call__(self, environ, start_response):
        """ Handle a WSGI request
        """
        return self.dispatch(environ, start_response)

    def dispatch(self, environ, start_response):
        """
        Performs the actual WSGI dispatching for the application, may be
        wrapped during the initialization of the object.

        Call the object directly.
        """
        request = werkzeug.wrappers.Request(environ)
        request.parameter_storage_class = werkzeug.datastructures.ImmutableDict

        if request.path == '/':
            params = urllib.urlencode(dict(request.args, debug=''))
            return werkzeug.utils.redirect(self.root + '?' + params, 301)(
                environ, start_response)
        elif request.path == '/mobile':
            return werkzeug.utils.redirect(
                '/web_mobile/static/src/web_mobile.html', 301)(environ, start_response)

        handler = self.find_handler(*(request.path.split('/')[1:]))

        if not handler:
            response = werkzeug.exceptions.NotFound()
        else:
            with http.session(request, self.session_storage, self.session_cookie) as session:
                result = handler(
                    request, self.config)

                if isinstance(result, basestring):
                    response = werkzeug.wrappers.Response(
                        result, headers=[('Content-Type', 'text/html; charset=utf-8'),
                                         ('Content-Length', len(result))])
                else:
                    response = result

                response.set_cookie(self.session_cookie, session.sid)

        return response(environ, start_response)

    def _load_addons(self):
        """
        Loads all addons at the specified addons path, returns a mapping of
        static URLs to the corresponding directories
        """
        statics = {}
        for addons_path in self.config.addons_path:
            if addons_path not in sys.path:
                sys.path.insert(0, addons_path)
            for module in os.listdir(addons_path):
                if module not in addons_module:
                    manifest_path = os.path.join(addons_path, module, '__openerp__.py')
                    path_static = os.path.join(addons_path, module, 'static')
                    if os.path.isfile(manifest_path) and os.path.isdir(path_static):
                        manifest = ast.literal_eval(open(manifest_path).read())
                        manifest['addons_path'] = addons_path
                        _logger.info("Loading %s", module)
                        m = __import__(module)
                        addons_module[module] = m
                        addons_manifest[module] = manifest
                        statics['/%s/static' % module] = path_static
        for k, v in controllers_class.items():
            if k not in controllers_object:
                o = v()
                controllers_object[k] = o
                if hasattr(o, '_cp_path'):
                    controllers_path[o._cp_path] = o
        return statics

    def find_handler(self, *l):
        """
        Tries to discover the controller handling the request for the path
        specified by the provided parameters

        :param l: path sections to a controller or controller method
        :returns: a callable matching the path sections, or ``None``
        :rtype: ``Controller | None``
        """
        if len(l) > 1:
            for i in range(len(l), 1, -1):
                ps = "/" + "/".join(l[0:i])
                if ps in controllers_path:
                    c = controllers_path[ps]
                    rest = l[i:] or ['index']
                    meth = rest[0]
                    m = getattr(c, meth)
                    if getattr(m, 'exposed', False):
                        _logger.debug("Dispatching to %s %s %s", ps, c, meth)
                        return m
        return None
<|endoftext|>


<commit_before>from autoslug import AutoSlugField
from ckeditor_uploader.fields import RichTextUploadingField
from django.conf import settings
from django.core.urlresolvers import reverse
from django.core.validators import RegexValidator
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _

from adhocracy4.ckeditor.fields import RichTextCollapsibleUploadingField
from adhocracy4.models import base
from adhocracy4 import transforms as html_transforms
from adhocracy4.images import fields

class ProjectManager(models.Manager):

    def get_by_natural_key(self, name):
        return self.get(name=name)

    def featured(self):
        return self.filter(is_draft=False, is_archived=False)\
                   .order_by('-created')[:8]

class ProjectContactDetailMixin(models.Model):
    class Meta:
        abstract = True

    phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$',
        message=_("Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed."))

    contact_address_text = models.TextField()
    contact_email = models.EmailField()
    contact_name = models.CharField(max_length=120)
    contact_phone = models.CharField(validators=[phone_regex], max_length=17)
    contact_url = models.URLField()

class Project(ProjectContactDetailMixin, base.TimeStampedModel):
    slug = AutoSlugField(populate_from='name', unique=True)
    name = models.CharField(
        max_length=120,
        verbose_name=_('Title of your project'),
        help_text=_('This title will appear on the '
                    'teaser card and on top of the project '
                    'detail page. It should be max. 120 characters long')
    )
    organisation = models.ForeignKey(
        settings.A4_ORGANISATIONS_MODEL,
        on_delete=models.CASCADE)
    description = models.CharField(
        max_length=250,
        verbose_name=_('Short description of your project'),
        help_text=_('This short description will appear on '
                    'the header of the project and in the teaser. '
                    'It should briefly state the goal of the project '
                    'in max. 250 chars.')
    )
    information = RichTextCollapsibleUploadingField(
        blank=True,
        config_name='collapsible-image-editor',
        verbose_name=_('Description of your project'),
        help_text=_('This description should tell participants '
                    'what the goal of the project is, how the project’s '
                    'participation will look like. It will be always visible '
                    'in the „Info“ tab on your project’s page.')
    )
    result = RichTextUploadingField(
        blank=True,
        config_name='image-editor',
        verbose_name=_('Results of your project'),
        help_text=_('Here you should explain what the expected outcome of the '
                    'project will be and how you are planning to use the '
                    'results. If the project is finished you should add a '
                    'summary of the results.')
    )
    is_public = models.BooleanField(
        default=True,
        verbose_name=_('Access to the project'),
        help_text=_('Please indicate whether this project should be public '
                    'or restricted to invited users. Teasers for your project '
                    'including title and short description will always be '
                    'visible to everyone')
    )
    is_draft = models.BooleanField(default=True)
    image = fields.ConfiguredImageField(
        'heroimage',
        verbose_name=_('Header image'),
        help_prefix=_(
            'The image will be shown as a decorative background image.'
        ),
        upload_to='projects/backgrounds',
        blank=True)
    image_copyright = fields.ImageCopyrightField(image_name=_('Header image'))
    tile_image = fields.ConfiguredImageField(
        'tileimage',
        verbose_name=_('Tile image'),
        help_prefix=_(
            'The image will be shown in the project tile.'
        ),
        upload_to='projects/tiles',
        blank=True)
    tile_image_copyright = fields.ImageCopyrightField(
        image_name=_('Tile image'))
    participants = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='project_participant',
        blank=True,
    )
    moderators = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='project_moderator',
        blank=True,
    )
    is_archived = models.BooleanField(
        default=False,
        verbose_name=_('Project is archived'),
        help_text=_('Exclude this project from all listings by default. '
                    'You can still access this project by using filters.'),
    )

    objects = ProjectManager()

    class Meta:
        ordering = ['-created']

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.information = html_transforms.clean_html_field(
            self.information, 'collapsible-image-editor')
        self.result = html_transforms.clean_html_field(
            self.result, 'image-editor')
        super(Project, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('project-detail', kwargs=dict(slug=self.slug))

    def has_member(self, user):
        """
        Everybody is member of all public projects and private projects can
        be joined as moderator or participant.
        """
        return (
            (user.is_authenticated() and self.is_public)
            or (user in self.participants.all())
            or (user in self.moderators.all())
        )

    def has_moderator(self, user):
        return user in self.moderators.all()

    @cached_property
    def other_projects(self):
        other_projects = self.organisation.project_set\
            .filter(is_draft=False, is_archived=False).exclude(slug=self.slug)
        return other_projects

    @property
    def is_private(self):
        return not self.is_public

    @cached_property
    def modules(self):
        return self.module_set.all()

    @property
    def last_active_phase(self):
        """
        Return the last active phase.

        The last active phase is defined as the phase that out of all past
        and currently active phases started last.
        """
        return self.phases\
            .past_and_active_phases()\
            .last()

    @property
    def last_active_module(self):
        """Return the module of the last active phase."""
        last_active_phase = self.last_active_phase
        if last_active_phase:
            return last_active_phase.module
        return None

    @property
    def active_phase(self):
        """
        Return the currently active phase.

        The currently active phase is defined as the phase that out of all
        currently active phases started last. This is analogous to the last
        active phase.

        Attention: This method is _deprecated_ as multiple phases may be
        active at the same time.
        """
        last_active_phase = self.last_active_phase
        if last_active_phase and not last_active_phase.is_over:
            return last_active_phase
        return None

    @property
    def days_left(self):
        """
        Return the number of days left in the currently active phase.

        Attention: This method is _deprecated_ as multiple phases may be
        active at the same time.
        """
        active_phase = self.active_phase
        if active_phase:
            today = timezone.now().replace(hour=0, minute=0, second=0)
            time_delta = active_phase.end_date - today
            return time_delta.days
        return None

    @cached_property
    def phases(self):
        from adhocracy4.phases import models as phase_models
        return phase_models.Phase.objects.filter(module__project=self)

    @cached_property
    def future_phases(self):
        return self.phases.future_phases()

    @cached_property
    def past_phases(self):
        return self.phases.past_phases()

    @property
    def has_started(self):
        return self.phases.past_and_active_phases().exists()

    @property
    def has_finished(self):
        return not self.phases.active_phases().exists()\
               and not self.phases.future_phases().exists()

    @property
    def is_archivable(self):
        return not self.is_archived and self.has_finished
<commit_msg>Allowing contact fields to be blank<commit_after>from autoslug import AutoSlugField
from ckeditor_uploader.fields import RichTextUploadingField
from django.conf import settings
from django.core.urlresolvers import reverse
from django.core.validators import RegexValidator
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _

from adhocracy4.ckeditor.fields import RichTextCollapsibleUploadingField
from adhocracy4.models import base
from adhocracy4 import transforms as html_transforms
from adhocracy4.images import fields

class ProjectManager(models.Manager):

    def get_by_natural_key(self, name):
        return self.get(name=name)

    def featured(self):
        return self.filter(is_draft=False, is_archived=False)\
                   .order_by('-created')[:8]

class ProjectContactDetailMixin(models.Model):
    class Meta:
        abstract = True

    phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$',
        message=_("Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed."))

    contact_address_text = models.TextField(blank=True)
    contact_email = models.EmailField(blank=True)
    contact_name = models.CharField(max_length=120, blank=True)
    contact_phone = models.CharField(validators=[phone_regex], max_length=17, blank=True)
    contact_url = models.URLField(blank=True)

class Project(ProjectContactDetailMixin, base.TimeStampedModel):
    slug = AutoSlugField(populate_from='name', unique=True)
    name = models.CharField(
        max_length=120,
        verbose_name=_('Title of your project'),
        help_text=_('This title will appear on the '
                    'teaser card and on top of the project '
                    'detail page. It should be max. 120 characters long')
    )
    organisation = models.ForeignKey(
        settings.A4_ORGANISATIONS_MODEL,
        on_delete=models.CASCADE)
    description = models.CharField(
        max_length=250,
        verbose_name=_('Short description of your project'),
        help_text=_('This short description will appear on '
                    'the header of the project and in the teaser. '
                    'It should briefly state the goal of the project '
                    'in max. 250 chars.')
    )
    information = RichTextCollapsibleUploadingField(
        blank=True,
        config_name='collapsible-image-editor',
        verbose_name=_('Description of your project'),
        help_text=_('This description should tell participants '
                    'what the goal of the project is, how the project’s '
                    'participation will look like. It will be always visible '
                    'in the „Info“ tab on your project’s page.')
    )
    result = RichTextUploadingField(
        blank=True,
        config_name='image-editor',
        verbose_name=_('Results of your project'),
        help_text=_('Here you should explain what the expected outcome of the '
                    'project will be and how you are planning to use the '
                    'results. If the project is finished you should add a '
                    'summary of the results.')
    )
    is_public = models.BooleanField(
        default=True,
        verbose_name=_('Access to the project'),
        help_text=_('Please indicate whether this project should be public '
                    'or restricted to invited users. Teasers for your project '
                    'including title and short description will always be '
                    'visible to everyone')
    )
    is_draft = models.BooleanField(default=True)
    image = fields.ConfiguredImageField(
        'heroimage',
        verbose_name=_('Header image'),
        help_prefix=_(
            'The image will be shown as a decorative background image.'
        ),
        upload_to='projects/backgrounds',
        blank=True)
    image_copyright = fields.ImageCopyrightField(image_name=_('Header image'))
    tile_image = fields.ConfiguredImageField(
        'tileimage',
        verbose_name=_('Tile image'),
        help_prefix=_(
            'The image will be shown in the project tile.'
        ),
        upload_to='projects/tiles',
        blank=True)
    tile_image_copyright = fields.ImageCopyrightField(
        image_name=_('Tile image'))
    participants = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='project_participant',
        blank=True,
    )
    moderators = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='project_moderator',
        blank=True,
    )
    is_archived = models.BooleanField(
        default=False,
        verbose_name=_('Project is archived'),
        help_text=_('Exclude this project from all listings by default. '
                    'You can still access this project by using filters.'),
    )

    objects = ProjectManager()

    class Meta:
        ordering = ['-created']

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.information = html_transforms.clean_html_field(
            self.information, 'collapsible-image-editor')
        self.result = html_transforms.clean_html_field(
            self.result, 'image-editor')
        super(Project, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('project-detail', kwargs=dict(slug=self.slug))

    def has_member(self, user):
        """
        Everybody is member of all public projects and private projects can
        be joined as moderator or participant.
        """
        return (
            (user.is_authenticated() and self.is_public)
            or (user in self.participants.all())
            or (user in self.moderators.all())
        )

    def has_moderator(self, user):
        return user in self.moderators.all()

    @cached_property
    def other_projects(self):
        other_projects = self.organisation.project_set\
            .filter(is_draft=False, is_archived=False).exclude(slug=self.slug)
        return other_projects

    @property
    def is_private(self):
        return not self.is_public

    @cached_property
    def modules(self):
        return self.module_set.all()

    @property
    def last_active_phase(self):
        """
        Return the last active phase.

        The last active phase is defined as the phase that out of all past
        and currently active phases started last.
        """
        return self.phases\
            .past_and_active_phases()\
            .last()

    @property
    def last_active_module(self):
        """Return the module of the last active phase."""
        last_active_phase = self.last_active_phase
        if last_active_phase:
            return last_active_phase.module
        return None

    @property
    def active_phase(self):
        """
        Return the currently active phase.

        The currently active phase is defined as the phase that out of all
        currently active phases started last. This is analogous to the last
        active phase.

        Attention: This method is _deprecated_ as multiple phases may be
        active at the same time.
        """
        last_active_phase = self.last_active_phase
        if last_active_phase and not last_active_phase.is_over:
            return last_active_phase
        return None

    @property
    def days_left(self):
        """
        Return the number of days left in the currently active phase.

        Attention: This method is _deprecated_ as multiple phases may be
        active at the same time.
        """
        active_phase = self.active_phase
        if active_phase:
            today = timezone.now().replace(hour=0, minute=0, second=0)
            time_delta = active_phase.end_date - today
            return time_delta.days
        return None

    @cached_property
    def phases(self):
        from adhocracy4.phases import models as phase_models
        return phase_models.Phase.objects.filter(module__project=self)

    @cached_property
    def future_phases(self):
        return self.phases.future_phases()

    @cached_property
    def past_phases(self):
        return self.phases.past_phases()

    @property
    def has_started(self):
        return self.phases.past_and_active_phases().exists()

    @property
    def has_finished(self):
        return not self.phases.active_phases().exists()\
               and not self.phases.future_phases().exists()

    @property
    def is_archivable(self):
        return not self.is_archived and self.has_finished
<|endoftext|>