common.py 11 KB
Newer Older
Administrator committed
1 2 3 4 5 6 7 8 9
"""commons functions to collect data through API."""

from django.conf import settings
import xmlrpc.client
import couchdb
import logging

coop_logger = logging.getLogger("coop.framework")

10 11
MARSHALL_ERROR = "cannot marshal None unless allow_none is enabled" #  api user need to use english language

Administrator committed
12 13 14
class OdooAPI:
    """Class to handle  Odoo API requests."""

15 16 17 18
    url = None
    user = None
    passwd = None
    db = None
Administrator committed
19 20 21 22
    common = None
    uid = None
    models = None

23
    def __init__(self, odoo=settings.ODOO):
Administrator committed
24 25
        """Initialize xmlrpc connection."""
        try:
26 27 28 29
            self.url = odoo['url']
            self.user = odoo['user']
            self.passwd = odoo['passwd']
            self.db = odoo['db']
Administrator committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
            common_proxy_url = '{}/xmlrpc/2/common'.format(self.url)
            object_proxy_url = '{}/xmlrpc/2/object'.format(self.url)
            self.common = xmlrpc.client.ServerProxy(common_proxy_url)
            self.uid = self.common.authenticate(self.db,
                                                self.user, self.passwd, {})
            self.models = xmlrpc.client.ServerProxy(object_proxy_url)
        except:
            coop_logger.error("Impossible d'initialiser la connexion API Odoo")

    def get_entity_fields(self, entity):
        fields = self.models.execute_kw(self.db, self.uid, self.passwd,
                                        entity, 'fields_get',
                                        [],
                                        {'attributes': ['string', 'help',
                                         'type']})
        return fields

    def search_count(self, entity, cond=[]):
        """Return how many lines are matching the request."""
        return self.models.execute_kw(self.db, self.uid, self.passwd,
                                      entity, 'search_count', [cond])

    def search_read(self, entity, cond=[], fields={}, limit=3500, offset=0,
                    order='id ASC'):
        """Main api request, retrieving data according search conditions."""
        fields_and_context = {'fields': fields,
56
                              'context': {'lang': 'fr_FR', 'tz': 'Europe/Paris'},
Administrator committed
57 58 59 60 61 62 63 64 65 66 67
                              'limit': limit,
                              'offset': offset,
                              'order': order
                              }

        return self.models.execute_kw(self.db, self.uid, self.passwd,
                                      entity, 'search_read', [cond],
                                      fields_and_context)

    def update(self, entity, ids, fields):
        """Update entities which have ids, with new fields values."""
68 69 70
        context = {
                    'context': {'lang': 'fr_FR', 'tz': 'Europe/Paris'}
                  }
Administrator committed
71
        return self.models.execute_kw(self.db, self.uid, self.passwd,
72
                                      entity, 'write', [ids, fields], context)
Administrator committed
73 74 75

    def create(self, entity, fields):
        """Create entity instance with given fields values."""
76 77 78
        context = {
                    'context': {'lang': 'fr_FR', 'tz': 'Europe/Paris'}
                  }
Administrator committed
79
        return self.models.execute_kw(self.db, self.uid, self.passwd,
80
                                      entity, 'create', [fields], context)
Administrator committed
81

82 83 84 85 86
    def delete(self, entity, ids):
        """Destroy entity instance by given ids."""
        return self.models.execute_kw(self.db, self.uid, self.passwd,
                                      entity, 'unlink', [ids])

Administrator committed
87
    def execute(self, entity, method, ids, params={}):
88 89 90 91 92 93 94
        res = []
        try:
            res = self.models.execute_kw(self.db, self.uid, self.passwd,
                                          entity, method, [ids], params)
        except Exception as e:
            if not (MARSHALL_ERROR in str(e)):
                coop_logger.error("Error while api execute: %s", str(e))
95
                raise RuntimeError('Failed api execute : ' + str(e)) from e
96
        return res
Administrator committed
97 98 99 100

    def authenticate(self, login, password):
        return self.common.authenticate(self.db, login, password, {})

101 102 103 104 105 106 107 108 109 110 111 112
    def get_system_param(self, key):
        value = ''
        try:
            res = self.search_read('ir.config_parameter',
                                   [['key', '=', key]],
                                   ['value'])
            if res:
                value = res[0]['value']
        except Exception as e:
            coop_logger.error('get_system_param: (%s) %s', key, str(e))
        return value

Administrator committed
113 114 115
class CouchDB:
    """Class to handle interactions with CouchDB"""

116 117 118 119
    if 'private_url' in settings.COUCHDB:
        url = settings.COUCHDB['private_url']
    else:
        url = settings.COUCHDB['url']
Administrator committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
    dbs = settings.COUCHDB['dbs']
    db = None
    dbc = None

    def __init__(self, arg_db=''):
        url = self.url
        dbs = self.dbs
        if len(arg_db) > 0:
            db = arg_db

        couchserver = couchdb.Server(url)
        self.dbc = couchserver[dbs[db]]

    def getDocById(self, id):
        # https://gist.github.com/marians/8e41fc817f04de7c4a70
        return self.dbc[id]

    def getView(self, view, my_key=None):
        #  return self.dbc.view(view, my_key,include_docs=False)
        return self.dbc.view(view, include_docs=True)

    def getAllDocs(self, key=None, value=None, with_key=True, descending=False):
        """
            Get all documents
            If key and value provided :
                Filter with the key/value pair if 'with_key' is True
                Filter without key or without value if 'with_key' if False
        """
        res = []
        for item in self.dbc.view('_all_docs', include_docs=True, descending=descending):
            m = item.doc
            if key and value:
                if with_key:
                    if (key in m) and m[key] == value:
                        res.append(m)
                else:
                    if not(key in m) or m[key] != value:
                        res.append(m)
            else:
                res.append(m)
        return res

    def updateDoc(self, data, key_name='_id', remove_keys=[]):
        """
            Update a document with data
            Fetch the document using provided key_name
                - key must be in data
                - key must be unique (duh)
            Remove the remove_keys from document

            Returns None in case of error.
        """
        existing = None
        try:
            if key_name in data:
                try:
                    key_value = int(data[key_name])
                except:
                    key_value = data[key_name]
                _rev = None

                # Find existing doc
                document = None
                try:
                    # Use view if it exists for this key
                    index = self.dbc['_design/index']
                    if index and ('by_' + key_name) in index['views']:
                        for item in self.dbc.view('index/by_' + key_name, key=key_value, include_docs=True, limit=1):
                            document = item.doc
                except:
                    pass
                    # else fetch in all docs
                if document is None:
                    for item in self.dbc.view('_all_docs', include_docs=True):
                        if key_name in item.doc:
                            if item.doc[key_name] == key_value:
                                document = item.doc

                if not (document is None):
                    existing = document
                if ('_rev' in data) and data['_rev'] == document['_rev']:
                    existing = document

                if not (existing is None):
                    for key in data:
                        value = data[key]
                        if (key == 'odoo_id'):
                            value = int(value)
                        existing[key] = value
                    for rk in remove_keys:
                        existing.pop(rk, None)
                    if ('_rev' in data):
                        (_id, _rev) = self.dbc.save(existing)
                    else:
                        res = self.dbc.update([existing])
                        if res[0] and (res[0][0] is True):
                            _rev = res[0][2]

                    if not(_rev is None):
                        existing['_rev'] = _rev
                else:
                    coop_logger.warning('CouchDB : Document not found')
            else:
                coop_logger.warning('CouchDB : Key not found in data.')
        except Exception as e:
            coop_logger.error('Update couchdb: %s, %s', str(e), str(data))

        return existing

    def delete(self, doc):
        # Database has to be purged to completly remove data
        # http://docs.couchdb.org/en/stable/api/database/misc.html
        res = ''
        try:
            res = self.dbc.delete(doc)
            # only admin can definitly purge docs
        except Exception as e:
            res = str(e)
        return res

class MConfig:
    """Module configuration"""

    def get_settings(module):
        import json
        try:
            with open(module + '/settings.json') as json_file:
                msettings = json.load(json_file)
                # file automatically closed with 'with..as' statement
        except Exception as e:
            msettings = {}

        return msettings

    def save_settings(module, data):
        import json # TODO : which performance to declare here instead of file headings
        res = False
        try:
            with open(module + '/settings.json', 'w') as outfile:
                    json.dump(data, outfile)
            res = True
        except Exception as e:
            coop_logger.error(str(e))
        return res

class Verification:

    @staticmethod
    def verif_token(token, coop_id):
        import hashlib
        match = False
        api = OdooAPI()
        cond = [['id', '=', coop_id]]
        fields = ['create_date']
        res = api.search_read('res.partner', cond, fields, 1)
        if (res and len(res) == 1):
            coop = res[0]
            md5_calc = hashlib.md5(coop['create_date'].encode('utf-8')).hexdigest()
            if token == md5_calc:
                match = True
        return match
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

    @staticmethod
    def has_right_to_overriden_entrance_date(request):
        from members.models import CagetteUser  # Error if declared on top....
        from urllib.parse import unquote
        answer = False
        u_grants = getattr(settings, 'ODOO_USERS_GRANTS', [])

        if len(u_grants) > 0 and CagetteUser.are_credentials_ok(request):
            try:
                login = unquote(request.COOKIES['login'])
                for user in u_grants:
                    if user['login'] == login:
                        if 'bdm' in user:
                            if (('all' in user['bdm'] and user['bdm']['all'] is True)
                                or ('record_absence' in user['bdm'] and user['bdm']['record_absence'] is True)):
                                answer = True
            except Exception as e:
                coop_logger.error("has right to overriden date : %s", str(e))

        return answer