async def test_validate_attr_DATE_None(): with pytest.raises(InvalidAttrException): await validate_attr( attr_name='test_validate_attr_DATE', attr_type=ATTR.DATE(), attr_val=None, mode='create', )
async def test_validate_attr_DATE_None_allow_none(): attr_val = await validate_attr( attr_name='test_validate_attr_DATE', attr_type=ATTR.DATE(), attr_val=None, mode='update', ) assert attr_val == None
async def test_validate_attr_DATE_date(): attr_val = await validate_attr( attr_name='test_validate_attr_DATE', attr_type=ATTR.DATE(), attr_val='2020-02-02', mode='create', ) assert attr_val == '2020-02-02'
async def test_validate_attr_DATE_default_int_allow_none(): attr_type = ATTR.DATE() attr_type._default = 'test_validate_attr_DATE' attr_val = await validate_attr( attr_name='test_validate_attr_DATE', attr_type=attr_type, attr_val=1, mode='update', ) assert attr_val == None
async def test_validate_attr_DATE_default_None(): attr_type = ATTR.DATE() attr_type._default = 'test_validate_attr_DATE' attr_val = await validate_attr( attr_name='test_validate_attr_DATE', attr_type=attr_type, attr_val=None, mode='create', ) assert attr_val == 'test_validate_attr_DATE'
def test_generate_attr_DATE_ranges_dynamic_start_end_negative(): attr_val = utils.generate_attr(attr_type=ATTR.DATE( ranges=[['-5d', '+52w']])) assert attr_val == (datetime.datetime.utcnow() + datetime.timedelta(days=-5)).isoformat().split('T')[0]
def test_generate_attr_DATE_ranges_datetime_start(): attr_val = utils.generate_attr(attr_type=ATTR.DATE( ranges=[['2020-10-20', '+104w']])) assert attr_val == '2020-10-20'
def test_generate_attr_DATE_ranges_datetime_end(): attr_val = utils.generate_attr(attr_type=ATTR.DATE( ranges=[['+1d', '2020-10-20']])) assert attr_val == '2020-10-19'
def test_generate_attr_DATE_no_args(): attr_val = utils.generate_attr(attr_type=ATTR.DATE()) assert attr_val == datetime.datetime.utcnow().isoformat().split('T')[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.' ), 'date': ATTR.DATE( desc= 'Analytics event date. This allows clustering of events occupancies to limit doc size.' ), 'occurrences': ATTR.LIST( desc='All occurrences of the event as list.', list=[ ATTR.TYPED_DICT( desc='Single occurrence of the event details.', dict={ 'args': ATTR.KV_DICT( desc= 'Key-value `dict` containing event args, if any.', 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', 'date')] methods = { 'read': METHOD(permissions=[PERM(privilege='read')]), 'create': METHOD( permissions=[PERM(privilege='__sys')], doc_args={ 'event': ATTR.STR(), 'subevent': ATTR.ANY(), 'args': ATTR.KV_DICT(key=ATTR.STR(), val=ATTR.ANY()), }, ), 'update': METHOD(permissions=[PERM(privilege='__sys')]), 'delete': METHOD(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'], 'date': datetime.date.today().isoformat(), }, { '$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 }, }, ) if analytic_results.status == 200: return (skip_events, env, query, doc, { '__results': analytic_results }) else: raise self.exception( status=analytic_results.status, msg=analytic_results.msg, args=analytic_results.args, ) else: doc = { 'event': doc['event'], 'subevent': doc['subevent'], 'date': datetime.date.today().isoformat(), '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)