Beispiel #1
0
 def test_number_serialize(self):
     """
     NumberAttribute.serialize
     """
     attr = NumberAttribute()
     self.assertEqual(attr.serialize(3.141), '3.141')
     self.assertEqual(attr.serialize(1), '1')
Beispiel #2
0
 def test_number_deserialize(self):
     """
     NumberAttribute.deserialize
     """
     attr = NumberAttribute()
     self.assertEqual(attr.deserialize('1'), 1)
     self.assertEqual(attr.deserialize('3.141'), 3.141)
Beispiel #3
0
 def test_number_deserialize(self):
     """
     NumberAttribute.deserialize
     """
     attr = NumberAttribute()
     assert attr.deserialize('1') == 1
     assert attr.deserialize('3.141') == 3.141
     assert attr.deserialize('12345678909876543211234234324234') == 12345678909876543211234234324234
Beispiel #4
0
 def test_number_serialize(self):
     """
     NumberAttribute.serialize
     """
     attr = NumberAttribute()
     assert attr.serialize(3.141) == '3.141'
     assert attr.serialize(1) == '1'
     assert attr.serialize(12345678909876543211234234324234) == '12345678909876543211234234324234'
Beispiel #5
0
class MeasuredDuration(Model):
    class Meta:
        table_name = 'aws-scheduler-testing'

    id = UnicodeAttribute(hash_key=True)
    delay = NumberAttribute()
Beispiel #6
0
class Product(Model):
    class Meta:
        table_name = "product"
        region = 'ap-northeast-2'

    id = UnicodeAttribute(hash_key=True)
    do_crawl = BooleanAttribute(default=False)
    created_at = UnicodeAttribute()
    last_crawled_at = UTCDateTimeAttribute(null=True)
    min_price = NumberAttribute(default=0)  # min_price 가격 이상인 결과만 사용

    queries = ListAttribute(of=ShoppingQuery)

    lprice = NumberAttribute(null=True)  # 현재까지 최저 가격
    lprice_item = MapAttribute(null=True)  # 최저가격의 item 정보

    def update_lprice(self, lprice_item):
        self.last_crawled_at = datetime.datetime.utcnow()

        if lprice_item:
            self.lprice_item = lprice_item
            self.lprice = int(lprice_item['lprice'])

            record = PriceRecord(self.id, self.last_crawled_at)
            record.item = lprice_item
            record.save()

        return self.save()

    def update_last_crawled_at(self):
        self.last_crawled_at = datetime.datetime.utcnow()
        return self.save()

    def search_lowest_price(self):
        # 최저가를 찾고, 최저가 발견시 알림
        min_price_criterion = self.min_price

        lprice = self.lprice or 100000000
        lprice_item = None

        try:
            for q in self.queries:
                api = naver_shopping_api.format(quote(q.query), q.display
                                                or DEFAULT_DISPLAY, q.sort
                                                or DEFAULT_SORT)

                api_response = requests.get(api, headers=naver_api_header)
                if api_response.status_code == 200:
                    response_json = api_response.json()

                    if not response_json.get('total', 0):
                        # TODO: no item warning noti
                        pass

                    for item in response_json.get('items', {}):
                        item_lprice = int(item['lprice'])
                        if min_price_criterion <= item_lprice < lprice:
                            lprice = item_lprice
                            lprice_item = item
                            lprice_item['query'] = q.attribute_values
                else:
                    error_message = {
                        'message': 'Naver API Error for {}'.format(self.id),
                        'status_code': api_response.status_code,
                        'text': api_response.text,
                        'api': api,
                        'query': q.attribute_values
                    }
                    send_slack_notification(
                        build_naver_warning_slack_message(error_message))
        except Exception as e:
            error_message = {
                'message': 'Exception occurs by {}'.format(self.id),
                'exception': e,
                'traceback': traceback.format_exc()
            }
            send_slack_notification(build_error_slack_message(error_message))

        self.update_lprice(lprice_item)

        if lprice_item:
            # print(build_normal_slack_message(self))
            status_code = send_slack_notification(
                build_normal_slack_message(self))

        return lprice_item
Beispiel #7
0
class CustomAttrMap(MapAttribute):
    overridden_number_attr = NumberAttribute(attr_name="number_attr")
    overridden_unicode_attr = UnicodeAttribute(attr_name="unicode_attr")
Beispiel #8
0
 class SubModel(Model):
     key = NumberAttribute(hash_key=True)
     sub_map = SubMapAttribute(attr_name='dyn_sub_map')
Beispiel #9
0
    class MyModel(Model):
        class Meta:
            table_name = 'some-table'

        notmyid = UnicodeAttribute(null=True)
        myid = NumberAttribute(hash_key=True)
class DifferentRegion(Model):
    class Meta:
        region = 'us-east-2'
        table_name = 'different-region'

    entry_index = NumberAttribute(hash_key=True)
Beispiel #11
0
 class SomeModel(Model):
     key = NumberAttribute(hash_key=True)
     typed_map = TypedMap()
def test_should_number_convert_int():
    assert_attribute_conversion(NumberAttribute(), graphene.Int)
class User(Model):
    class Meta:
        region = 'us-east-1'
        table_name = 'user'

    user_id = NumberAttribute(hash_key=True)
Beispiel #14
0
class Position(MapAttribute):
    lat = NumberAttribute(null=False)
    lng = NumberAttribute(null=False)
Beispiel #15
0
class PetLocation(MapAttribute):
    position = Position(null=False)
    at = NumberAttribute(null=False,
                         default=lambda: datetime.timestamp(datetime.now()))
Beispiel #16
0
class Zone(MapAttribute):
    position = Position(null=False)
    radius = NumberAttribute(null=False)
Beispiel #17
0
class FoodAttribute(MapAttribute):
    food_name = UnicodeAttribute(null=False)
    food_calorie = NumberAttribute(null=False)
Beispiel #18
0
class CoordinateMap(MapAttribute):
    lat = NumberAttribute()
    lon = NumberAttribute()
Beispiel #19
0
class MotionAttribute(MapAttribute):
    motion_name = UnicodeAttribute(null=False)
    motion_calorie = NumberAttribute(null=False)
Beispiel #20
0
class ResourceUpdatedAtIndex(LocalSecondaryIndex):
    class Meta:
        projection = AllProjection()

    site = UnicodeAttribute(hash_key=True)
    updated_at = NumberAttribute(range_key=True)
Beispiel #21
0
 class ThingModel(Model):
     key = NumberAttribute(hash_key=True)
     nested = NestedThing()
Beispiel #22
0
class Location(MapAttribute):
    lat = NumberAttribute(null=False)
    lon = NumberAttribute(null=False)
Beispiel #23
0
 class MyModel(Model):
     key = NumberAttribute(hash_key=True)
     outer_map = OuterMapAttribute(attr_name='dyn_out_map')
Beispiel #24
0
class Note(MapAttribute):
    content = UnicodeAttribute(null=True)
    created_at = NumberAttribute(attr_name="createdAt",
                                 default=int(datetime.now().strftime("%s")))
    created_by = UnicodeAttribute(attr_name="createdBy")
Beispiel #25
0
class ShoppingQuery(MapAttribute):
    # ref: https://developers.naver.com/docs/search/shopping/
    query = UnicodeAttribute()
    display = NumberAttribute(default=50)
    sort = UnicodeAttribute(default='sim')
Beispiel #26
0
class TwitchVod(Model):
    """A Twitch VOD link associated with a replay and mapped to a DynamoDB table"""

    twitch_channel_name = UnicodeAttribute(
        hash_key=True)  # The name of the Twitch channel
    friendly_player_name = UnicodeAttribute(
    )  # The BattleTag for the friendly player
    hsreplaynet_user_id = NumberAttribute(
    )  # The HSReplay.net user id of the uploader
    replay_shortid = UnicodeAttribute()  # The replay's shortid
    rank = NumberAttribute()  # The friendly player's rank

    # The friendly player's legend rank, when normal rank is 0, NULL otherwise

    legend_rank = NumberAttribute(null=True)

    # A synthetic key composing legend and normal rank to use as a DynamoDB range key

    combined_rank = UnicodeAttribute(range_key=True)

    won = BooleanAttribute(
    )  # True if the friendly player won the game, else False
    went_first = BooleanAttribute(
    )  # True if the friendly player went first, else False

    game_date = NumberAttribute(
    )  # The epoch second timestamp of the match start
    game_length_seconds = NumberAttribute(
    )  # The duration of the game in seconds

    format_type = UnicodeAttribute(
    )  # The format of the game (as a FormatType enum name)
    game_type = UnicodeAttribute(
    )  # The game type (as a BnetGameType enum name)

    # The full, canonicalized deck string for the friendly player
    friendly_player_canonical_deck_string = UnicodeAttribute()

    # The archetype id for the friendly player's deck, if available, else NULL
    friendly_player_archetype_id = NumberAttribute(null=True)

    # The player class of the opposing player (as a CardClass enum name)
    opposing_player_class = UnicodeAttribute()

    # The archetype id for the opposing player's deck, if available, else NULL
    opposing_player_archetype_id = NumberAttribute(null=True)

    url = UnicodeAttribute()  # The URL of the VOD on Twitch

    archetype_index = TwitchVodArchetypeIndex()
    deck_index = TwitchVodDeckStringIndex()
    user_id_index = TwitchVodUserIdIndex()

    # The TTL, in epoch seconds. DynamoDB must be told to use this attribute as the row TTL;
    # see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html

    ttl = NumberAttribute()

    class Meta:
        table_name = settings.DYNAMODB_TABLES["twitch_vod"]["NAME"]
        host = settings.DYNAMODB_TABLES["twitch_vod"]["HOST"]
        if hasattr(settings, "AWS_CREDENTIALS"):
            aws_access_key_id = settings.AWS_CREDENTIALS["AWS_ACCESS_KEY_ID"]
            aws_secret_access_key = settings.AWS_CREDENTIALS[
                "AWS_SECRET_ACCESS_KEY"]
        read_capacity_units = 2
        write_capacity_units = 5
        ttl_days = 14

    def __eq__(self, other):
        """Overrides the default implementation"""

        if isinstance(other, TwitchVod):
            return self.__dict__ == other.__dict__
        return NotImplemented

    def __init__(self, *args, ttl=None, **kwargs):
        effective_ttl = ttl \
         if ttl else int(time.time() + TwitchVod.Meta.ttl_days * 24 * 60 * 60)
        super().__init__(*args, ttl=effective_ttl, **kwargs)
Beispiel #27
0
class Beverage(Model):
    class Meta:
        table_name = 'Cellar'
        region = Config.AWS_REGION
        if local:  # Use the local DynamoDB instance when running locally
            host = 'http://localhost:8008'

    # Primary Attributes
    # `beverage_id`: Concat of producer, beverage name, year, size, and {batch or bottle date}.
    beverage_id = UnicodeAttribute(hash_key=True)

    # Required Attributes
    producer = UnicodeAttribute()
    name = UnicodeAttribute()
    year = NumberAttribute()
    size = UnicodeAttribute()
    location = UnicodeAttribute(range_key=True)
    batch = NumberAttribute(null=True)
    bottle_date = UnicodeAttribute(null=True)

    # Optional Attributes
    qty = NumberAttribute(null=True, default=0)
    qty_cold = NumberAttribute(null=True, default=0)
    style = UnicodeAttribute(null=True)
    specific_style = UnicodeAttribute(null=True)
    for_trade = BooleanAttribute(null=True, default=True)
    trade_value = NumberAttribute(null=True, default=0)
    aging_potential = NumberAttribute(null=True, default=2)
    untappd = UnicodeAttribute(null=True)
    note = UnicodeAttribute(null=True)

    # date_added should always be <= last_modified
    date_added = UTCDateTimeAttribute(default=datetime.utcnow())
    last_modified = UTCDateTimeAttribute(default=datetime.utcnow())

    def to_dict(self, dates_as_epoch=True) -> dict:
        """
        Return a dictionary with all attributes.
        Dates return as epoch (default) or in ISO format.
        """

        output = {
            "beverage_id":
            self.beverage_id.__str__(),
            "name":
            self.name.__str__(),
            "producer":
            self.producer.__str__(),
            "year":
            int(self.year),
            "batch":
            int(self.batch) if self.batch else None,
            "size":
            self.size.__str__(),
            "bottle_date":
            self.bottle_date.__str__() if self.bottle_date else None,
            "location":
            self.location.__str__(),
            "style":
            self.style.__str__() if self.style else None,
            "specific_style":
            self.specific_style.__str__() if self.specific_style else None,
            "qty":
            int(self.qty) if self.qty else 0,
            "qty_cold":
            int(self.qty_cold) if self.qty_cold else 0,
            "untappd":
            self.untappd.__str__() if self.untappd else None,
            "aging_potential":
            int(self.aging_potential) if self.aging_potential else None,
            "trade_value":
            int(self.trade_value) if self.trade_value else None,
            "for_trade":
            self.for_trade,
            "note":
            self.note.__str__() if self.note else None,
            "date_added":
            self.date_added.timestamp() * 1000,  # JS timestamps are in ms
            "last_modified":
            self.last_modified.timestamp() * 1000
        }

        if not dates_as_epoch:
            output['date_added'] = self.date_added.__str__()
            output['last_modified'] = self.last_modified.__str__()

        return output

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # logger.debug(f"Initializing a new instance of the Beverage model for {kwargs}.")
        # Replace empty strings with None
        # Construct the concatenated beverage_id when not provided:
        #  producer, beverage name, year, size, {bottle date or batch}.  Bottle date preferred.
        if 'beverage_id' not in kwargs.keys():
            # Need to create a beverage_id for this beverage
            self.beverage_id = f"{kwargs['producer']}_{kwargs['name']}_{kwargs['year']}_{kwargs['size']}"
            if 'batch' in kwargs.keys() and 'bottle_date' in kwargs.keys():
                # If both bottle_date and batch are provided, prefer bottle_date
                self.beverage_id += f"_{kwargs['bottle_date']}"

            elif 'batch' not in kwargs.keys() or kwargs['batch'] == '':
                # Batch is not provided
                self.batch = None
                if 'bottle_date' not in kwargs.keys(
                ) or kwargs['bottle_date'] == '':
                    # When no batch or bottle_date is provided, append "_None"
                    self.beverage_id += "_None"
                    self.bottle_date = None
                else:
                    # Bottle_date is provided
                    self.beverage_id += f"_{kwargs['bottle_date']}"
            else:
                # Use batch when bottle_date isn't provided
                self.beverage_id += f"_{kwargs['batch']}"
            logger.debug(
                f"Created a beverage_id for this new Beverage: {self.beverage_id}."
            )

        # Must provide a location
        if 'location' not in kwargs.keys() or kwargs['location'] is None:
            logger.debug(f"No value for location provided, raising KeyError.")
            raise KeyError("Location is required.")

        # Type check: Year
        try:
            self.year = int(kwargs['year'])
        except ValueError as e:
            logger.debug(f"Year must be an integer.\n{e}")
            raise ValueError(f"Year must be an integer.\n{e}")

        # Type check: Batch
        if 'batch' in kwargs.keys():
            try:
                if self.batch and self.batch != "":
                    self.batch = int(kwargs['batch'])
                if self.batch == "":
                    self.batch = None
            except ValueError as e:
                logger.debug(f"Batch number must be an integer.\n{e}")
                raise ValueError(f"Batch number must be an integer.\n{e}")

        # Type check: qty
        if 'qty' in kwargs.keys():
            try:
                self.qty = int(kwargs['qty'])
            except ValueError as e:
                logger.debug(f"Qty must be an integer.\n{e}")
                raise ValueError(f"Qty must be an integer.\n{e}")

        # Type check: qty_cold
        if 'qty_cold' in kwargs.keys():
            try:
                self.qty_cold = int(kwargs['qty_cold'])
            except ValueError as e:
                logger.debug(f"Qty_cold must be an integer.\n{e}")
                raise ValueError(f"Qty_cold must be an integer.\n{e}")

        # Adjust last_modified due to JS working in milliseconds
        if 'last_modified' in kwargs.keys():
            # Accept an epoch (`float` or `int`) for date_added
            if isinstance(self.last_modified, (float, int)):
                self.last_modified = datetime.utcfromtimestamp(
                    kwargs['last_modified'] / 1000)
            else:
                # Assume a string was provided and parse a datetime object from that
                try:
                    self.last_modified = datetime.fromisoformat(
                        str(self.last_modified))
                except (TypeError, ValueError) as e:
                    logger.debug(
                        f"Provided value for last_modified: {self.last_modified}, "
                        f"{type(self.last_modified)} cannot be converted to datetime."
                    )
                    raise ValueError(
                        f"Value for last_modified must be an epoch (float/int) or "
                        f"an ISO-formatted string. {e}")
        else:
            self.last_modified = datetime.utcnow()

        # Type & value checks for date_added
        if 'date_added' in kwargs.keys():
            # Accept an epoch (`float` or `int`) for date_added
            if isinstance(self.date_added, (float, int)):
                self.date_added = datetime.utcfromtimestamp(
                    kwargs['date_added'] / 1000)
            else:
                # Assume a string was provided and parse a datetime object from that
                try:
                    self.date_added = datetime.fromisoformat(
                        str(self.date_added))
                except (TypeError, ValueError) as e:
                    logger.debug(
                        f"Provided value for date_added: {self.date_added}, "
                        f"{type(self.date_added)} cannot be converted to datetime."
                    )
                    raise ValueError(
                        f"Value for date_added must be an epoch (float/int) or "
                        f"an iso-formatted string. {e}")

            # Ensure date_added is always <= last_modified
            if self.date_added > self.last_modified:
                self.last_modified = self.date_added
        # else:
        # When date_added is not provided
        # self.date_added = self.last_modified or datetime.utcnow()

    def __repr__(self) -> str:
        return f'<Beverage | beverage_id: {self.beverage_id}, qty: {self.qty} ({self.qty_cold}),' \
               f' location: {self.location}>'
Beispiel #28
0
class InboundResponse(MapAttribute):
    body = UnicodeAttribute()
    headers = ListAttribute(of=HeaderAttribute)
    status = NumberAttribute()
Beispiel #29
0
        class CustomMapAttribute(MapAttribute):
            custom = NumberAttribute()

            def __eq__(self, other):
                return self.custom == other.custom
Beispiel #30
0
class OAuthToken(Model):
    """
    Stores token data from some OAuth provider
    """

    ALIVE = ALIVE
    DEAD = DEAD

    access_token = UnicodeAttribute(hash_key=True)
    refresh_token = UnicodeAttribute(null=True)
    token_type = UnicodeAttribute(null=True)

    # Consistency would be too easy
    expires_in = NumberAttribute(null=True)
    expires_at = NumberAttribute(null=True)

    scope = UnicodeAttribute(null=True)
    user_info = JSONAttribute(null=True)

    state_index = TokensByState()
    state = UnicodeAttribute(default=ALIVE)

    updated_at = UTCDateTimeAttribute()
    created_at = UTCDateTimeAttribute(range_key=True)
    ttl = TTLAttribute(null=True)

    def save(self, *args, **kwargs):
        timestamp = timezone.now()
        if not self.created_at:
            self.created_at = timestamp
        self.updated_at = timestamp
        super().save(*args, **kwargs)

    def update(self, *args, **kwargs):
        self.updated_at = timezone.now()
        super().save(*args, **kwargs)

    def set_updated_at(self, *args, **kwargs):
        self.updated_at = timezone.now()

    def set_expiration(self):
        self.ttl = datetime.timedelta(days=30)

    @property
    def session_data(self):
        return dict(
            access_token=self.access_token,
            token_type=self.token_type,
            refresh_token=self.refresh_token,
            expires_at=self.expires_at,
            expires_in=self.expires_in,
        )

    @classmethod
    def create_if_non_existent(cls):
        if not cls.exists():
            cls.create_table(read_capacity_units=1, write_capacity_units=1, wait=True)

    class Meta:
        table_name = settings.OAUTH_TOKEN_TABLE_NAME
        region = settings.AWS_REGION
class OccurrenceDetail(MapAttribute):
    occurrence_num = NumberAttribute()
    start_date = DateAttribute()
    end_date = DateAttribute()
Beispiel #32
0
class ViewIndex(GlobalSecondaryIndex):
    class Meta:
        projection = AllProjection()
        region = CURRENT_REGION

    view = NumberAttribute(default=0, hash_key=True)
Beispiel #33
0
class CurrentHistoricalModel(object):
    eventTime = EventTimeAttribute(default=default_event_time)
    ttl = NumberAttribute(default=default_ttl())
    eventSource = UnicodeAttribute()
Beispiel #34
0
class HistoricalDecimalAttribute(Attribute):
    """
    A number attribute
    """
    attr_type = NUMBER

    def serialize(self, value):
        """
        Encode numbers as JSON
        """
        return json.dumps(value, default=decimal_default)

    def deserialize(self, value):
        """
        Decode numbers from JSON
        """
        return json.loads(value)


pynamodb.attributes.SERIALIZE_CLASS_MAP = {
    dict: MapAttribute(),
    list: ListAttribute(),
    set: ListAttribute(),
    bool: BooleanAttribute(),
    float: NumberAttribute(),
    int: NumberAttribute(),
    str: HistoricalUnicodeAttribute(),
    decimal.Decimal: HistoricalDecimalAttribute()
}