Example #1
0
class AcarsPosition(model.Model):
    flight_id = model.StringProperty()
    message_id = model.StringProperty()
    system_time = model.DateTimeProperty(auto_now_add=True)
    remote_time = model.DateTimeProperty()
    message_type = model.StringProperty()
    flight_status = model.IntegerProperty()
    waypoint = model.StringProperty()
    lat_lon = model.GeoPtProperty()
    hdg = model.IntegerProperty()
    alt = model.IntegerProperty()
    vs = model.IntegerProperty()
    gs = model.IntegerProperty()
    ias = model.IntegerProperty()
    tas = model.IntegerProperty()
    fob = model.IntegerProperty()
    wnd = model.StringProperty()
    oat = model.IntegerProperty()
    tat = model.IntegerProperty()
    distance_from_dept = model.IntegerProperty()
    distance_total = model.IntegerProperty()
    pause_mode = model.IntegerProperty()
    airport = model.StringProperty()
    message = model.TextProperty()

    def add_position(self):
        self.put_async()
        if self.lat_lon:
            acars_flight = AcarsFlight.query(
                AcarsFlight.flight_id == self.flight_id).get()
            flight_position = FlightPosition(lat_lon=self.lat_lon,
                                             altitude=self.alt)
            acars_flight.flight_path.append(flight_position)
            acars_flight.put_async()
Example #2
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)
Example #3
0
class EnkiModelUser(model.Model):

    #=== MODEL ====================================================================

    # if logged in through enki auth, otherwise null
    email = model.StringProperty()  # unique
    password = model.StringProperty()

    # if logged in through external provider at least once, otherwise null. Format "provider:userId"
    auth_ids_provider = model.StringProperty(repeated=True)  # unique

    roles = model.StringProperty(repeated=True)

    time_created = model.DateTimeProperty(auto_now_add=True)
    time_updated = model.DateTimeProperty(auto_now=True)

    #=== QUERIES ==================================================================

    @classmethod
    def count(cls):
        count = EnkiModelUser.query().count()
        return count

    @classmethod
    def get_key_by_email(cls, email):
        return cls.query(cls.email == email).get(keys_only=True)

    @classmethod
    def get_by_email(cls, email):
        return cls.query(cls.email == email).get()

    @classmethod
    def exist_by_auth_id(cls, auth_id):
        count = cls.query(cls.auth_ids_provider == auth_id).count(1)
        return count > 0

    @classmethod
    def get_by_auth_id(cls, auth_id):
        return cls.query(cls.auth_ids_provider == auth_id).get()

    #=== UTILITIES ================================================================

    @classmethod
    def exist_by_email(cls, email):
        if email and email != 'removed':
            count = cls.query(cls.email == email).count(1)
            return count > 0
        return False

    @classmethod
    def has_password_by_email(cls, email):
        user = cls.get_by_email(email)
        if user.password:
            return True
        return False
Example #4
0
class EnkiModelUser(model.Model):

    # if logged in through enki auth, otherwise null
    email = model.StringProperty()  # unique
    password = model.StringProperty()

    # if logged in through external provider at least once, otherwise null. Format "provider:userId"
    auth_ids_provider = model.StringProperty(repeated=True)  # unique

    # other
    time_created = model.DateTimeProperty(auto_now_add=True)
    time_updated = model.DateTimeProperty(auto_now=True)
Example #5
0
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
Example #6
0
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 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)
Example #8
0
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
Example #9
0
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 )
Example #10
0
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 )
Example #11
0
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')
Example #12
0
class AcarsPirep(model.Model):
    time_report = model.DateTimeProperty(auto_now_add=True)
    acars_id = model.IntegerProperty()
    user_id = model.StringProperty()
    flight_number = model.StringProperty()
    ac_icao = model.StringProperty()
    cruise_alt = model.IntegerProperty()
    flight_type = model.StringProperty()
    departure = model.StringProperty()
    destination = model.StringProperty()
    alternate = model.StringProperty()
    dep_time = model.DateTimeProperty()
    block_time = model.IntegerProperty()
    block_fuel = model.IntegerProperty()
    flight_time = model.IntegerProperty()
    flight_fuel = model.IntegerProperty()
    pax = model.IntegerProperty()
    cargo = model.IntegerProperty()
    online = model.IntegerProperty()
    engine_start_ts = model.IntegerProperty()
    takeoff_ts = model.IntegerProperty()
    landing_ts = model.IntegerProperty()
    engine_stop_ts = model.IntegerProperty()
    zero_fuel_weight = model.IntegerProperty()
    take_off_weight = model.IntegerProperty()
    landing_weight = model.IntegerProperty()
    out_geo = model.GeoPtProperty()
    out_altitude = model.IntegerProperty()
    in_geo = model.GeoPtProperty()
    in_altitude = model.IntegerProperty()
    max_climb_rate = model.IntegerProperty()
    max_descend_rate = model.IntegerProperty()
    max_ias = model.IntegerProperty()
    max_gs = model.IntegerProperty()

    def add_pirep(self):
        self.put_async()

    def flights_for_user(self, user_id, limit=20, offset=0):
        return AcarsPirep.query(AcarsPirep.user_id == user_id).fetch(
            limit, offset=offset)

    flights_for_user = classmethod(flights_for_user)
Example #13
0
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)
Example #14
0
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)
Example #15
0
class Message(model.Model):
    
    username = model.StringProperty()
    picture_url = model.StringProperty()
    message = model.StringProperty()
    commit_hash = model.StringProperty(required=False)
    url = model.StringProperty(required=False)
    project = model.StringProperty(required=False)
    timestamp = model.DateTimeProperty(auto_now_add=True)
    
    def __str__(self):
        return '%s - %s' % (self.username, self.message)
    
    def __unicode__(self):
        return self.__str__()
    
    def to_json(self):
        data = self.to_dict(exclude=['timestamp'])
        return json.dumps(data)
    
    @classmethod
    def create_message(cls, username, picture_url, message, **kwargs):
        message = cls(username=username, picture_url=picture_url, message=message)
        message.populate(**kwargs)
        message.put()
        
        deferred.defer(send_live_message, message.key.urlsafe(), _queue="live")
        return message
    
    @classmethod
    def add_commit(cls, key):
        commit_key = model.Key(urlsafe=key)
        commit = commit_key.get()
        parent_key = commit_key.parent()
        if parent_key is None:
            return
        
        parent = parent_key.get()
        
        picture_url = getattr(parent, 'picture_url', '/static/images/spread_the_word_button.png')
        
        message = cls(username=parent.username, 
            picture_url=picture_url, message=commit.message[:200], url=commit.url,
            project=commit.project, commit_hash=commit.hash)
        message.put()
        
        deferred.defer(send_live_message, message.key.urlsafe(), _queue="live")
        return message
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
Example #17
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 EnkiModelRestAPIConnectToken(model.Model):

    token = model.StringProperty()
    user_id = model.IntegerProperty()
    time_created = model.DateTimeProperty(auto_now_add=True)
Example #19
0
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))
Example #20
0
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 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)
Example #22
0
class EnkiModelBackoffTimer(model.Model):
    # protect password entry against brute force attack
    identifier = model.StringProperty()
    last_failure = model.DateTimeProperty()
    backoff_duration = TimeDeltaProperty()
Example #23
0
class User(model.Expando):
    """Stores user authentication credentials or authorization ids."""

    #: The model used to ensure uniqueness.
    unique_model = Unique
    #: The model used to store tokens.
    token_model = UserToken

    created = model.DateTimeProperty(auto_now_add=True)
    updated = model.DateTimeProperty(auto_now=True)
    # ID for third party authentication, e.g. 'google:username'. UNIQUE.
    auth_ids = model.StringProperty(repeated=True)
    # Hashed password. Not required because third party authentication
    # doesn't use password.
    password = model.StringProperty()

    def get_id(self):
        """Returns this user's unique ID, which can be an integer or string."""
        return self._key.id()

    @classmethod
    def get_by_auth_id(cls, auth_id):
        """Returns a user object based on a auth_id.

        :param auth_id:
            String representing a unique id for the user. Examples:

            - own:username
            - google:username
        :returns:
            A user object.
        """
        return cls.query(cls.auth_ids == auth_id).get()

    @classmethod
    def get_by_auth_token(cls, user_id, token):
        """Returns a user object based on a user ID and token.

        :param user_id:
            The user_id of the requesting user.
        :param token:
            The token string to be verified.
        :returns:
            A tuple ``(User, timestamp)``, with a user object and
            the token timestamp, or ``(None, None)`` if both were not found.
        """
        token_key = cls.token_model.get_key(user_id, 'auth', token)
        user_key = model.Key(cls, user_id)
        # Use get_multi() to save a RPC call.
        valid_token, user = model.get_multi([token_key, user_key])
        if valid_token and user:
            timestamp = int(time.mktime(valid_token.created.timetuple()))
            return user, timestamp

        return None, None

    @classmethod
    def get_by_auth_password(cls, auth_id, password):
        """Returns a user object, validating password.

        :param auth_id:
            Authentication id.
        :param password:
            Password to be checked.
        :returns:
            A user object, if found and password matches.
        :raises:
            ``auth.InvalidAuthIdError`` or ``auth.InvalidPasswordError``.
        """
        user = cls.get_by_auth_id(auth_id)
        if not user:
            raise auth.InvalidAuthIdError()

        if not security.check_password_hash(password, user.password):
            raise auth.InvalidPasswordError()

        return user

    @classmethod
    def validate_token(cls, user_id, subject, token):
        """Checks for existence of a token, given user_id, subject and token.

        :param user_id:
            User unique ID.
        :param subject:
            The subject of the key. Examples:

            - 'auth'
            - 'signup'
        :param token:
            The token string to be validated.
        :returns:
            A :class:`UserToken` or None if the token does not exist.
        """
        return cls.token_model.get(user=user_id, subject=subject,
                                   token=token) is not None

    @classmethod
    def create_auth_token(cls, user_id):
        """Creates a new authorization token for a given user ID.

        :param user_id:
            User unique ID.
        :returns:
            A string with the authorization token.
        """
        return cls.token_model.create(user_id, 'auth').token

    @classmethod
    def validate_auth_token(cls, user_id, token):
        return cls.validate_token(user_id, 'auth', token)

    @classmethod
    def delete_auth_token(cls, user_id, token):
        """Deletes a given authorization token.

        :param user_id:
            User unique ID.
        :param token:
            A string with the authorization token.
        """
        cls.token_model.get_key(user_id, 'auth', token).delete()

    @classmethod
    def create_signup_token(cls, user_id):
        entity = cls.token_model.create(user_id, 'signup')
        return entity.token

    @classmethod
    def validate_signup_token(cls, user_id, token):
        return cls.validate_token(user_id, 'signup', token)

    @classmethod
    def delete_signup_token(cls, user_id, token):
        cls.token_model.get_key(user_id, 'signup', token).delete()

    @classmethod
    def create_user(cls, auth_id, unique_properties=None, **user_values):
        """Creates a new user.

        :param auth_id:
            A string that is unique to the user. Users may have multiple
            auth ids. Example auth ids:

            - own:username
            - own:[email protected]
            - google:username
            - yahoo:username

            The value of `auth_id` must be unique.
        :param unique_properties:
            Sequence of extra property names that must be unique.
        :param user_values:
            Keyword arguments to create a new user entity. Since the model is
            an ``Expando``, any provided custom properties will be saved.
            To hash a plain password, pass a keyword ``password_raw``.
        :returns:
            A tuple (boolean, info). The boolean indicates if the user
            was created. If creation succeeds, ``info`` is the user entity;
            otherwise it is a list of duplicated unique properties that
            caused creation to fail.
        """
        assert user_values.get('password') is None, \
            'Use password_raw instead of password to create new users.'

        assert not isinstance(auth_id, list), \
            'Creating a user with multiple auth_ids is not allowed, ' \
            'please provide a single auth_id.'

        if 'password_raw' in user_values:
            user_values['password'] = security.generate_password_hash(
                user_values.pop('password_raw'), length=12)

        user_values['auth_ids'] = [auth_id]
        user = cls(**user_values)

        # Set up unique properties.
        uniques = [('%s.auth_id:%s' % (cls.__name__, auth_id), 'auth_id')]
        if unique_properties:
            for name in unique_properties:
                key = '%s.%s:%s' % (cls.__name__, name, user_values[name])
                uniques.append((key, name))

        ok, existing = cls.unique_model.create_multi(k for k, v in uniques)
        if ok:
            user.put()
            return True, user
        else:
            properties = [v for k, v in uniques if k in existing]
            return False, properties
Example #24
0
class VoteChange(ndb.Model):
    voteId = model.StringProperty()
    when = model.DateTimeProperty()
    subscriberId = model.StringProperty()
Example #25
0
class EnkiModelTokenVerify(model.Model):

    token = model.StringProperty()
    email = model.StringProperty()
    user_id = model.IntegerProperty()  # ndb user ID
    time_created = model.DateTimeProperty(auto_now_add=True)
    type = model.StringProperty()
    auth_ids_provider = model.StringProperty(
    )  # store auth Id info for registration

    @classmethod
    def get_by_token(cls, token):
        entity = cls.query(cls.token == token).get()
        return entity

    @classmethod
    def get_by_token_type(cls, token, type):
        entity = cls.query(ndb.AND(cls.token == token, cls.type == type)).get()
        return entity

    @classmethod
    def get_by_user_id_email_type(cls, user_id, email, type):
        entity = cls.query(
            ndb.AND(cls.user_id == user_id, cls.email == email,
                    cls.type == type)).get()
        return entity

    @classmethod
    def get_by_user_id_auth_id_type(cls, user_id, auth_id, type):
        entity = cls.query(
            ndb.AND(cls.user_id == user_id, cls.auth_ids_provider == auth_id,
                    cls.type == type)).get()
        return entity

    @classmethod
    def get_by_user_id_type(cls, user_id, type):
        entity = cls.query(ndb.AND(cls.user_id == user_id,
                                   cls.type == type)).get()
        return entity

    @classmethod
    def get_by_auth_id_type(cls, auth_id, type):
        entity = cls.query(
            ndb.AND(cls.auth_ids_provider == auth_id, cls.type == type)).get()
        return entity

    @classmethod
    def fetch_by_user_id_type(cls, user_id, type):
        list = cls.query(
            ndb.AND(cls.user_id == user_id,
                    cls.type == type)).order(-cls.time_created).fetch()
        return list

    @classmethod
    def fetch_keys_by_user_id_type(cls, user_id, type):
        keys = cls.query(ndb.AND(cls.user_id == user_id,
                                 cls.type == type)).fetch(keys_only=True)
        return keys

    @classmethod
    def fetch_keys_by_user_id_except_type(cls, user_id, type):
        keys = cls.query(ndb.AND(cls.user_id == user_id,
                                 cls.type != type)).fetch(keys_only=True)
        return keys

    @classmethod
    def fetch_keys_by_email_type(cls, email, type):
        keys = cls.query(ndb.AND(cls.email == email,
                                 cls.type == type)).fetch(keys_only=True)
        return keys

    @classmethod
    def exist_by_token_type(cls, token, type):
        count = cls.query(ndb.AND(cls.token == token,
                                  cls.type == type)).count(1)
        return count > 0

    @classmethod
    def exist_by_user_id_token(cls, user_id, token):
        count = cls.query(ndb.AND(cls.user_id == user_id,
                                  cls.token == token)).count(1)
        return count > 0

    @classmethod
    def delete_by_user_id_token(cls, user_id, token):
        key = cls.query(ndb.AND(cls.user_id == user_id,
                                cls.token == token)).fetch(keys_only=True)
        if key:
            key[0].delete()
            return True
        return False

    @classmethod
    def delete_token_by_id(cls, token_id):
        ndb.Key(cls, int(token_id)).delete()

    @classmethod
    def fetch_old_tokens_by_types(cls, days_old, types):
        list = cls.query(
            ndb.AND(
                cls.type.IN(types), cls.time_created <=
                (datetime.datetime.now() -
                 datetime.timedelta(days=days_old)))).fetch(keys_only=True)
        return list
Example #26
0
class EnkiModelForum(model.Model):

    #=== MODEL ====================================================================

    title = model.StringProperty()
    description = model.StringProperty()
    group = model.StringProperty()  # group of forums
    group_order = model.IntegerProperty(
        default=0)  # order the groups appear in on the page
    forum_order = model.IntegerProperty(
        default=0)  # order the forums appear in 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)
    time_updated = model.DateTimeProperty(auto_now=True)

    #=== QUERIES ==================================================================

    @classmethod
    def exist(cls):
        count = cls.query().count(1)
        return count > 0

    @classmethod
    def fetch(cls):
        return cls.query().order(cls.group_order, cls.forum_order).fetch()

    #=== UTILITIES ================================================================

    @classmethod
    def create_forums(cls):
        # create an executable string from the forums settings to add the forums
        expression_forum = '''enki.modelforum.EnkiModelForum( group_order = {group_order}, forum_order = {forum_order}, group = "{group}", title = "{title}", description = "{description}" ), '''
        expression = "ndb.put_multi([ "
        increment = 10
        group_order = 0
        forum_order = 0
        current_group = ''
        for index, item in enumerate(settings.FORUMS):
            if item[0] != current_group:
                # new group: increment the group order index and reset the forum order index
                current_group = item[0]
                group_order += increment
                forum_order = increment
            else:
                forum_order += increment
            expression += expression_forum.format(group_order=group_order,
                                                  forum_order=forum_order,
                                                  group=current_group,
                                                  title=item[1],
                                                  description=item[2])
        expression += " ])"
        exec(expression)

    @classmethod
    def get_forums_data(cls):
        forums_data = []
        forums_list = cls.fetch()
        if forums_list:
            # get the groups from the list (ordered)
            groups = []
            for forum in forums_list:
                if forum.group not in groups:
                    groups.append(forum.group)
            # get the forums for each group (ordered)
            for group in groups:
                group_num_threads = 0
                group_num_posts = 0
                forums = []
                for forum in forums_list:
                    if forum.group == group:
                        group_num_threads += forum.num_threads
                        group_num_posts += forum.num_posts
                        url = enki.libutil.get_local_url(
                            'forum', {'forum': str(forum.key.id())})
                        forums.append({
                            'title': forum.title,
                            'description': forum.description,
                            'time_updated': forum.time_updated,
                            'num_threads': forum.num_threads,
                            'num_posts': forum.num_posts,
                            'url': url
                        })
                forums_data.append({
                    'name': group,
                    'num_threads': group_num_threads,
                    'num_posts': group_num_posts,
                    'forums': forums
                })
        return forums_data
Example #27
0
class EnkiModelMessage(model.Model):

    sender = model.IntegerProperty()
    recipient = model.IntegerProperty()
    type = model.StringProperty()
    time_created = model.DateTimeProperty(auto_now_add=True)
Example #28
0
class PlaceChange(ndb.Model):
    placeId = model.StringProperty()
    when = model.DateTimeProperty()
    subscriberId = model.StringProperty()
class EnkiModelEmailSubscriptions(model.Model):

    #=== MODEL ====================================================================

    email = model.StringProperty()
    newsletters = model.StringProperty(repeated=True)
    token = model.StringProperty()  # unsubscribe token

    time_created = model.DateTimeProperty(auto_now_add=True)
    time_updated = model.DateTimeProperty(auto_now=True)

    #=== CONSTANTS ================================================================

    #=== QUERIES ==================================================================

    @classmethod
    def exist_by_email(cls, email):
        count = cls.query(cls.email == email).count(1)
        return count > 0

    @classmethod
    def get_by_email(cls, email):
        return cls.query(cls.email == email).get()

    @classmethod
    def get_by_token(cls, token):
        return cls.query(cls.token == token).get()

    @classmethod
    def exist_by_email_newsletter(cls, email, newsletter):
        count = cls.query(
            ndb.AND(cls.email == email,
                    cls.newsletters == newsletter)).count(1)
        return count > 0

    @classmethod
    def get_by_email_newsletter(cls, email, newsletter):
        return cls.query(
            ndb.AND(cls.email == email, cls.newsletters == newsletter)).get()

    @classmethod
    def exist_by_token_newsletter(cls, token, newsletter):
        count = cls.query(
            ndb.AND(cls.token == token,
                    cls.newsletters == newsletter)).count(1)
        return count > 0

    @classmethod
    def fetch_keys_by_email(cls, email):
        return cls.query(cls.email == email).fetch(keys_only=True)

    @classmethod
    def count(cls):
        return cls.query().count()

    #=== UTILITIES ================================================================

    @classmethod
    def add_newsletter(cls, email, newsletter):
        entity = cls.get_by_email_newsletter(email, newsletter)
        if entity:
            return entity.token
        else:
            existing_entity = cls.get_by_email(email)
            if existing_entity:
                # add the newsletter to the list
                existing_entity.newsletters.append(newsletter)
                existing_entity.put()
                return existing_entity.token
            else:
                # create a new entity
                token = security.generate_random_string(entropy=256)
                new_entity = cls(email=email,
                                 newsletters=[newsletter],
                                 token=token)
                new_entity.put()
                return token

    @classmethod
    def remove_newsletter_by_email(cls, email, newsletter):
        if cls.exist_by_email_newsletter(email, newsletter):
            entity = cls.get_by_email(email)
            if len(entity.newsletters) == 1:
                # if no newsletter would be left after removal, delete the record
                entity.key.delete()
            else:
                # remove the newsletter.
                index = entity.newsletters.index(newsletter)
                del entity.newsletters[index]
                entity.put()

    @classmethod
    def remove_newsletter_by_token(cls, token, newsletter):
        if cls.exist_by_token_newsletter(token, newsletter):
            entity = cls.get_by_token(token)
            if len(entity.newsletters) == 1:
                # if no newsletter would be left after removal, delete the record
                entity.key.delete()
            else:
                # remove the newsletter
                index = entity.newsletters.index(newsletter)
                del entity.newsletters[index]
                entity.put()

    @classmethod
    def remove_by_email(cls, email):
        entities = cls.fetch_keys_by_email(email)
        if entities:
            ndb.delete_multi_async(entities)

    @classmethod
    def count_newsletters_by_email(cls, email):
        count = 0
        entity = cls.get_by_email(email)
        if entity:
            count = len(entity.newsletters)
        return count

    @classmethod
    def get_mailgun_email_batches(cls, newsletter):
        # get batches of email addresses and their respective recipient variables (unsubscribe tokens) for a given newsletter.
        # reference: https://documentation.mailgun.com/en/latest/user_manual.html#batch-sending
        BATCH_SIZE = 1000
        batches_emails = []
        batches_emails_recipient_variables = []
        results, next_cursor, more = cls.query(
            cls.newsletters == newsletter).fetch_page(BATCH_SIZE)
        batch_emails = ''
        batch_emails_recipient_variables = {}
        for result in results:
            batch_emails += result.email + ', '
            batch_emails_recipient_variables[result.email] = {
                'token': result.token
            }
        batches_emails.append(batch_emails[:-2])
        batches_emails_recipient_variables.append(
            batch_emails_recipient_variables)
        while (more and next_cursor):
            cursor = next_cursor
            results, next_cursor, more = cls.query(
                cls.newsletters == newsletter).fetch_page(BATCH_SIZE,
                                                          start_cursor=cursor)
            batch_emails = ''
            batch_emails_recipient_variables = {}
            for result in results:
                batch_emails += result.email + ', '
                batch_emails_recipient_variables[result.email] = {
                    'token': result.token
                }
            batches_emails.append(batch_emails[:-2])
            batches_emails_recipient_variables.append(
                batch_emails_recipient_variables)
        return batches_emails, batches_emails_recipient_variables
Example #30
0
class Fact(model.Model):
    author = model.StringProperty()
    poster_ip = model.StringProperty()
    posted_on = model.DateTimeProperty(auto_now_add=True)
    last_changed = model.DateTimeProperty(auto_now=True)
    text = model.TextProperty()
    language = model.StringProperty(default='en')  # The language of the post
    # For selecting random instances
    random_index = model.ComputedProperty(
        lambda self: random.randint(0, sys.maxint))
    # For the Elo rating system
    # see http://en.wikipedia.org/wiki/Elo_rating_system
    total_opponent_ratings = model.FloatProperty(default=0.)
    wins = model.IntegerProperty(default=0)
    losses = model.IntegerProperty(default=0)
    games = model.IntegerProperty(default=0)
    elo_rating = model.FloatProperty(default=400.)

    @property
    def k_factor(self):
        "Gives the correction (K) factor"
        if self.elo_rating <= 2100:
            return 32.
        elif self.elo_rating <= 2400:
            return 24.
        else:
            return 16.

    def expected_chance_against(self, fact):
        "Gives the expected odds of this fact winning a match with fact"
        return 1 / (1 + 10**((self.elo_rating + fact.elo_rating) / 400.))

    @tasklets.tasklet
    def won_over(self, fact):
        """
        Self won a match over another fact. Recalculates Elo ratings and saves
        them

        fact1.won_over(fact2)
        """
        if self.key == fact.key:
            raise ValueError('A fact cannot compete with itself')

        previous_elo_rating = self.elo_rating

        # +------+-----+
        # |Result|Score|
        # +------+-----+
        # |Win   |1    |
        # +------+-----+
        # |Draw  |0.5  |
        # +------+-----+
        # |Loss  |0    |
        # +------+-----+

        self.elo_rating = self.elo_rating + self.k_factor * \
            (1 - self.expected_chance_against(fact))
        self.total_opponent_ratings += fact.elo_rating
        self.games += 1
        self.wins += 1
        f1 = self.put_async()

        fact.elo_rating = fact.elo_rating - fact.k_factor * \
            (1 - fact.expected_chance_against(self))
        # TODO: check if Elo ratings can become negative
        fact.elo_rating = 0 if fact.elo_rating < 0 else fact.elo_rating
        fact.total_opponent_ratings += previous_elo_rating
        fact.games += 1
        fact.losses += 1
        f2 = fact.put_async()
        yield f1, f2

    def sync_won_over(self, fact):
        """
        Self won a match over another fact. Recalculates Elo ratings and saves
        them, but do it synchronously

        fact1.sync_won_over(fact2)
        """
        if self.key == fact.key:
            raise ValueError('A fact cannot compete with itself')

        previous_elo_rating = self.elo_rating

        self.elo_rating = self.elo_rating + self.k_factor * \
            (1 - self.expected_chance_against(fact))
        self.total_opponent_ratings += fact.elo_rating
        self.games += 1
        self.wins += 1
        self.put()

        fact.elo_rating = fact.elo_rating - fact.k_factor * \
            (1 - fact.expected_chance_against(self))
        # TODO: check if Elo ratings can become negative
        fact.elo_rating = 0 if fact.elo_rating < 0 else fact.elo_rating
        fact.total_opponent_ratings += previous_elo_rating
        fact.games += 1
        fact.losses += 1
        f2 = fact.put()

    @classmethod
    def random(cls, exclude=[]):
        "Returns a random instance"
        # TODO: do this asychronously
        f = None
        while not f:
            position = random.randint(1, sys.maxint)
            f = cls.query(cls.random_index >= position).get()
            if f and f.key in [e.key for e in exclude]:
                logging.error('got an excluded: ' + str(f))
                f = None  # Try again
        return f

    @classmethod
    def random_pair(cls):
        "Returns two random distinct facts"
        # TODO: do this asynchronously
        pos1 = random.randint(1, sys.maxint)
        f1 = cls.query(cls.random_index <= pos1).get()
        if not f1:
            f1 = cls.query(cls.random_index > pos1).get()
        pos2 = random.randint(1, sys.maxint)
        f2 = cls.query(cls.random_index <= pos1).get()
        if not f2:
            f2 = cls.query(cls.random_index > pos1).get()
        # f1 and f2 only must be resolved here
        if f1 == f2:
            f2 = cls.random(exclude=[f1])
        return (f1, f2)