Пример #1
0
 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)
Пример #2
0
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)
Пример #3
0
    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()
Пример #5
0
    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
Пример #6
0
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})
Пример #7
0
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}\""})
Пример #8
0
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))
Пример #9
0
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())
Пример #10
0
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"})
Пример #11
0
    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
Пример #12
0
    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)
Пример #13
0
    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
Пример #14
0
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())
Пример #15
0
 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"]
Пример #16
0
 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")
Пример #17
0
 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()
Пример #19
0
def get_avatar(event: HTTPEvent) -> JSONResponse:
    sub = event.params["sub"]
    avatar = BeneficiariesService.get_avatar(sub)
    return JSONResponse(avatar)
Пример #20
0
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)