class BaseStatistic(model.Model): """Base Statistic Model class. Attributes: bytes: the total number of bytes taken up in Cloud Datastore for the statistic instance. count: attribute is the total number of occurrences of the statistic in Cloud Datastore. timestamp: the time the statistic instance was written to Cloud Datastore. """ STORED_KIND_NAME = '__BaseStatistic__' bytes = model.IntegerProperty() count = model.IntegerProperty() timestamp = model.DateTimeProperty() @classmethod def _get_kind(cls): """Kind name override.""" return cls.STORED_KIND_NAME
class KindPropertyNamePropertyTypeStat(BaseKindStatistic): """Statistic on (kind, property_name, property_type) tuples in Cloud Datastore. There is an instance of the KindPropertyNamePropertyTypeStat for every (kind, property_name, property_type) tuple in the application's datastore. Attributes: property_type: the property type associated with the statistic instance. property_name: the name of the property associated with the statistic instance. builtin_index_bytes: the number of bytes taken up to store builtin-in index entries builtin_index_count: the number of built-in index entries. """ STORED_KIND_NAME = '__Stat_PropertyType_PropertyName_Kind__' property_type = model.StringProperty() property_name = model.StringProperty() builtin_index_bytes = model.IntegerProperty(default=0) builtin_index_count = model.IntegerProperty(default=0)
class GlobalStat(BaseStatistic): """An aggregate of all entities across the entire application. This statistic only has a single instance in Cloud Datastore that contains the total number of entities stored and the total number of bytes they take up. Attributes: entity_bytes: the number of bytes taken up to store the statistic in Cloud Datastore minus the cost of storing indices. builtin_index_bytes: the number of bytes taken up to store builtin-in index entries builtin_index_count: the number of built-in index entries. composite_index_bytes: the number of bytes taken up to store composite index entries composite_index_count: the number of composite index entries. """ STORED_KIND_NAME = '__Stat_Total__' entity_bytes = model.IntegerProperty(default=0) builtin_index_bytes = model.IntegerProperty(default=0) builtin_index_count = model.IntegerProperty(default=0) composite_index_bytes = model.IntegerProperty(default=0) composite_index_count = model.IntegerProperty(default=0)
class PropertyTypeStat(BaseStatistic): """An aggregate of all properties across the entire application by type. There is an instance of the PropertyTypeStat for every property type (google.appengine.api.datastore_types._PROPERTY_TYPES) in use by the application in its datastore. Attributes: property_type: the property type associated with the statistic instance. entity_bytes: the number of bytes taken up to store the statistic in Cloud Datastore minus the cost of storing indices. builtin_index_bytes: the number of bytes taken up to store builtin-in index entries builtin_index_count: the number of built-in index entries. """ STORED_KIND_NAME = '__Stat_PropertyType__' property_type = model.StringProperty() entity_bytes = model.IntegerProperty(default=0) builtin_index_bytes = model.IntegerProperty(default=0) builtin_index_count = model.IntegerProperty(default=0)
class KindStat(BaseKindStatistic): """An aggregate of all entities at the granularity of their Kind. There is an instance of the KindStat for every Kind that is in the application's datastore. This stat contains per-Kind statistics. Attributes: builtin_index_bytes: the number of bytes taken up to store builtin-in index entries builtin_index_count: the number of built-in index entries. composite_index_bytes: the number of bytes taken up to store composite index entries composite_index_count: the number of composite index entries. """ STORED_KIND_NAME = '__Stat_Kind__' builtin_index_bytes = model.IntegerProperty(default=0) builtin_index_count = model.IntegerProperty(default=0) composite_index_bytes = model.IntegerProperty(default=0) composite_index_count = model.IntegerProperty(default=0)
class EnkiModelPost(model.Model): author = model.IntegerProperty() body = model.TextProperty() thread = model.IntegerProperty() # thread the post belongs to time_created = model.DateTimeProperty(auto_now_add=True) time_updated = model.DateTimeProperty(auto_now=True)
class NotificationToken(ndb.Model): android = 0 ios = 1 winPhone = 2 userId = model.IntegerProperty() token = model.StringProperty() email = model.StringProperty() kind = model.IntegerProperty()
class EnkiModelThread(model.Model): author = model.IntegerProperty() title = model.StringProperty() forum = model.IntegerProperty() # forum the thread belongs to num_posts = model.IntegerProperty( default=0) # number of posts in the thread time_created = model.DateTimeProperty(auto_now_add=True)
class EnkiModelForum(model.Model): title = model.StringProperty() description = model.StringProperty() group = model.StringProperty() # group of forums order = model.IntegerProperty( default=0) # sort the forums (within a group) num_threads = model.IntegerProperty( default=0) # number of threads in the forum num_posts = model.IntegerProperty( default=0) # number of posts in the forum's threads time_created = model.DateTimeProperty(auto_now_add=True)
class EnkiModelApp(model.Model): user_id = model.IntegerProperty() name = model.StringProperty() secret = model.StringProperty() time_created = model.DateTimeProperty(auto_now_add=True) @classmethod def exist_by_name(cls, name): count = cls.query(cls.name == name).count(1) return count > 0 @classmethod def count_by_user_id(cls, user_id): return cls.query(cls.user_id == user_id).count() @classmethod def fetch_by_user_id(cls, user_id): list = cls.query(cls.user_id == user_id).order( cls.time_created).fetch() return list @classmethod def exist_by_app_id_app_secret(cls, app_id, app_secret): item = ndb.Key(cls, int(app_id)).get() if item and item.secret == app_secret: return True return False
class EnkiModelSummary(model.Model): #=== MODEL ==================================================================== name = model.StringProperty() count = model.IntegerProperty() time_created = model.DateTimeProperty(auto_now_add=True) #=== UTILITIES ================================================================ @classmethod def create(cls, name, count): cls(name=name, count=count).put_async() @classmethod def csv(cls): list = cls.query().order(-cls.time_created, cls.name).fetch() result = '"time_created","count","name"\n' for item in list: time_created = '"' + str(item.time_created).replace('"', "''") + '"' count = '"' + str(item.count) + '"' name = '"' + str(item.name).replace('"', "''") + '"' result += ','.join([time_created, count, name]) + '\n' return result
class AcarsFlight(model.Model): flight_id = model.StringProperty() user_id = model.StringProperty() acars_id = model.IntegerProperty() aircraft_type = model.StringProperty() flight_number = model.StringProperty() flight_type = model.StringProperty() flight_plan = model.StringProperty(repeated=True) departure = model.StringProperty() destination = model.StringProperty() flight_path = model.LocalStructuredProperty(FlightPosition, repeated=True) def add_flight(self): self.put() def active_flights_for_user(self, user_id, limit=20, offset=0): flights = AcarsFlight.query(AcarsFlight.user_id == user_id).fetch( limit, offset=offset) active_flights = [] for flight in flights: position = AcarsPosition.query( AcarsPosition.flight_id == flight.flight_id, AcarsPosition.message_type == 'ZZ').get() if position is None: active_flights.append(flight) return active_flights active_flights_for_user = classmethod(active_flights_for_user)
class EntityGroup(_BaseMetadata): """Model for __entity_group__ metadata (available in HR datastore only). This metadata contains a numeric __version__ property that is guaranteed to increase on every change to the entity group. The version may increase even in the absence of user-visible changes to the entity group. The __entity_group__ entity may not exist if the entity group was never written to. """ KIND_NAME = '__entity_group__' ID = 1 version = model.IntegerProperty(name='__version__') @classmethod def key_for_entity_group(cls, key): """Return the key for the entity group containing key. Args: key: a key for an entity group whose __entity_group__ key you want. Returns: The __entity_group__ key for the entity group containing key. """ return model.Key(cls.KIND_NAME, cls.ID, parent=key.root())
class EnkiModelTokenEmailRollback(model.Model): #=== MODEL ==================================================================== token = model.StringProperty() email = model.StringProperty() user_id = model.IntegerProperty() # ndb user ID time_created = model.DateTimeProperty(auto_now_add=True) #=== QUERIES ================================================================== @classmethod def get_by_user_id_email(cls, user_id, email): return cls.query(ndb.AND(cls.user_id == user_id, cls.email == email)).get() @classmethod def get_by_token(cls, token): return cls.query(cls.token == token).get() @classmethod def fetch_keys_by_user_id(cls, user_id): return cls.query(cls.user_id == user_id).fetch(keys_only=True) @classmethod def fetch_keys_by_user_id_time(cls, user_id, time_created): return cls.query( ndb.AND(cls.time_created >= time_created, cls.user_id == user_id)).fetch(keys_only=True)
class EnkiModelDisplayName( model.Model ): user_id = model.IntegerProperty() prefix = model.StringProperty() # prefix e.g. 'Jane' prefix_lower = model.ComputedProperty(lambda self: self.prefix.lower()) # lowercase prefix e.g. "jane" suffix = model.StringProperty() # suffix e.g. '#1234' => full display name = 'Jane#1234' current = model.BooleanProperty( default = True ) time_created = model.DateTimeProperty( auto_now_add = True )
class EnkiModelProductKey( model.Model ): licence_key = model.StringProperty() # mandatory product_name = model.StringProperty() # mandatory purchaser_email = model.StringProperty() # mandatory purchaser_user_id = model.IntegerProperty() # if the purchaser is registered shop_name = model.StringProperty() #choices = [ 'FastSpring' ]) purchase_price = model.StringProperty() quantity = model.IntegerProperty() order_id = model.StringProperty() order_type = model.StringProperty( choices = [ 'emulated', 'test', 'normal' ]) activated_by_user = model.IntegerProperty( ) time_created = model.DateTimeProperty( auto_now_add = True ) time_updated = model.DateTimeProperty( auto_now = True )
class EnkiModelRestAPIDataStore(model.Model): user_id = model.IntegerProperty() app_id = model.StringProperty() data_type = model.StringProperty() data_id = model.StringProperty() data_payload = model.JsonProperty() time_expires = model.DateTimeProperty(auto_now_add=False) read_access = model.StringProperty( choices=['private', 'friends', 'public'], default='private')
class EnkiModelUserPageData(model.Model): user_id = model.IntegerProperty() route = model.StringProperty() data = model.PickleProperty() @classmethod def get_by_user_id_route(cls, user_id, route): entity = cls.query(ndb.AND(cls.user_id == user_id, cls.route == route)).get() return entity
class NamespaceStat(BaseStatistic): """An aggregate of all entities across an entire namespace. This statistic has one instance per namespace. The key_name is the represented namespace. NamespaceStat entities will only be found in the namespace "" (empty string). It contains the total number of entities stored and the total number of bytes they take up. Attributes: subject_namespace: the namespace associated with the statistic instance. entity_bytes: the number of bytes taken up to store the statistic in Cloud Datastore minus the cost of storing indices. builtin_index_bytes: the number of bytes taken up to store builtin-in index entries builtin_index_count: the number of built-in index entries. composite_index_bytes: the number of bytes taken up to store composite index entries composite_index_count: the number of composite index entries. """ STORED_KIND_NAME = '__Stat_Namespace__' subject_namespace = model.StringProperty() entity_bytes = model.IntegerProperty(default=0) builtin_index_bytes = model.IntegerProperty(default=0) builtin_index_count = model.IntegerProperty(default=0) composite_index_bytes = model.IntegerProperty(default=0) composite_index_count = model.IntegerProperty(default=0)
class EnkiModelUserPageData(model.Model): #=== MODEL ==================================================================== user_id = model.IntegerProperty() route = model.StringProperty() data = model.PickleProperty() #=== QUERIES ================================================================== @classmethod def get_by_user_id_route(cls, user_id, route): return cls.query(ndb.AND(cls.user_id == user_id, cls.route == route)).get()
class EnkiModelRestAPIConnectToken(model.Model): #=== MODEL ==================================================================== token = model.StringProperty() user_id = model.IntegerProperty() time_created = model.DateTimeProperty(auto_now_add=True) #=== CONSTANTS ================================================================ MAX_AGE = 5 # in minutes, duration of a connection token validity #=== QUERIES ================================================================== @classmethod def get_by_user_id_token_valid_age(cls, user_id, token): return cls.query( ndb.AND( cls.user_id == user_id, cls.token == token, cls.time_created > (datetime.datetime.now() - datetime.timedelta(minutes=cls.MAX_AGE)))).get() @classmethod def fetch_by_user(cls, user_id): return cls.query(cls.user_id == user_id).fetch(keys_only=True) @classmethod def fetch_expired(cls): return cls.query( cls.time_created < (datetime.datetime.now() - datetime.timedelta( minutes=cls.MAX_AGE))).fetch(keys_only=True) #=== UTILITIES ================================================================ @classmethod def cleanup_and_get_new_connection_token(cls, user_id): # note: ensure user is logged in and has display name before calling this function if user_id: # delete any existing connect token for the user ndb.delete_multi_async(cls.fetch_by_user(user_id)) # create a new token and return it token = enki.libutil.generate_connect_code() entity = cls(token=token, user_id=int(user_id)) entity.put() return token return None
class KindCompositeIndexStat(BaseStatistic): """Statistic on (kind, composite_index_id) tuples in Cloud Datastore. There is an instance of the KindCompositeIndexStat for every unique (kind, composite_index_id) tuple in the application's datastore indexes. Attributes: index_id: the id of the composite index associated with the statistic instance. kind_name: the name of the kind associated with the statistic instance. """ STORED_KIND_NAME = '__Stat_Kind_CompositeIndex__' index_id = model.IntegerProperty() kind_name = model.StringProperty()
class BaseKindStatistic(BaseStatistic): """Base Statistic Model class for stats associated with kinds. Attributes: kind_name: the name of the kind associated with the statistic instance. entity_bytes: the number of bytes taken up to store the statistic in Cloud Datastore minus the cost of storing indices. """ STORED_KIND_NAME = '__BaseKindStatistic__' kind_name = model.StringProperty() entity_bytes = model.IntegerProperty(default=0)
class Change(ndb.Model): recordId = model.StringProperty() when = model.DateTimeProperty() subscriberId = model.StringProperty() kind = model.IntegerProperty() CHANGE_VOTE = 1 CHANGE_PLACE = 2 CHANGE_COMMENT = 3 @classmethod def migrate_old_votes_to_changes(cls): count = 0 vote_changes = VoteChange.query() for v in vote_changes: new_change = Change() new_change.kind = cls.CHANGE_VOTE new_change.subscriberId = v.subscriberId new_change.recordId = v.voteId new_change.when = v.when new_change.put() count += 1 return count @classmethod def migrate_old_places_to_changes(cls): count = 0 place_changes = PlaceChange.query() for p in place_changes: new_change = Change() new_change.kind = cls.CHANGE_PLACE new_change.subscriberId = p.subscriberId new_change.recordId = p.placeId new_change.when = p.when new_change.put() count += 1 return count
class EnkiModelMessage(model.Model): #=== MODEL ==================================================================== sender = model.IntegerProperty() recipient = model.IntegerProperty() type = model.StringProperty() time_created = model.DateTimeProperty(auto_now_add=True) #=== QUERIES ================================================================== @classmethod def get_by_id(cls, message_id): return ndb.Key(cls, message_id).get() @classmethod def exist_by_recipient(cls, user_id): count = cls.query(cls.recipient == user_id).count(1) return count > 0 @classmethod def count_by_recipient(cls, user_id): return cls.query(cls.recipient == user_id).count() @classmethod def fetch_by_recipient(cls, user_id): return cls.query(cls.recipient == user_id).fetch() @classmethod def exist_by_sender_recipient(cls, sender_id, recipient_id): count = cls.query( ndb.AND(cls.sender == sender_id, cls.recipient == recipient_id)).count(1) return count > 0 @classmethod def get_key_by_sender_recipient(cls, sender_id, recipient_id): return cls.query( ndb.AND(cls.sender == sender_id, cls.recipient == recipient_id)).get(keys_only=True) @classmethod def get_by_sender_recipient(cls, sender_id, recipient_id): return cls.query( ndb.AND(cls.sender == sender_id, cls.recipient == recipient_id)).get() @classmethod def exist_sent_or_received(cls, user_id): count = cls.query( ndb.OR(cls.sender == user_id, cls.recipient == user_id)).count(1) return count > 0 @classmethod def fetch_keys_sent_or_received(cls, user_id): return cls.query( ndb.OR(cls.sender == user_id, cls.recipient == user_id)).fetch(keys_only=True) #=== UTILITIES ================================================================ @classmethod def send_message(cls, sender_id, recipient_id, type): message = EnkiModelMessage(sender=sender_id, recipient=recipient_id, type=type) message.put() @classmethod def get_messages(cls, user_id): list = cls.fetch_by_recipient(user_id) message_list = [] if list: for i, item in enumerate(list): entity = EnkiModelDisplayName.get_by_user_id_current( item.sender) sender = EnkiModelDisplayName.get_user_id_display_name_url( entity) type = item.type message_id = item.key.id() message = messageData(message_id, type, sender) message_list.append(message) return message_list @classmethod def remove_message(cls, message_id): message = cls.get_by_id(message_id) if message: message.key.delete() @classmethod def remove_messages_crossed(cls, sender_or_receiver_a_id, sender_or_receiver_b_id): message_a = cls.get_by_sender_recipient(sender_or_receiver_a_id, sender_or_receiver_b_id) message_b = cls.get_by_sender_recipient(sender_or_receiver_b_id, sender_or_receiver_a_id) if message_a: if message_a.type == 'friend_request': message_a.key.delete() if message_b: if message_b.type == 'friend_request': message_b.key.delete() @classmethod def delete_user_messages(cls, user_id): ndb.delete_multi(cls.fetch_keys_sent_or_received(user_id))
class EnkiModelRestAPIConnectToken(model.Model): token = model.StringProperty() user_id = model.IntegerProperty() time_created = model.DateTimeProperty(auto_now_add=True)
class BlobInfo(model.Model): """Information about blobs in Blobstore. This is a Model subclass that has been doctored to be unwritable. Properties: - content_type: Content type of blob. - creation: Creation date of blob, when it was uploaded. - filename: Filename user selected from their machine. - size: Size of uncompressed blob. - md5_hash: The md5 hash value of the uploaded blob (in hex). Additional API: Class methods: - get(): retrieve a BlobInfo by key - get_multi(): retrieve a list of BlobInfos by keys - get_async(), get_multi_async(): async version of get() and get_multi() Instance methods: - delete(): delete this blob - delete_async(): async version of delete() - key(): return the BlobKey for this blob - open(): return a BlobReader instance for this blob Because BlobInfo instances are synchronized with Blobstore, the class cache policies are off. Do not subclass this class. """ _use_cache = False _use_memcache = False content_type = model.StringProperty() creation = model.DateTimeProperty() filename = model.StringProperty() size = model.IntegerProperty() md5_hash = model.StringProperty() @classmethod def _get_kind(cls): """Override this to match the datastore entities written by Blobstore.""" return BLOB_INFO_KIND @classmethod def get(cls, blob_key, **ctx_options): """Retrieve a BlobInfo by key. Args: blob_key: A blob key. This may be a str, unicode or BlobKey instance. **ctx_options: Context options for Model().get_by_id(). Returns: A BlobInfo entity associated with the provided key, If there was no such entity, returns None. """ fut = cls.get_async(blob_key, **ctx_options) return fut.get_result() @classmethod def get_async(cls, blob_key, **ctx_options): """Async version of get().""" if not isinstance(blob_key, (BlobKey, six.text_type, six.binary_type)): raise TypeError('Expected blob key, got %r' % (blob_key, )) if 'parent' in ctx_options: raise TypeError('Parent is not supported') blob_key_id = str(blob_key) if isinstance(blob_key, BlobKey) else blob_key return cls.get_by_id_async(six.ensure_binary(blob_key_id), **ctx_options) @classmethod def get_multi(cls, blob_keys, **ctx_options): """Multi-key version of get(). Args: blob_keys: A list of blob keys. **ctx_options: Context options for Model().get_by_id(). Returns: A list whose items are each either a BlobInfo entity or None. """ futs = cls.get_multi_async(blob_keys, **ctx_options) return [fut.get_result() for fut in futs] @classmethod def get_multi_async(cls, blob_keys, **ctx_options): """Async version of get_multi().""" for blob_key in blob_keys: if not isinstance(blob_key, (BlobKey, six.string_types)): raise TypeError('Expected blob key, got %r' % (blob_key, )) if 'parent' in ctx_options: raise TypeError('Parent is not supported') blob_key_strs = list(map(str, blob_keys)) keys = [model.Key(BLOB_INFO_KIND, id) for id in blob_key_strs] return model.get_multi_async(keys, **ctx_options) def _put_async(self, **ctx_options): """Cheap way to make BlobInfo entities read-only.""" raise TypeError('BlobInfo is read-only') put_async = _put_async def key(self): """Get key for blob. Returns: BlobKey instance that identifies this blob. """ return BlobKey(self._key.id()) def delete(self, **options): """Permanently delete this blob from Blobstore. Args: **options: Options for create_rpc(). """ fut = delete_async(self.key(), **options) fut.get_result() def delete_async(self, **options): """Async version of delete().""" return delete_async(self.key(), **options) def open(self, *args, **kwds): """Returns a BlobReader for this blob. Args: *args, **kwargs: Passed to BlobReader constructor. Returns: A BlobReader instance. """ return BlobReader(self, *args, **kwds)
class EnkiModelTokenAuth( model.Model ): token = model.StringProperty() # unique user_id = model.IntegerProperty() # the ndb ID nr time_created = model.DateTimeProperty( auto_now_add = True )
class MyUser(models.User): newsletter = model.BooleanProperty() age = model.IntegerProperty()
class Employee(model.Model): name = model.StringProperty() age = model.IntegerProperty()