예제 #1
0
class UserRedis(walrus.Model):
    __database__ = WALRUS_DB
    _id = walrus.AutoIncrementField(walrus.IntegerField)
    username = walrus.TextField()
    password = walrus.TextField()
    age = walrus.IntegerField()
    place = walrus.TextField()
    dob = walrus.DateTimeField()
예제 #2
0
class Player(walrus.Model):
    @staticmethod
    def new(session_id=uuid.uuid4(), user="******", wall=0) -> 'Player':
        player = Player.create(
            id=uuid.uuid4(),
            room=NULL_UUID,
            username=user,
            score=0,
        )
        player.paddle['pos'] = .5
        player.paddle['wall'] = wall

        player.save()
        return Player.load(player.id)

    def set_room(self, wall: int, room: uuid.UUID) -> 'Player':
        self.room = room
        self.paddle['wall'] = wall
        self.save()
        return Player.load(self.id)

    def updatePaddle(self, newPos):
        self.paddle['pos'] = newPos
        self.save()

    def to_json(self):
        ''' Recursively convert fields to json-friendly output.

        Return a dict with the following schema:
        {
          id: "uuid",
          score: int,
          paddle: {
            wall: int,
            pos:  float,
            },
        }
        '''
        return dict(
            id=str(self.id),
            score=self.score,
            paddle=dict(
                wall=as_int(self.paddle['wall']),
                pos=asFloat(self.paddle['pos']),
            ),
        )

    __database__ = walrus_conn
    id = walrus.UUIDField(primary_key=True, index=True)
    room = walrus.UUIDField()
    username = walrus.TextField()
    score = walrus.IntegerField()
    paddle = walrus.HashField()
예제 #3
0
class Task(walrus.Model):
    __database__ = redis_db

    id = walrus.TextField(primary_key=True)

    args = walrus.TextField()

    stdout = walrus.TextField()
    stderr = walrus.TextField()

    returncode = walrus.IntegerField()

    state = walrus.TextField()
예제 #4
0
class Room(walrus.Model):
    @staticmethod
    def new() -> 'Room':
        room = Room.create(
            id=uuid.uuid4(),
            lastUpdate=time(),
        )
        return room

    def add_player(self, player) -> Player:
        ''' Add a player to the room by id or instance and set their room field.

        Return the updated Player instance loaded from redis.
        player -- Type uuid.UUID or Player
        '''
        if not (isinstance(player, Player) or isinstance(player, uuid.UUID)):
                raise TypeError("Parameter must be of type"
                                "multipong.model.Player or uuid.UUID,"
                                "not {}".format(type(player)))
        if isinstance(player, Player):
            player = player.id
        self.players.add(player)
        self.wall += 2
        self.save()
        added = Player.load(player)
        added.set_room(self.wall - 2, self.id)
        self.__update_ball_count()
        # Return the updated player model
        added = Player.load(player)
        return added

    def __update_ball_count(self):
        ''' Check the ball count for the room to ensure that it is equal to the
        number of players currently playing.'''
        numPlayers = len(self.players)
        numBalls = len(self.balls)
        if numPlayers > numBalls:
            self.add_ball()
        elif numPlayers < numBalls:
            self.pop_last_ball()

    def remove_player(self, player) -> Player:
        ''' Remove player from room but do not delete instance.

        We also check to make sure that the room is balanced for the number
        of current players.
        '''
        if not (isinstance(player, Player) or isinstance(player, uuid.UUID)):
                raise TypeError("Parameter must be of type"
                                "multipong.model.Player or uuid.UUID,"
                                "not {}".format(type(player)))
        if isinstance(player, Player):
            player = player.id
        self.players.remove(player)
        self.wall -= 2
        self.save()
        self.__update_ball_count()
        return Player.load(player).set_room(wall=-1, room=NULL_UUID)

    def add_ball(self) -> Ball:
        ball = Ball.new()
        self.balls.append(ball.id)
        return ball

    def delete_ball(self, uid: uuid.UUID):
        del self.balls[uid]
        Ball.load(uid).delete()

    def pop_last_ball(self):
        self.balls.popright()

    def ball_at(self, index: int) -> Ball:
        ball_id = uuid.UUID(self.balls[index].decode('utf-8'))
        return Ball.load(ball_id)

    def moveBalls(self):
        elapsedTime = time() - self.lastUpdate
        self.lastUpdate = time()
        self.save()
        for ball in self.balls:
            ball = ball.decode('utf-8')
            Ball.load(ball).move(elapsedTime)

    def to_json(self) -> dict:
        ''' Recursively convert fields to json-friendly output.

        Return a dict with the following schema:
        {
          id: "uuid",
          balls: [<see Ball.to_json()>],
          players: [<see Player.to_json()>]
        }
        '''
        as_uuid = lambda obj: uuid.UUID(obj.decode('utf-8'))
        return dict(
                id=str(self.id),
                balls=list(map(
                    lambda ball: Ball.load(ball).to_json(),
                    map(as_uuid, self.balls))),
                players=list(map(
                    lambda player: Player.load(player).to_json(),
                    map(as_uuid, self.players))),
                )

    __database__ = walrus_conn
    id = walrus.UUIDField(primary_key=True, index=True)
    balls = walrus.ListField()
    players = walrus.SetField()
    wall = walrus.IntegerField(default=0)
    lastUpdate = walrus.FloatField(default=0)
예제 #5
0
class UserDetail(walrus.Model):
    """
    A walrus data model to store some user data to Redis to be
    synced to Postgres asynchronously.
    """
    __database__ = redis_connection
    __namespace__ = 'redash.user.details'

    user_id = walrus.IntegerField(index=True)
    updated_at = UTCDateTimeField(index=True, default=utcnow)

    @classmethod
    def update(cls, user_id):
        """
        Update the user details hash using the given redis
        pipeline, user id, optional redis id and optional user
        details.

        The fields uid, rid and updated (timestamp) are
        enforced and can't be overwritten.
        """
        # try getting the user detail with the given user ID
        # or create one if it doesn't exist yet (e.g. when key was purged)
        try:
            user_detail = cls.get(cls.user_id == user_id)
            # update the timestamp with the current time
            user_detail.updated_at = utcnow()
            # save to Redis
            user_detail.save()
        except ValueError:
            user_detail = cls.create(
                user_id=user_id,
                updated_at=utcnow(),
            )
        return user_detail

    @classmethod
    def sync(cls, chunksize=1000):
        """
        Syncs user details to Postgres (to the JSON field User.details).
        """
        to_sync = {}
        try:
            for user_detail in cls.all():
                to_sync[user_detail.user_id] = user_detail

            user_ids = list(to_sync.keys())
            if not user_ids:
                return
            logger.info('syncing users: %s',
                        ', '.join([str(uid) for uid in user_ids]))
            # get all SQLA users that need to be updated
            users = User.query.filter(User.id.in_(user_ids))
            for i, user in enumerate(users):
                update = to_sync[user.id]
                user.active_at = update.updated_at
                # flush changes to the database after a certain
                # number of items and extend the list of keys to
                # stop sync in case of exceptions
                if i % chunksize == 0:
                    db.session.flush()
            db.session.commit()
        except DBAPIError:
            # reset list of keys to stop sync
            pass
        finally:
            user_ids = [str(user_id) for user_id in to_sync.keys()]
            if user_ids:
                logger.info('Deleting temporary user details for users %s',
                            ', '.join(user_ids))
                delete_query = [
                    UserDetail.user_id == str(user_id) for user_id in user_ids
                ]
                UserDetail.query_delete(reduce(or_, delete_query))