class User(Model, UserMixin): __metadata__ = { "_name": "users", 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('email-username', 'username').throughput(read=1, write=1), GlobalIndex.all('email-index', 'email').throughput(read=1, write=1) ], } id = Field(hash_key=True) active = Field(data_type=bool) # User authentication information username = Field() password = Field() # User email information email = Field() email_confirmed_at = Field(data_type=datetime) # User information first_name = Field() last_name = Field() def get_id(self): if self.id is None: self.id = str(uuid.uuid1()) return self.id
class PrimitiveWidget(Model): """ Model for testing python data types """ __metadata__ = { 'global_indexes': [ GlobalIndex('gindex', 'string2', 'num'), GlobalIndex('gindex2', 'num2', 'binary'), ], } string = Field(data_type=six.binary_type, hash_key=True) string2 = Field(data_type=six.text_type) num = Field(data_type=int, coerce=True) num2 = Field(data_type=float) binary = Field(data_type=BINARY, coerce=True) myset = Field(data_type=set) data = Field(data_type=dict) friends = Field(data_type=list) created = Field(data_type=datetime) birthday = Field(data_type=date) wobbles = Field(data_type=bool) price = Field(data_type=Decimal) def __init__(self, **kwargs): kwargs.setdefault('string', 'abc') super(PrimitiveWidget, self).__init__(**kwargs)
def test_add_index_test_mode(self): """Simulates adding a global secondary index at a later time. However, with the test attribute selected, no change is actually made.""" table = self.engine.dynamo.describe_table( WidgetToAddIndex.meta_.ddb_tablename(self.engine.namespace)) self.assertEqual(len(table.global_indexes), 1) # Simulating adding an index later on. WidgetToAddIndex.meta_.global_indexes.append( GlobalIndex('gindex-2', 'string2', 'string')) WidgetToAddIndex.meta_.post_create() WidgetToAddIndex.meta_.validate_model() WidgetToAddIndex.meta_.post_validate() changed = self.engine.update_schema(test=True) self.assertListEqual( changed, [WidgetToAddIndex.meta_.ddb_tablename(self.engine.namespace)]) table = self.engine.dynamo.describe_table( WidgetToAddIndex.meta_.ddb_tablename(self.engine.namespace)) self.assertEqual(len(table.global_indexes), 1) try: self.engine.query(WidgetToAddIndex).index('gindex-2').filter( WidgetToAddIndex.string2 == 'test').all() except DynamoDBError: pass else: assert False, "An error was expected"
def test_add_index(self): """Simulates adding a global secondary index at a later time.""" table = self.engine.dynamo.describe_table( WidgetToAddIndex.meta_.ddb_tablename(self.engine.namespace)) self.assertEqual(len(table.global_indexes), 1) # Simulating adding an index later on. WidgetToAddIndex.meta_.global_indexes.append( GlobalIndex('gindex-2', 'string2', 'string')) WidgetToAddIndex.meta_.post_create() WidgetToAddIndex.meta_.validate_model() WidgetToAddIndex.meta_.post_validate() changed = self.engine.update_schema() self.assertListEqual( changed, [WidgetToAddIndex.meta_.ddb_tablename(self.engine.namespace)]) table = self.engine.dynamo.describe_table( WidgetToAddIndex.meta_.ddb_tablename(self.engine.namespace)) self.assertEqual(len(table.global_indexes), 2) one = WidgetToAddIndex("one-1", "one-2", 1) two = WidgetToAddIndex("one-2", "test", 2) self.engine.save(one) self.engine.save(two) result = self.engine.query(WidgetToAddIndex).index('gindex-2').filter( WidgetToAddIndex.string2 == 'test').all() self.assertEqual(len(result), 1) self.assertNotEqual(result[0], one) self.assertEqual(result[0], two)
class EmailNotify(DyBase): __tablename__ = 'email_notify' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'email', 'date_created').throughput(read=1, write=1), ], } # Identification Data: email & password email = Field(data_type=STRING, nullable=False) type = Field(data_type=STRING, nullable=False) # New instance instantiation procedure def __init__(self, email, type): super(EmailNotify, self).__init__() self.email = email self.type = type def __repr__(self): return '<email %r>' % (self.email)
class PublicHoliday(DyBase): __tablename__ = 'public_holiday' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'group_id').throughput(read=1, write=1), ], } created_by_id = Field(data_type=STRING, nullable=False) group_id = Field(data_type=STRING, nullable=False) holiday_date = Field(data_type=NUMBER, nullable=False) # full day or half day is_halfday = Field(data_type=NUMBER, nullable=False) # New instance instantiation procedure def __init__(self, created_by_id, group_id, date, is_halfday): super(PublicHoliday, self).__init__() self.created_by_id = created_by_id self.group_id = group_id self.holiday_date = date self.is_halfday = is_halfday def __repr__(self): return '<Date %r>' % (str(self.holiday_date)) def __getitem__(self, key): return self.holiday_date
class Rule(DyBase): __tablename__ = 'rule' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'group_id', 'term_id').throughput(read=1, write=1), ], } # Identification Data: email & password group_id = Field(data_type=STRING, nullable=False) term_id = Field(data_type=STRING, nullable=False) definition = Field(data_type=STRING, nullable=False) # New instance instantiation procedure def __init__(self, group_id, term_id, definition): super(Rule, self).__init__() self.group_id = group_id self.term_id = term_id self.definition = definition def __repr__(self): return '<group %r>' % (self.group_id)
class Group(DyBase): __tablename__ = 'group' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'domain').throughput(read=1, write=1), ], } # Identification Data: email & password name = Field(data_type=STRING) type_id = Field(data_type=NUMBER) domain = Field(data_type=STRING) default_term_id = Field(data_type=STRING, nullable=True) # New instance instantiation procedure def __init__(self, name, type_id, domain): super(Group, self).__init__() self.name = name self.type_id = type_id self.domain = domain def __repr__(self): return '<Group %r>' % (self.name) def __getitem__(self, key): return self.id
class Member(DyBase): __tablename__ = 'group_member' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'group_id').throughput(read=1, write=1), ], } group_id = Field(data_type=STRING, nullable=False) user_id = Field(data_type=NUMBER, nullable=False) # New instance instantiation procedure def __init__(self, group_id, user_id): super(Member, self).__init__() self.group_id = group_id self.user_id = user_id def __repr__(self): return '<User_id %r>' % (self.user_id)
class Term(DyBase): __tablename__ = 'term' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'group_id').throughput(read=1, write=1), ], } group_id = Field(data_type=STRING, nullable=False) name = Field(data_type=STRING, nullable=False) start_date = Field(data_type=NUMBER, nullable=False) end_date = Field(data_type=NUMBER, nullable=False) family_spread = Field(data_type=STRING, nullable=False) # New instance instantiation procedure def __init__(self, group_id, name, start_dt, end_dt, family_spread): super(Term, self).__init__() self.group_id = group_id self.name = name self.start_date =start_dt self.end_date = end_dt self.family_spread = family_spread def __repr__(self): return '<term name %r>' % (self.name)
class Post(Model): """ Test model with composite fields """ __metadata__ = { 'global_indexes': [ GlobalIndex('score-index', 'c_all', 'score'), ] } hkey = Composite('userid', 'id', hash_key=True) userid = Field() id = Field() c_all = Composite('userid', 'id', 'about', 'text') score = Composite('likes', 'ts', 'deleted', data_type=NUMBER, merge=lambda x, y, z: None if z else x + y) likes = Field(data_type=int, default=0) ts = Field(data_type=float, default=0) deleted = Field(data_type=bool, default=False) points = Field(data_type=Decimal, default=Decimal('0')) about = Field() text = Field() tags = Field(data_type=set) keywords = Composite('text', 'about', data_type=set, merge=lambda t, a: t.split() + a.split(), coerce=True) def __init__(self, userid, id, ts, text='foo', about='bar'): super(Post, self).__init__(userid=userid, id=id, ts=ts, text=text, about=about)
class Invite(DyBase): __tablename__ = 'invite' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'group_id').throughput(read=1, write=1), ], } email = Field(data_type=STRING, nullable=False) group_id = Field(data_type=STRING, nullable=False) invite_token = Field(data_type=STRING, nullable=True) # New instance instantiation procedure def __init__(self, email, group_id, invite_token): super(Invite, self).__init__() self.email = email self.group_id = group_id self.invite_token = invite_token def __repr__(self): return '<Email %r>' % (self.email)
class Widget(Model): """ Model for testing default field values """ __metadata__ = { 'global_indexes': [ GlobalIndex('gindex', 'string2', 'num'), ], } string = Field(hash_key=True) string2 = Field() num = Field(data_type=NUMBER) binary = Field(data_type=BINARY, coerce=True) str_set = Field(data_type=STRING_SET) num_set = Field(data_type=NUMBER_SET) bin_set = Field(data_type=BINARY_SET) data_dict = Field(data_type=dict) data_list = Field(data_type=list) bigdata = Field(data_type=CompressedDict) natural_num = Field(data_type=int, check=lambda x: x >= 0, default=1) check_num = Field(data_type=int, check=(lambda x: x != 0, lambda x: x != 2)) not_null = Field(data_type=int, nullable=False, default=0) not_null_natural = Field(data_type=int, check=lambda x: x != 1, nullable=False, default=0) comp = Composite('str_set', data_type=int, merge=len) def __init__(self, **kwargs): kwargs.setdefault('string', 'abc') super(Widget, self).__init__(**kwargs)
class Children(DyBase): __tablename__ = 'term_children' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'term_id').throughput(read=1, write=1), ], } term_id = Field(data_type=STRING, nullable=False) child_count = Field(data_type=NUMBER, nullable=False) # New instance instantiation procedure def __init__(self, term_id, child_count): super(Children, self).__init__() self.term_id = term_id self.child_count = child_count def __repr__(self): return '<term id %r>' % (self.term_id)
class Store(Model): """ Test model for indexes """ __metadata__ = { 'global_indexes': [ GlobalIndex.all('name-index', 'name', 'city'), GlobalIndex.keys('name-emp-index', 'name', 'num_employees'), GlobalIndex.include('name-profit-index', 'name', 'monthly_profit', includes=['name', 'num_employees']), ], } city = Field(hash_key=True) name = Field(range_key=True) sq_feet = Field(data_type=int).all_index('size-index') num_employees = Field(data_type=int).keys_index('emp-index') monthly_profit = Field(data_type=float)\ .include_index('profit-index', ['name', 'num_employees'])
class User(DyBase): __tablename__ = 'user' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index1', 'email').throughput(read=1, write=1), GlobalIndex.all('ts-index', 'role').throughput(read=1, write=1), ], } # Identification Data: email & password name = Field(data_type=STRING, nullable=False) given_name = Field(data_type=STRING, nullable=False) family_name = Field(data_type=STRING, nullable=False) email = Field(data_type=STRING, nullable=False) password = Field(data_type=STRING, nullable=True) auth_token = Field(data_type=STRING, nullable=True) image_url = Field(data_type=STRING, nullable=True) # Authorisation Data: role & status role = Field(data_type=NUMBER, nullable=False, default=1) is_active = Field(data_type=NUMBER, nullable=False, default=True) # New instance instantiation procedure def __init__(self, name, givenName, familyName, email, password, token, imageUrl): super(User, self).__init__() self.name = name self.given_name = givenName self.family_name = familyName self.email = email self.password = password self.auth_token = token self.image_url = imageUrl def __repr__(self): return '<User %r>' % (self.email) def __getitem__(self, key): return self.email
class Post(Model): """ Model for testing composite queries """ __metadata__ = { 'global_indexes': [ GlobalIndex('name-index', 'username', 'score'), GlobalIndex('ts-index', 'username', 'ts'), GlobalIndex('hash-index', 'total_uid') ], } uid = Composite('type', 'id', hash_key=True) type = Field() id = Field() score = Composite('ts', 'upvotes', range_key=True, data_type=NUMBER, merge=score_merge) username = Field() ts = Field(data_type=NUMBER, default=0) upvotes = Field(data_type=NUMBER, default=0) total_uid = Composite('uid', 'username', merge=lambda x, y: x + ':' + str(y))
class DynamoPackage(Package, Model): """ Python package stored in DynamoDB """ __metadata__ = { 'global_indexes': [ GlobalIndex('name-index', 'name'), ], } filename = Field(hash_key=True) name = Field() version = Field() last_modified = Field(data_type=datetime) data = Field(data_type=dict)
class User(Model): """ Model for testing queries """ __metadata__ = { 'global_indexes': [ GlobalIndex('name-index', 'name', 'score'), ], } id = Field(hash_key=True) name = Field(range_key=True) score = Field(data_type=NUMBER, index='score-index', default=0) str_set = Field(data_type=STRING_SET)
class OAuth2DynamoToken(Model): """ Dynamo version of authlib.integrations.sqla_oauth2.OAuth2TokenMixin """ __metadata__ = { 'global_indexes': [ GlobalIndex.keys('refresh-index', 'refresh_token').throughput(read=10, write=2), GlobalIndex.keys('user-index', 'user_id').throughput(read=10, write=2) ], } client_id = Field(type=STRING) user_id = Field(type=NUMBER) token_type = Field(type=STRING) access_token = Field(hash_key=True) refresh_token = Field(type=STRING) scope = Field() revoked = Field(type=bool) issued_at = Field(type=NUMBER) expires_in = Field(type=NUMBER) def get_client_id(self): return self.client_id def get_scope(self): return self.scope def get_expires_in(self): return self.expires_in def get_expires_at(self): return self.issued_at + self.expires_in def is_refresh_token_active(self): if self.revoked: return False expires_at = self.issued_at + self.expires_in * 2 return expires_at >= time.time()
class DynamoPackage(Package, Model): """ Python package stored in DynamoDB """ __metadata__ = {"global_indexes": [GlobalIndex("name-index", "name")]} filename = Field(hash_key=True) name = Field() version = Field() last_modified = Field(data_type=datetime) summary = Field() data = Field(data_type=dict) def __init__(self, *args, **kwargs): super(DynamoPackage, self).__init__(*args, **kwargs) # DynamoDB doesn't play nice with empty strings. if not self.summary: self.summary = None
class Widget(Model): """ Test model with composite fields """ __metadata__ = { 'global_indexes': [GlobalIndex('ts-index', 'userid', 'ts').throughput(1, 1)], 'throughput': { 'read': 1, 'write': 1, }, } userid = Field(hash_key=True) c_range = Composite('userid', 'id', range_key=True) c_index = Composite('userid', 'id', index='comp-index') c_plain = Composite('userid', 'id') _c_private = Composite('userid', 'id') id = Field() ts = Field(data_type=NUMBER) def __init__(self, userid, id, ts): super(Widget, self).__init__(userid, id=id, ts=ts)
class AbstractWidget(Model): """ This is an abstract widget. It should be ignored. """ __metadata__ = { 'global_indexes': [ GlobalIndex('gindex', 'string2', 'num'), ], "_abstract": True } string = Field(hash_key=True) string2 = Field() num = Field(data_type=NUMBER) def __init__(self, string, string2, num): super(AbstractWidget, self).__init__() self.string = string self.string2 = string2 self.num = num
class Switchday(DyBase): __tablename__ = 'switchday' __metadata__ = { 'throughput': { 'read': 1, 'write': 1, }, 'global_indexes': [ GlobalIndex.all('ts-index', 'group_id').throughput(read=1, write=1), ], } group_id = Field(data_type=STRING, nullable=False) switch_date = Field(data_type=NUMBER, nullable=False) from_time_in_24hours = Field(data_type=STRING, default = '0900') to_time_in_24hours = Field(data_type=STRING, default = '1630') standin_user_id = Field(data_type=STRING, nullable=True) is_half_day = Field(data_type=NUMBER, nullable=False, default=False) is_work_day = Field(data_type=NUMBER, nullable=False, default=False) # New instance instantiation procedure def __init__(self, group_id, switch_date, from_time, to_time, standin_user_id, is_half_day, is_work_day): super(Switchday, self).__init__() self.group_id = group_id self.switch_date = switch_date self.from_time_in_24hours = from_time self.to_time_in_24hours = to_time self.standin_user_id = standin_user_id self.is_half_day = is_half_day self.is_work_day = is_work_day def __repr__(self): return '<Switch_date %r>' % (self.switch_date) def __getitem__(self, key): return self.switch_date
class AppDetails(Model): __metadata__ = { 'global_indexes': [ GlobalIndex('price-index', 'currency', 'price'), GlobalIndex('iphone-index', 'supportediPhone'), GlobalIndex('ipad-index', 'supportediPad'), GlobalIndex('ipod-index', 'supportediPod'), ], } app_id = Field(data_type=types.IntType, hash_key=True, coerce=True) country = Field(data_type=types.StringType, range_key=True) advisories = Field(data_type=types.ListType) artistId = Field(data_type=types.IntType, coerce=True) artistName = Field(data_type=types.StringType) artistViewUrl = Field(data_type=types.StringType) artworkUrl100 = Field(data_type=types.StringType) artworkUrl512 = Field(data_type=types.StringType) artworkUrl60 = Field(data_type=types.StringType) averageUserRating = Field(data_type=types.FloatType, coerce=True) averageUserRatingForCurrentVersion = Field(data_type=types.FloatType, coerce=True) bundleId = Field(data_type=types.StringType) contentAdvisoryRating = Field(data_type=types.StringType) currency = Field(data_type=types.StringType) currentVersionReleaseDate = Field(data_type=types.DateTimeType) description = Field(data_type=types.StringType) features = Field(data_type=types.ListType) fileSizeBytes = Field(data_type=types.IntType, coerce=True) formattedPrice = Field(data_type=types.StringType) genreIds = Field(data_type=types.ListType) genres = Field(data_type=types.ListType) ipadScreenshotUrls = Field(data_type=types.ListType) isGameCenterEnabled = Field(data_type=types.BoolType) isVppDeviceBasedLicensingEnabled = Field(data_type=types.BoolType) kind = Field(data_type=types.StringType) languageCodesISO2A = Field(data_type=types.ListType) minimumOsVersion = Field(data_type=types.StringType) price = Field(data_type=types.FloatType, coerce=True) primaryGenreId = Field(data_type=types.IntType, coerce=True) primaryGenreName = Field(data_type=types.StringType) releaseDate = Field(data_type=types.DateTimeType, coerce=True) releaseNotes = Field(data_type=types.StringType) screenshotUrls = Field(data_type=types.ListType) sellerName = Field(data_type=types.StringType) sellerUrl = Field(data_type=types.StringType) supportedDevices = Field(data_type=set_(str), coerce=True) supportediPad = Field(data_type=types.IntType, default=0) supportediPads = Field(data_type=set_(str)) supportediPhone = Field(data_type=types.IntType, default=0) supportediPhones = Field(data_type=set_(str)) supportediPod = Field(data_type=types.IntType, default=0) supportediPods = Field(data_type=set_(str)) trackCensoredName = Field(data_type=types.StringType) trackContentRating = Field(data_type=types.StringType) trackId = Field(data_type=types.IntType, coerce=True) trackName = Field(data_type=types.StringType) trackViewUrl = Field(data_type=types.StringType) userRatingCount = Field(data_type=types.IntType, coerce=True) userRatingCountForCurrentVersion = Field(data_type=types.IntType, coerce=True) version = Field(data_type=types.StringType) wrapperType = Field(data_type=types.StringType) def __repr__(self): return "%s (%s) selling for %s %s" % (self.app_id, self.country, self.price, self.currency) def __init__(self, app_id, country, data=None): if data is None: data = {} self.app_id = app_id self.country = country for field, value in data.items(): if 'Date' in field: value = strptime(value, "%Y-%m-%dT%H:%M:%SZ") value = datetime.fromtimestamp(mktime(value)) setattr(self, field, value) if self.supportedDevices: self.index_devices() def index_devices(self): ipads = set() ipods = set() iphones = set() for device in self.supportedDevices: if device.startswith('iPad'): ipads.add(device) elif device.startswith('iPod'): ipods.add(device) else: iphones.add(device) if ipods: self.supportediPod = 1 self.supportediPods = ipods if ipads: self.supportediPad = 1 self.supportediPads = ipads if iphones: self.supportediPhone = 1 self.supportediPhones = iphones @classmethod def with_price(cls, currency, lower_price, higher_price=None): """ Finds all apps based on currency and price. If only one price is passed, finds apps with exact price. If higher_price is passed, finds apps within range of lower_price and higher_price. """ if higher_price is None: return engine(cls).filter( currency=currency, price=lower_price).index('price-index').all() return engine(cls).filter( cls.currency == currency, cls.price.between_(lower_price, higher_price)).index('price-index').all() @classmethod def with_supportedDevice(cls, device): """ Query method that returns an array of all apps that support the given device name. Device name must start with 'iPod', 'iPad', or 'iPhone', else will raise an error. """ if device.startswith('iPod'): device_type = cls.supportediPod device_list = cls.supportediPods device_index = 'ipod-index' elif device.startswith('iPad'): device_type = cls.supportediPad device_list = cls.supportediPads device_index = 'ipad-index' elif device.startswith('iPhone'): device_type = cls.supportediPhone device_list = cls.supportediPads device_index = 'iphone-index' else: return InValidDeviceName("%s is not a valid device name" % device) return engine(cls).filter( device_type == 1, device_list.contains_(device)).index(device_index).all()