Example #1
0
class Analytic(BaseModule):
    '''`Analytic` module provides data type and controller from `Analytics Workflow` and accompanying analytics docs. It uses `pre_create` handler to assure no events duplications occur and all occurrences of the same event are recorded in one doc.'''
    collection = 'analytics'
    attrs = {
        'user':
        ATTR.ID(desc='`_id` of `User` doc the doc belongs to.'),
        'event':
        ATTR.STR(desc='Analytics event name.'),
        'subevent':
        ATTR.ANY(
            desc=
            'Analytics subevent distinguishing attribute. This is usually `STR`, or `ID` but it is introduced in the module as `ANY` to allow wider use-cases by developers.'
        ),
        'occurrences':
        ATTR.LIST(
            desc='All occurrences of the event as list.',
            list=[
                ATTR.DICT(
                    desc='Single occurrence of the event details.',
                    dict={
                        'args':
                        ATTR.DICT(
                            desc=
                            'Key-value `dict` containing event args, if any.',
                            dict={
                                '__key': ATTR.STR(),
                                '__val': ATTR.ANY()
                            }),
                        'score':
                        ATTR.INT(
                            desc='Numerical score for occurrence of the event.'
                        ),
                        'create_time':
                        ATTR.DATETIME(
                            desc=
                            'Python `datetime` ISO format of the occurrence of the event.'
                        ),
                    })
            ]),
        'score':
        ATTR.INT(
            desc=
            'Total score of all scores of all occurrences of the event. This can be used for data analysis.'
        ),
    }
    unique_attrs = [('user', 'event', 'subevent')]
    methods = {
        'read': {
            'permissions': [PERM(privilege='read')]
        },
        'create': {
            'permissions': [PERM(privilege='__sys')],
            'doc_args': {
                'event': ATTR.STR(),
                'subevent': ATTR.ANY(),
                'args': ATTR.DICT(dict={
                    '__key': ATTR.STR(),
                    '__val': ATTR.ANY()
                }),
            },
        },
        'update': {
            'permissions': [PERM(privilege='__sys')]
        },
        'delete': {
            'permissions': [PERM(privilege='delete')]
        },
    }

    async def pre_create(self, skip_events, env, query, doc, payload):
        analytic_results = await self.read(
            skip_events=[Event.PERM],
            env=env,
            query=[
                {
                    'user': env['session'].user._id,
                    'event': doc['event'],
                    'subevent': doc['subevent'],
                },
                {
                    '$limit': 1
                },
            ],
        )
        if analytic_results.args.count:
            analytic_results = await self.update(
                skip_events=[Event.PERM],
                env=env,
                query=[{
                    '_id': analytic_results.args.docs[0]._id
                }],
                doc={
                    'occurrences': {
                        '$append': {
                            'args': doc['args'],
                            'score':
                            doc['score'] if 'score' in doc.keys() else 0,
                            'create_time':
                            datetime.datetime.utcnow().isoformat(),
                        }
                    },
                    'score': {
                        '$add': doc['score'] if 'score' in doc.keys() else 0
                    },
                },
            )
            return analytic_results
        else:
            doc = {
                'event':
                doc['event'],
                'subevent':
                doc['subevent'],
                'occurrences': [{
                    'args':
                    doc['args'],
                    'score':
                    doc['score'] if 'score' in doc.keys() else 0,
                    'create_time':
                    datetime.datetime.utcnow().isoformat(),
                }],
                'score':
                doc['score'] if 'score' in doc.keys() else 0,
            }
            return (skip_events, env, query, doc, payload)
Example #2
0
File: group.py Project: masaar/limp
class Group(BaseModule):
    '''`Group` module provides data type and controller for groups in LIMP eco-system.'''
    collection = 'groups'
    attrs = {
        'user':
        ATTR.ID(desc='`_id` of `User` doc the doc belongs to.'),
        'name':
        ATTR.LOCALE(desc='Name of the groups as `LOCALE`.'),
        'desc':
        ATTR.LOCALE(
            desc=
            'Description of the group as `LOCALE`. This can be used for dynamic generated groups that are meant to be exposed to end-users.'
        ),
        'privileges':
        ATTR.DICT(
            desc='Privileges that any user is a member of the group has.',
            dict={
                '__key': ATTR.STR(),
                '__val': ATTR.LIST(list=[ATTR.STR()])
            }),
        'settings':
        ATTR.DICT(
            desc=
            '`Setting` docs to be created, or required for members users when added to the group.',
            dict={
                '__key': ATTR.STR(),
                '__val': ATTR.ANY()
            }),
        'create_time':
        ATTR.DATETIME(
            desc='Python `datetime` ISO format of the doc creation.'),
    }
    defaults = {
        'desc': {locale: ''
                 for locale in Config.locales},
        'privileges': {},
        'settings': {},
    }
    methods = {
        'read': {
            'permissions': [PERM(privilege='admin')]
        },
        'create': {
            'permissions': [PERM(privilege='admin')]
        },
        'update': {
            'permissions': [
                PERM(privilege='admin'),
                PERM(
                    privilege='update',
                    query_mod={'user': '******'},
                    doc_mod={'privileges': None},
                ),
            ],
            'query_args': {
                '_id': ATTR.ID()
            },
        },
        'delete': {
            'permissions': [
                PERM(privilege='admin'),
                PERM(privilege='delete', query_mod={'user': '******'}),
            ],
            'query_args': {
                '_id': ATTR.ID()
            },
        },
    }

    async def pre_create(self, skip_events, env, query, doc, payload):
        return (skip_events, env, query, doc, payload)

    async def pre_update(self, skip_events, env, query, doc, payload):
        # [DOC] Make sure no attrs overwriting would happen
        if 'attrs' in doc.keys():
            results = await self.read(skip_events=[Event.PERM],
                                      env=env,
                                      query=query)
            if not results.args.count:
                return self.status(status=400,
                                   msg='Group is invalid.',
                                   args={'code': 'INVALID_GROUP'})
            if results.args.count > 1:
                return self.status(
                    status=400,
                    msg=
                    'Updating group attrs can be done only to individual groups.',
                    args={'code': 'MULTI_ATTRS_UPDATE'},
                )
            results.args.docs[0]['attrs'].update({
                attr: doc['attrs'][attr]
                for attr in doc['attrs'].keys()
                if doc['attrs'][attr] != None and doc['attrs'][attr] != ''
            })
            doc['attrs'] = results.args.docs[0]['attrs']
        return (skip_events, env, query, doc, payload)
Example #3
0
class Diff(BaseModule):
    '''`Diff` module provides data type and controller for `Diff Workflow`. It is meant for use by internal calls only. Best practice to accessing diff docs is by creating proxy modules or writing LIMP methods that expose the diff docs.'''
    collection = 'diff'
    attrs = {
        'user':
        ATTR.ID(desc='`_id` of `User` doc the doc belongs to.'),
        'module':
        ATTR.STR(desc='Name of the module the original doc is part of.'),
        'doc':
        ATTR.ID(desc='`_id` of the original doc.'),
        'vars':
        ATTR.DICT(
            desc=
            'Key-value `dict` containing all attrs that have been updated from the original doc.',
            dict={
                '__key': ATTR.STR(),
                '__val': ATTR.ANY()
            }),
        'remarks':
        ATTR.STR(
            desc=
            'Human-readable remarks of the doc. This is introduced to allow developers to add log messages to diff docs.'
        ),
        'create_time':
        ATTR.DATETIME(
            desc='Python `datetime` ISO format of the doc creation.'),
    }
    defaults = {'doc': None, 'remarks': ''}
    methods = {
        'read': {
            'permissions': [PERM(privilege='read')]
        },
        'create': {
            'permissions': [PERM(privilege='__sys')]
        },
        'delete': {
            'permissions': [PERM(privilege='delete')]
        },
    }

    async def pre_create(self, skip_events, env, query, doc, payload):
        # [DOC] Detect non-_id update query:
        if '_id' not in query:
            results = await Config.modules[doc['module']
                                           ].read(skip_events=[Event.PERM],
                                                  env=env,
                                                  query=query)
            if results.args.count > 1:
                query.append(
                    {'_id': {
                        '$in': [doc._id for doc in results.args.docs]
                    }})
            elif results.args.count == 1:
                query.append({'_id': results.args.docs[0]._id})
            else:
                return self.status(status=400,
                                   msg='No update docs matched.',
                                   args={'code': 'NO_MATCH'})
        if '_id' in query and type(query['_id'][0]) == list:
            for i in range(len(query['_id'][0]) - 1):
                self.create(
                    skip_events=[Event.PERM],
                    env=env,
                    query=[{
                        '_id': query['_id'][0][i]
                    }],
                    doc=doc,
                )
            query['_id'][0] = query['_id'][0][-1]
        doc['doc'] = ObjectId(query['_id'][0])
        return (skip_events, env, query, doc, payload)
Example #4
0
class Setting(BaseModule):
    '''`Setting` module module provides data type and controller for settings in LIMP eco-system. This is used by `User` module tp provide additional user-wise settings. It also allows for global-typed settings.'''
    collection = 'settings'
    attrs = {
        'user':
        ATTR.ID(desc='`_id` of `User` doc the doc belongs to.'),
        'var':
        ATTR.STR(
            desc=
            'Name of the setting. This is unique for every `user` in the module.'
        ),
        'val':
        ATTR.ANY(desc='Value of the setting.'),
        'type':
        ATTR.LITERAL(
            desc=
            'Type of the setting. This sets whether setting is global, or belong to user, and whether use can update it or not.',
            literal=['global', 'user', 'user_sys']),
    }
    diff = True
    unique_attrs = [('user', 'var', 'type')]
    extns = {
        'val':
        ATTR_MOD(
            condition=lambda skip_events, env, query, doc, scope: type(scope)
            == dict and '__extn' in scope.keys(),
            default=lambda skip_events, env, query, doc, scope: {
                '__extn':
                EXTN(
                    module=scope['__extn']['__module'],
                    attrs=scope['__extn']['__attrs'],
                    force=scope['__extn']['__force'],
                ),
                '__val':
                scope['__extn']['__val'],
            },
        )
    }
    methods = {
        'read': {
            'permissions': [
                PERM(privilege='admin', query_mod={'$limit': 1}),
                PERM(
                    privilege='read',
                    query_mod={
                        'user':
                        '******',
                        'type':
                        ATTR_MOD(
                            condition=lambda skip_events, env, query, doc:
                            'type' in doc.keys() and doc['type'] == 'user_sys',
                            default=lambda skip_events, env, query, doc:
                            InvalidAttrException(
                                attr_name='type',
                                attr_type=ATTR.LITERAL(literal=
                                                       ['global', 'user']),
                                val_type=str,
                            ),
                        ),
                        '$limit':
                        1,
                    },
                ),
            ],
            'query_args': [
                {
                    '_id': ATTR.ID(),
                    'type':
                    ATTR.LITERAL(literal=['global', 'user', 'user_sys']),
                },
                {
                    'var': ATTR.STR(),
                    'type': ATTR.LITERAL(literal=['global']),
                },
                {
                    'var': ATTR.STR(),
                    'user': ATTR.ID(),
                    'type': ATTR.LITERAL(literal=['user', 'user_sys']),
                },
            ],
        },
        'create': {
            'permissions': [
                PERM(privilege='admin'),
                PERM(privilege='create', doc_mod={'type': 'user'}),
            ]
        },
        'update': {
            'permissions': [
                PERM(privilege='admin', query_mod={'$limit': 1}),
                PERM(
                    privilege='update',
                    query_mod={
                        'type': 'user',
                        'user': '******',
                        '$limit': 1
                    },
                    doc_mod={'type': None},
                ),
            ],
            'query_args': [
                {
                    '_id': ATTR.ID(),
                    'type':
                    ATTR.LITERAL(literal=['global', 'user', 'user_sys']),
                },
                {
                    'var': ATTR.STR(),
                    'type': ATTR.LITERAL(literal=['global']),
                },
                {
                    'var': ATTR.STR(),
                    'user': ATTR.ID(),
                    'type': ATTR.LITERAL(literal=['user', 'user_sys']),
                },
            ],
            'doc_args': {
                'val': ATTR.ANY()
            },
        },
        'delete': {
            'permissions': [PERM(privilege='admin', query_mod={'$limit': 1})],
            'query_args': [{
                '_id': ATTR.ID()
            }, {
                'var': ATTR.STR()
            }],
        },
        'retrieve_file': {
            'permissions': [PERM(privilege='*', query_mod={'type': 'global'})],
            'get_method': True,
        },
    }

    async def pre_create(self, skip_events, env, query, doc, payload):
        if (type(doc['val']) == list and len(doc['val']) == 1
                and type(doc['val'][0]) == dict
                and 'content' in doc['val'][0].keys()):
            doc['val'] = doc['val'][0]
        return (skip_events, env, query, doc, payload)

    async def on_create(self, results, skip_events, env, query, doc, payload):
        if doc['type'] in ['user', 'user_sys']:
            if doc['user'] == env['session'].user._id:
                env['session'].user.settings[doc['var']] = doc['val']
        return (results, skip_events, env, query, doc, payload)

    async def pre_update(self, skip_events, env, query, doc, payload):
        if (type(doc['val']) == list and len(doc['val']) == 1
                and type(doc['val'][0]) == dict
                and 'content' in doc['val'][0].keys()):
            doc['val'] = doc['val'][0]
        return (skip_events, env, query, doc, payload)

    async def on_update(self, results, skip_events, env, query, doc, payload):
        if query['type'][0] in ['user', 'user_sys']:
            if query['user'][0] == env['session'].user._id:
                if type(doc['val']) == dict and '$add' in doc['val'].keys():
                    env['session'].user.settings[query['var']
                                                 [0]] += doc['val']['$add']
                elif type(doc['val']
                          ) == dict and '$multiply' in doc['val'].keys():
                    env['session'].user.settings[
                        query['var'][0]] *= doc['val']['$multiply']
                elif type(
                        doc['val']) == dict and '$append' in doc['val'].keys():
                    env['session'].user.settings[query['var'][0]].append(
                        doc['val']['$append'])
                elif type(
                        doc['val']) == dict and '$remove' in doc['val'].keys():
                    env['session'].user.settings[query['var'][0]].remove(
                        doc['val']['$remove'])
                else:
                    env['session'].user.settings[query['var'][0]] = doc['val']
        return (results, skip_events, env, query, doc, payload)