Exemple #1
0
 def json(self):
     try:
         return json.loads(self.body)
     except JSONDecodeError:
         raise InvalidException('Body isn\'t a valid JSON data')
     except TypeError:
         raise InvalidException('Body isn\'t a valid JSON data')
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)
Exemple #3
0
def create_group(event: HTTPEvent):
    if not event.authorizer.is_admin:
        raise ForbiddenException("Only an admin can access this endpoint")
    district = event.params["district"]
    item = event.json
    code = item['code']
    try:
        del item['code']
        GroupsService.create(district, code, item, event.authorizer.sub,
                             event.authorizer.full_name)
    except GroupsService.exceptions().ConditionalCheckFailedException:
        raise InvalidException(
            f"Group in district {district} with code {code} already exists")
    except SchemaError as e:
        raise InvalidException(f"Item content is invalid: \"{e.code}\"")
    return JSONResponse({"message": "OK"})
Exemple #4
0
    def get_random(cls, category: RewardType, release: int,
                   rarity: RewardRarity):
        reward = Reward.factory(category, rarity)
        if reward is not None:
            return reward
        release = int(release)
        if release < 1:
            raise InvalidException('Release must be positive and non-zero')

        top = abs(release * REWARDS_PER_RELEASE - 1)
        if rarity == RewardRarity.RARE:
            top = -top

        lowest = min(0, top)
        highest = max(0, top)
        random_point = random.randint(lowest, highest)

        index = cls.get_interface()
        result = index.query(
            category.name, (Operator.BETWEEN, lowest, random_point),
            attributes=['category', 'description', 'release-id', 'price'],
            limit=1)
        if len(result.items) == 0:
            result = index.query(
                category.name, (Operator.BETWEEN, random_point, highest),
                attributes=['category', 'description', 'release-id', 'price'],
                limit=1)
        if len(result.items) == 0:
            raise NotFoundException(f'No reward of type {category.name} found')
        return Reward.from_db_map(result.items[0])
 def set_reward_index(cls, authorizer: Authorizer, index: int):
     interface = cls.get_interface()
     updates = {'n_claimed_tokens': index}
     conditions = "#attr_n_claimed_tokens < :val_n_claimed_tokens"
     try:
         return interface.update(authorizer.sub, updates, None, conditions=conditions)
     except interface.client.exceptions.ConditionalCheckFailedException:
         raise InvalidException('This token has already been claimed')
Exemple #6
0
def create_log(event: HTTPEvent):
    user_sub: str = event.params['sub']
    tag: str = event.params['tag'].upper()

    if event.authorizer.sub != user_sub:
        raise ForbiddenException("Only the same user can create logs")
    parent_tag = LogTag.from_short(split_key(tag)[0])
    if parent_tag not in USER_VALID_TAGS:
        raise ForbiddenException(f"A user can only create logs with the following tags: {USER_VALID_TAGS}")

    body = event.json

    log = body['log']
    data = body.get('data')

    if len(body) > 1024:
        raise InvalidException(f"A log can't have more than 1024 characters")

    if data is not None:
        if len(json.dumps(data)) > 2048:
            raise InvalidException(f"Log data is too big")

    response_body = {}

    if parent_tag == LogTag.PROGRESS:
        if tag != parent_tag.short:
            raise InvalidException(f"A progress log tag can't be compound")
        if body.get('token') is None:
            raise InvalidException(f"To post a PROGRESS log you must provide the task token")

        objective = TasksService.get_task_token_objective(body['token'], authorizer=event.authorizer)
        tag = join_key(LogTag.PROGRESS.value, objective).upper()

        now = int(datetime.now(timezone.utc).timestamp() * 1000)
        last_progress_log = LogsService.get_last_log_with_tag(event.authorizer.sub, tag.upper())

        if last_progress_log is None or now - last_progress_log.timestamp > 24 * 60 * 60 * 1000:
            response_body['token'] = RewardsFactory.get_reward_token_by_reason(authorizer=event.authorizer,
                                                                               area=split_key(objective)[1],
                                                                               reason=RewardReason.PROGRESS_LOG)

    log = LogsService.create(user_sub, tag, log_text=log, data=body.get('data'), append_timestamp_to_tag=True)
    response_body['item'] = log.to_api_map()

    return JSONResponse(body=response_body)
def claim_reward(event: HTTPEvent):
    body = event.json
    token = body.get('token')
    if token is None:
        raise InvalidException('No reward token given')
    box_index = body.get('box_index')
    if box_index is not None:
        try:
            box_index = int(box_index)
        except ValueError:
            raise InvalidException('Box index must be an int')
    rewards = RewardsService.claim_reward(event.authorizer,
                                          reward_token=token,
                                          box_index=box_index,
                                          release=1)
    return JSONResponse({
        'message': 'Claimed rewards!',
        'rewards': [reward.to_api_map() for reward in rewards]
    })
Exemple #8
0
def query_logs(event: HTTPEvent):
    user_sub = event.params['sub']
    tag = LogTag.normalize(split_key(event.params['tag'])).upper() if event.params.get('tag') else None

    limit = event.queryParams.get('limit', 25)
    if not isinstance(limit, int) or limit > 100:
        raise InvalidException("Limit must be an integer and lower or equal than 100")

    logs = LogsService.query(user_sub, tag, limit=limit)
    return JSONResponse(body=QueryResult.from_list([log.to_api_map() for log in logs]).as_dict())
Exemple #9
0
 def from_api_map(cls, item: dict):
     try:
         return Reward(category=RewardType.from_value(item['category']),
                       release=int(item['release']),
                       id_=item.get('id'),
                       description=item['description'],
                       rarity=RewardRarity.from_name(item['rarity']),
                       price=item.get('price'))
     except KeyError as e:
         raise InvalidException(f'Missing key: {e.args}')
 def mark_as_initialized(cls, authorizer: Authorizer):
     interface = cls.get_interface()
     updates = {
         'set_base_tasks': True,
     }
     try:
         return interface.update(authorizer.sub, updates,
                                 return_values=UpdateReturnValues.UPDATED_NEW,
                                 conditions=Attr('set_base_tasks').eq(False))
     except interface.client.exceptions.ConditionalCheckFailedException:
         raise InvalidException('Beneficiary already initialized')
Exemple #11
0
    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)
Exemple #12
0
 def join_as_scouter(cls, authorizer: Authorizer, district: str, group: str,
                     code: str):
     interface = cls.get_interface()
     try:
         interface.update(district, {
             'scouters.' + authorizer.sub: {
                 'name': authorizer.full_name,
                 'role': 'scouter'
             }
         },
                          group,
                          condition_equals={'scouters_code': code})
     except cls.exceptions().ConditionalCheckFailedException:
         raise InvalidException('Wrong scouters code')
     UsersCognito.add_to_scout_group(authorizer.username, district, group,
                                     authorizer.scout_groups)
    def buy_item(cls, authorizer: Authorizer, area: str, item_category: str, item_release: int, item_id: int,
                 amount: int = 1):
        interface = cls.get_interface()
        item = RewardsService.get(item_category, item_release, item_id).item
        if item is None:
            return False

        price = item.get('price')
        if price is None:
            raise InvalidException('This reward cannot be bought')

        release_id = item_release * 100000 + item_id
        return interface.update(authorizer.sub, None, None, add_to={
            f'bought_items.{item_category}{release_id}': amount,
            f'score.{area}': int(-amount * price)
        }, conditions=Attr(f'score.{area}').gte(int(amount * price)), return_values=UpdateReturnValues.UPDATED_NEW)[
            'Attributes']
Exemple #14
0
 def get_task_token_objective(cls, token: str,
                              authorizer: Authorizer) -> str:
     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))
     decoded = jwt.JWT().decode(token, jwk)
     try:
         Schema({
             "sub": str,
             "objective": str,
             "exp": int,
             "iat": int
         }).validate(decoded)
     except SchemaError:
         raise InvalidException("The given task token is not valid")
     if authorizer.sub != decoded['sub']:
         raise ForbiddenException(
             "The given task token does not belong to this user")
     return decoded['objective']
    def create(cls, district: str, group: str, authorizer: Authorizer):
        interface = cls.get_interface()

        beneficiary = Beneficiary(
            user_sub=authorizer.sub,
            district=district,
            group=group,
            unit=authorizer.unit,
            full_name=authorizer.full_name,
            nickname=authorizer.nickname,
            birthdate=authorizer.birth_date,
            target=None,
            set_base_tasks=False,
            score={area: 0 for area in VALID_AREAS},
            n_tasks={area: 0 for area in VALID_AREAS},
            bought_items={}
        )

        try:
            interface.create(authorizer.sub, beneficiary.to_db_dict(),
                             raise_if_exists_partition=True)
        except cls.exceptions().ConditionalCheckFailedException:
            raise InvalidException("Already joined a group")
    def clear_active_task(cls, authorizer: Authorizer,
                          return_values: UpdateReturnValues = UpdateReturnValues.UPDATED_OLD,
                          receive_score=False
                          ):
        interface = cls.get_interface()
        updates = {'target': None}
        add_to = None
        if receive_score:
            beneficiary = BeneficiariesService.get(authorizer.sub, ["target"])

            if beneficiary.target is None:
                return None
            score = ScoreConfiguration.instance().base_score
            area = split_key(beneficiary.target.objective_key)[1]
            add_to = {
                f'score.{area}': score,
                f'n_tasks.{area}': 1
            }

        try:
            return interface.update(authorizer.sub, updates, None, return_values=return_values,
                                    add_to=add_to, conditions=Attr('target').ne(None))["Attributes"]
        except interface.client.exceptions.ConditionalCheckFailedException:
            raise InvalidException('No active target')
Exemple #17
0
 def from_value(value: str):
     for member in RewardType:
         if value.upper() == member.value:
             return member
     raise InvalidException(f"Unknown reward type: {value}")
Exemple #18
0
 def sub(self):
     sub = self.attributes.get('sub')
     if sub is None:
         raise InvalidException('Can\'t get user ID')
     return sub
Exemple #19
0
 def from_name(name: str):
     for member in RewardRarity:
         if name.upper() == member.name:
             return member
     raise InvalidException(f"Unknown reward rarity: {name}")
Exemple #20
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