def normalize(tag: List[str], short=False): parent_tag_full = LogTag.from_tag(tag, short=False) parent_tag = LogTag.from_tag( tag, short=True) if parent_tag_full is None else parent_tag_full if parent_tag is None: raise InvalidException(f'Tag {join_key(*tag)} does not exist') source_is_short = parent_tag_full is None tag_body = tag[ 1 if source_is_short else len(split_key(parent_tag.value)):] if not short: return join_key(parent_tag.value, *tag_body) else: return join_key(parent_tag.short, *tag_body)
def from_tag(tag: List[str], short=False): tag = join_key(*tag) for member in LogTag: value = member.short if short else member.value if len(tag) >= len(value) and value == tag[:len(value)]: return member return None
def start_task(cls, authorizer: Authorizer, stage: str, area: str, subline: str, tasks: List[str], description: str): from core.services.beneficiaries import BeneficiariesService line_, subline_ = subline.split('.') objective = ObjectivesService.get(stage, area, int(line_), int(subline_)) now = datetime.now(timezone.utc) task = Task(created=int(now.timestamp() * 1000), completed=False, objective_key=join_key(stage, area, subline), original_objective=objective, personal_objective=description, tasks=[ Subtask(completed=False, description=description) for description in tasks ]) try: BeneficiariesService.update(authorizer, active_task=task.to_db_dict()) except BeneficiariesService.exceptions( ).ConditionalCheckFailedException: return None return task
def get(cls, sub: str, stage: str, area: str, line: int, subline: int) -> Task: interface = cls.get_interface() item = interface.get(sub, join_key(stage, area, f"{line}.{subline}")).item if item is None: raise NotFoundException('Task not found') return Task.from_db_dict(item)
def add_to_scout_group(cls, username: str, district: str, group: str, current_groups: List[str]): client = cls.get_client() new_group = join_key(district, group) if new_group in current_groups: return client.admin_update_user_attributes( UserPoolId=cls.__user_pool_id__, Username=username, UserAttributes=[ { 'Name': 'custom:groups', 'Value': ','.join(current_groups + [join_key(district, group)]) }, ], )
def to_api_map(self): tag: str = self.tag.name if isinstance(self.tag, LogTag) else self.tag long_tag: str = tag if not self.append_timestamp else join_key( tag, self.timestamp) data = { "tag": self.parent_tag.normalize(split_key(long_tag), short=True), "user": self.sub, "log": self.log, 'timestamp': self.timestamp } if self.data is not None: data['data'] = self.data return data
def query(cls, sub: str, stage: str = None, area: str = None): interface = cls.get_interface() args = [arg for arg in (stage, area) if arg is not None] sort_key = (Operator.BEGINS_WITH, join_key(*args, '')) if len(args) > 0 else None return QueryResult.from_list([ Task.from_db_dict(item) for item in interface.query( partition_key=sub, sort_key=sort_key, attributes=[ 'objective', 'original-objective', 'personal-objective', 'completed', 'tasks', 'user' ]).items ])
def get_user_active_task(event: HTTPEvent) -> JSONResponse: sub = event.params['sub'] result = TasksService.get_active_task(sub) beneficiary_authorizer = event.authorizer if event.authorizer is not None and event.authorizer.sub == sub else None d = result.to_api_dict(authorizer=beneficiary_authorizer) if beneficiary_authorizer is not None: # generate log reward claim token last_task_log = LogsService.get_last_log_with_tag( sub, tag=join_key(LogTag.PROGRESS.value, result.objective_key).upper()) d['eligible_for_progress_reward'] = last_task_log is None or int( time.time() * 1000) - last_task_log.timestamp > 24 * 60 * 60 * 1000 return JSONResponse(d)
def create(cls, sub: str, tag: str, log_text: str, data: Any, append_timestamp_to_tag: bool = False) -> Log: log = Log(tag=tag.upper(), log=log_text, data=data, timestamp=cls._get_current_timestamp(), sub=sub, append_timestamp=append_timestamp_to_tag) cls.get_interface().create( sub, log.to_db_map(), join_key(log.tag, log.timestamp) if log.append_timestamp else log.tag) return log
def to_db_map(self): tag = self.tag.name if isinstance(self.tag, LogTag) else self.tag data = { "tag": tag if not self.append_timestamp else join_key( tag, self.timestamp), 'user': self.sub, 'log': self.log, 'timestamp': self.timestamp } if self.data is not None: data['data'] = self.data return data
def _add_objectives_as_completed(cls, authorizer: Authorizer, objectives: List[ObjectiveKey]): # noinspection PyProtectedMember model = cls.get_interface()._model client = model.get_table().meta.client now = datetime.now(timezone.utc) now = int(now.timestamp() * 1000) n_chunks = math.ceil(len(objectives) / 25) # do batch writes in chunks of 25 to avoid errors for i_chunk in range(n_chunks): start = i_chunk * 25 end = min((i_chunk + 1) * 25, len(objectives)) chunk = objectives[start:end] request_items = { model.__table_name__: [{ 'PutRequest': { 'Item': { 'completed': True, 'created': now, 'objective': join_key(authorizer.stage, key.area, f'{key.line}.{key.subline}'), 'original-objective': ObjectivesService.get(authorizer.stage, key.area, key.line, key.subline), 'personal-objective': None, 'score': 0, 'tasks': [], 'user': authorizer.sub }, } } for key in chunk] } client.batch_write_item(RequestItems=request_items)
def generate_beneficiary_code(district: str, group_code: str): h = hashlib.sha1(join_key(district, group_code).encode()).hexdigest() int_hash = (int(h, 16) + random.randint(0, 1024)) % (10**8) return f'{int_hash:08}'
def join(self, body: str): return join_key(self.value, body)
def concat(parent_tag: str, *args): return join_key(parent_tag, *args)
class LogTag(Enum): REWARD = 'REWARD' PROGRESS = join_key('STATS', 'PROGRESS') COMPLETED = join_key('STATS', 'COMPLETED') @staticmethod def concat(parent_tag: str, *args): return join_key(parent_tag, *args) def join(self, body: str): return join_key(self.value, body) @property def short(self): return split_key(self.value)[-1] @staticmethod def from_value(value: str): for member in LogTag: if value == member.value: return member return None @staticmethod def from_short(value: str): for member in LogTag: if value == member.short: return member return None @staticmethod def from_tag(tag: List[str], short=False): tag = join_key(*tag) for member in LogTag: value = member.short if short else member.value if len(tag) >= len(value) and value == tag[:len(value)]: return member return None @staticmethod def get_parent_tag(tag: List[str]) -> Optional[Enum]: parent_tag_full = LogTag.from_tag(tag, short=False) return LogTag.from_tag( tag, short=True) if parent_tag_full is None else parent_tag_full @staticmethod def normalize(tag: List[str], short=False): parent_tag_full = LogTag.from_tag(tag, short=False) parent_tag = LogTag.from_tag( tag, short=True) if parent_tag_full is None else parent_tag_full if parent_tag is None: raise InvalidException(f'Tag {join_key(*tag)} does not exist') source_is_short = parent_tag_full is None tag_body = tag[ 1 if source_is_short else len(split_key(parent_tag.value)):] if not short: return join_key(parent_tag.value, *tag_body) else: return join_key(parent_tag.short, *tag_body) @staticmethod def shorten(tag: List[str]): return LogTag.normalize(tag, short=True)
def generate_scouters_code(district: str, group_code: str): return hashlib.sha1(join_key(district, group_code).encode()).hexdigest()
def get_user_rewards(cls, authorizer: Authorizer, category: RewardType): tag = join_key(LogTag.REWARD.name, category.name) return LogsService.query(authorizer.sub, tag, limit=None)
def claim_reward(cls, authorizer: Authorizer, reward_token: str, release: int, box_index: int = None) -> \ List[Reward]: from core.services.beneficiaries import BeneficiariesService jwk_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'jwk.json') with open(jwk_path, 'r') as f: jwk = jwt.jwk_from_dict(json.load(f)) decoder = jwt.JWT() try: decoded = decoder.decode(reward_token, jwk) except JWTDecodeError as e: raise InvalidException(f'Invalid token: {e.args}') now = get_int_from_datetime(datetime.now()) if now > decoded["exp"]: raise ForbiddenException("The reward token has expired") if authorizer.sub != decoded["sub"]: raise ForbiddenException( "This token does not belong to the claimer") BeneficiariesService.set_reward_index(authorizer, decoded['index']) boxes = decoded["boxes"] probabilities: List[RewardProbability] = [ RewardProbability.from_map(reward) for reward in decoded["static"] ] if len(boxes) > 0: if box_index is None: raise InvalidException("A box must be chosen") if box_index >= len(boxes): raise InvalidException( f"Box index out of range, it must be between 0 (inclusive) and {len(boxes)} (exclusive)" ) probabilities += [ RewardProbability.from_map(reward) for reward in boxes[box_index] ] rewards = [ RewardsService.get_random(probability.type, release, probability.rarity) for probability in probabilities ] LogsService.batch_create(logs=[ Log(sub=authorizer.sub, tag=join_key(LogTag.REWARD.name, rewards[reward_i].type.name, rewards[reward_i].id), log='Won a reward', data=rewards[reward_i].to_api_map(), append_timestamp=rewards[reward_i].type != RewardType.AVATAR) for reward_i in range(len(rewards)) ]) area: Optional[str] = decoded.get('area') areas: List[str] = VALID_AREAS if area is None else [area] scores = {} for r in rewards: if r.type != RewardType.POINTS: continue total_score = r.description['amount'] for a in areas: scores[a] = scores.get(a, 0) + (total_score / len(areas)) for key in scores: scores[key] = math.ceil(scores[key]) BeneficiariesService.add_score(authorizer.sub, scores) return rewards