def initialize(cls, authorizer: Authorizer, objectives: List[ObjectiveKey]): from core.services.beneficiaries import BeneficiariesService cls._add_objectives_as_completed(authorizer, objectives) BeneficiariesService.mark_as_initialized(authorizer=authorizer) return RewardsFactory.get_reward_token_by_reason( authorizer=authorizer, reason=RewardReason.INITIALIZE, area=None)
def buy_item(event: HTTPEvent): category = event.params['category'] try: release = int(event.params['release']) except ValueError: return NotFoundException( f"Unknown release {event.params['release']}, it should be an int") try: id_ = int(event.params['id']) except ValueError: raise NotFoundException( f"Unknown id {event.params['id']}, it should be an int") area = event.params['area'] if area not in VALID_AREAS: raise NotFoundException(f"Area {area} does not exist") amount = event.json.get('amount', 1) if type(amount) is not int: raise InvalidException(f"The amount to be bought must be an integer") if amount < 1: raise InvalidException(f"The amount must be one or more") try: result = BeneficiariesService.buy_item(event.authorizer, area, category, release, id_, amount) except BeneficiariesService.exceptions().ConditionalCheckFailedException: raise ForbiddenException( f"You don't have enough {area} score to buy this item") if not result: return NotFoundException(f"Item not found") return JSONResponse(result)
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 ddb_stubber(): # noinspection PyProtectedMember ddb_stubber = Stubber( BeneficiariesService.get_interface()._model.get_table().meta.client) ddb_stubber.activate() yield ddb_stubber ddb_stubber.deactivate()
def get_active_task(cls, sub: str) -> Optional[Task]: from core.services.beneficiaries import BeneficiariesService beneficiary = BeneficiariesService.get(sub, ["target"]) target = beneficiary.target if target is None: raise NotFoundException('Task not found') return target
def update_avatar(event: HTTPEvent) -> JSONResponse: sub = event.params["sub"] if event.authorizer.sub != sub: return JSONResponse.generate_error( HTTPError.FORBIDDEN, "Only the avatar owner can update the avatar") new_avatar = BeneficiariesService.update_avatar(event.authorizer.sub, event.json) return JSONResponse({'avatar': new_avatar})
def join_group(event: HTTPEvent): district = event.params["district"] group = event.params["group"] code = event.json["code"] if not event.authorizer.is_beneficiary: if event.authorizer.is_scouter: raise ForbiddenException( 'Scouters accounts can\'t be migrated to a beneficiary account' ) UsersCognito.add_to_group(event.authorizer.username, "Beneficiaries") group_item = GroupsService.get(district, group, ["beneficiary_code"]).item if group_item is None: raise NotFoundException("Group not found") if group_item["beneficiary_code"] != code: raise ForbiddenException("Wrong code") BeneficiariesService.create(district, group, event.authorizer) return JSONResponse({"message": f"Joined group \"{group}\""})
def get_beneficiary(event: HTTPEvent): result = BeneficiariesService.get(event.params["sub"]) if result is None: return JSONResponse.generate_error(HTTPError.NOT_FOUND, "This user does not have a beneficiaries assigned") has_full_access = event.authorizer is not None and ( event.authorizer.is_scouter or event.authorizer.sub == event.params["sub"] ) return JSONResponse(result.to_api_dict(full=has_full_access))
def list_beneficiaries_unit(event: HTTPEvent): district = event.params["district"] group = event.params["group"] unit = event.params["unit"] if unit not in VALID_UNITS: return JSONResponse.generate_error(HTTPError.NOT_FOUND, f"Unknown unit: {unit}") result = BeneficiariesService.query_unit(district, group, unit) result.items = [item.to_api_dict() for item in result.items] return JSONResponse(result.as_dict())
def update_beneficiary(event: HTTPEvent): if event.authorizer.sub != event.params["sub"]: return JSONResponse.generate_error(HTTPError.FORBIDDEN, "You can not access data from this beneficiary") result = BeneficiariesService.update(event.authorizer, profile_picture=event.json.get('profile_picture'), nickname=event.json.get('nickname')) if result is None: return JSONResponse.generate_error(HTTPError.NOT_FOUND, "This user does not have a beneficiaries assigned") return JSONResponse({"message": "Updated successfully"})
def complete_active_task(cls, authorizer: Authorizer): from core.services.beneficiaries import BeneficiariesService old_active_task = BeneficiariesService.clear_active_task( authorizer, return_values=UpdateReturnValues.UPDATED_OLD, receive_score=True).get("target") if old_active_task is None: return None interface = cls.get_interface() old_active_task['completed'] = True for subtask in old_active_task['tasks']: subtask['completed'] = True interface.create(authorizer.sub, old_active_task, old_active_task['objective']) return old_active_task
def generate_reward_token(cls, authorizer: Authorizer, static: RewardSet = None, area: Optional[str] = None, boxes: List[RewardSet] = None, duration: timedelta = None, reason: Enum = None) -> str: from core.services.beneficiaries import BeneficiariesService if duration is None: duration = timedelta(days=7) 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)) encoder = jwt.JWT() now = datetime.now(timezone.utc) token_index = int(BeneficiariesService.add_token_index(authorizer)) payload = { "sub": authorizer.sub, "iat": get_int_from_datetime(now), "exp": get_int_from_datetime(now + duration), "static": static.to_map_list() if static is not None else [], "boxes": [box.to_map_list() for box in boxes] if boxes is not None else [], "index": token_index, "area": area, "reason": None if reason is None else reason.value } return encoder.encode(payload, jwk)
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
def list_beneficiaries_group(event: HTTPEvent): district = event.params["district"] group = event.params["group"] beneficiaries = BeneficiariesService.query_group(district, group) return JSONResponse(QueryResult.from_list([b.to_api_dict(full=False) for b in beneficiaries]).as_dict())
def update_active_task(cls, authorizer: Authorizer, description: str, tasks: List[str]) -> Union[Task, None]: from core.services.beneficiaries import BeneficiariesService return BeneficiariesService.update_active_task(authorizer, description, tasks)["target"]
def dismiss_active_task(cls, authorizer: Authorizer): from core.services.beneficiaries import BeneficiariesService return BeneficiariesService.clear_active_task( authorizer, return_values=UpdateReturnValues.UPDATED_OLD).get("target")
def stage(self): from core.services.beneficiaries import BeneficiariesService return BeneficiariesService.calculate_stage(self.birth_date)
def test_query_unit(ddb_stubber: Stubber): params = { 'IndexName': 'ByGroup', 'KeyConditionExpression': Key('group').eq('district::group') & Key('unit-user').begins_with('scouts::'), 'TableName': 'beneficiaries' } response = { 'Items': [{ 'user': { 'S': 'abcABC1234' }, 'group': { 'S': 'district::group' }, 'unit-user': { 'S': 'unit::abcABC1234' }, 'full-name': { 'S': 'Name' }, 'nickname': { 'S': 'Name' }, 'birthdate': { 'S': '01-01-2001' }, 'score': { 'M': {} }, 'n_tasks': { 'M': {} }, 'bought_items': { 'M': {} }, 'set_base_tasks': { 'BOOL': False }, }, { 'user': { 'S': 'abcABC12345' }, 'group': { 'S': 'district::group' }, 'unit-user': { 'S': 'unit::abcABC12345' }, 'full-name': { 'S': 'Name' }, 'nickname': { 'S': 'Name' }, 'birthdate': { 'S': '01-01-2001' }, 'score': { 'M': {} }, 'n_tasks': { 'M': {} }, 'bought_items': { 'M': {} }, 'set_base_tasks': { 'BOOL': False }, }] } ddb_stubber.add_response('query', response, params) BeneficiariesService.query_unit('district', 'group', 'scouts') ddb_stubber.assert_no_pending_responses()
def get_avatar(event: HTTPEvent) -> JSONResponse: sub = event.params["sub"] avatar = BeneficiariesService.get_avatar(sub) return JSONResponse(avatar)
def get_group_stats(event: HTTPEvent): district_code = event.params["district"] code = event.params["group"] response = GroupsService.get(district_code, code, attributes=["scouters"]) if response.item is None: return JSONResponse.generate_error(HTTPError.NOT_FOUND, f"Group '{code}' was not found") unit: str = event.queryParams.get('unit') if unit is not None: unit = str(unit).lower() if unit not in VALID_UNITS: unit = None stats = {} beneficiaries = BeneficiariesService \ .query_group(district_code, code, attributes=['user', 'unit-user']) if unit is None else \ BeneficiariesService.query_unit(district_code, code, unit, attributes=['user', 'unit-user']) logs: List[Log] = [] progress_logs: Dict[str, Dict[str, str]] = {} complete_logs: Dict[str, Dict[str, str]] = {} units = {} for beneficiary in beneficiaries: sub = beneficiary.user_sub units[sub] = beneficiary.unit beneficiary_logs = LogsService.query_stats_tags(user=sub) logs += beneficiary_logs progress_logs[sub] = [ log for log in beneficiary_logs if log.parent_tag == LogTag.PROGRESS ] complete_logs[sub] = [ log for log in beneficiary_logs if log.parent_tag == LogTag.COMPLETED ] stats['log_count'] = { tag.short: len([log for log in logs if log.parent_tag == tag]) for tag in LogTag } stats['completed_objectives'] = { sub: [{ 'stage': log.tags[2], 'area': log.tags[3], 'unit': units[sub], 'line': split_line(log.tags[4])[0], 'subline': split_line(log.tags[4])[1], 'timestamp': log.timestamp } for log in logs] for sub, logs in complete_logs.items() } stats['progress_logs'] = { sub: [{ 'stage': log.tags[2], 'area': log.tags[3], 'unit': units[sub], 'line': split_line(log.tags[4])[0], 'subline': split_line(log.tags[4])[1], 'timestamp': log.timestamp, 'log': log.log if event.authorizer.sub in response.item['scouters'].keys() else None } for log in logs] for sub, logs in progress_logs.items() } return JSONResponse(stats)