Example #1
0
    def followed_posts(self):
        return Meal.query.join(followers, (followers.c.followed_id == Meal.user_id)).filter(followers.c.follower_id == self.id).order_by(Meal.timestamp.desc())

	id = db.Column(db.Integer, primary_key=True)
	username = db.Column(db.String(64), index=True, unique=True)
	email = db.Column(db.String(120), index=True, unique=True)
	password = db.Column(db.String(128))
	meals = db.relationship('Meal', backref='author', lazy='dynamic')
	authenticated = db.Column(db.Boolean, default=False)
	followed = db.relationship('User', 
							   secondary=followers, 
							   primaryjoin=(followers.c.follower_id == id), 
							   secondaryjoin=(followers.c.followed_id == id), 
							   backref=db.backref('followers', lazy='dynamic'), 
							   lazy='dynamic')

	def __repr__(self):
		return '<User %r>' % (self.username)

	def follow(self, user):
		if not self.is_following(user):
			self.followed.append(user)
			return self

	def unfollow(self, user):
		if self.is_following(user):
			self.followed.remove(user)
			return self

	def is_following(self, user):
		return self.followed.filter(followers.c.followed_id == user.id).count()

	def followed_posts(self):
		return Meal.query.join(followers, (followers.c.followed_id == Meal.user_id)).filter(followers.c.follower_id == self.id).order_by(Meal.timestamp.desc())
Example #2
0
class Carnet(db.Model):
    __tablename__ = 'carnet'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), index=True, unique=False)

    id_parent_carnet = db.Column(db.Integer,
                                 db.ForeignKey('carnet.id'),
                                 nullable=True)
    children_carnets = db.relationship('Carnet',
                                       backref=db.backref('parent',
                                                          remote_side=[id]))

    answers = db.relationship('Answer', backref='carnet', lazy='dynamic')

    def __repr__(self):
        return f'<Carnet{self.id} {self.name} [parent={Carnet.query.get(self.id_parent_carnet).name if self.id_parent_carnet else ""}]>'

    def eraze(self):
        for a in self.answers:
            a.eraze()

        for child in self.children_carnets:
            child.eraze()

        db.session.delete(self)
        db.session.commit()
        logger.info(f"Carnet erazed: {self}")

    def move(self, id_carnet):
        self.id_parent_carnet = id_carnet
        print(self.id_parent_carnet)
        db.session.commit()

        logger.info(f"Carnet moved: {self}")

    def add_answer(self, text_content):
        db.session.add(Answer(text_content=text_content, carnet=self))
        db.session.commit()
        logger.info(f"Answer added to carnet {self}")

    def add_carnet(self, name):
        db.session.add(Carnet(name=name, parent=self))
        db.session.commit()
        logger.info(f"carnet added to carnet {self}")

    def get_questions(self):
        questions = []
        for a in self.answers:
            questions += a.questions
        return questions

    def get_all_questions(self):
        questions = self.get_questions()
        for c in self.children_carnets:
            questions += c.get_all_questions()
        return questions

    def get_knowledge_composition(self):
        questions = self.get_all_questions()
        knowledge = [0, 0, 0, 0, 0]

        if len(questions) == 0:
            return knowledge

        for q in questions:
            last_eval = q.last_evaluation()
            if last_eval:
                knowledge[last_eval.result.value] += 1
            else:
                knowledge[0] += 1
        print(list(map(lambda x: int(x * 100 / len(questions)), knowledge)))
        return list(map(lambda x: int(x * 100 / len(questions)), knowledge))
Example #3
0
class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    #role = db.relationship(Role)
    avatar_hash = db.Column(db.String(32))
    confirmed = db.Column(db.Boolean, default=False)
    name = db.Column(db.String(64))
    location = db.Column(db.String(64))
    about_me = db.Column(db.Text())
    member_since = db.Column(db.DateTime(), default=datetime.utcnow)
    last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    followed = db.relationship('Follow', foreign_keys=[Follow.follower_id], backref=db.backref('follower', lazy='joined'), 
        lazy='dynamic', cascade='all, delete-orphan')
    followers = db.relationship('Follow', foreign_keys=[Follow.followed_id], backref=db.backref('followed', lazy='joined'), 
        lazy='dynamic', cascade='all, delete-orphan')
    comments = db.relationship('Comment', backref='author', lazy='dynamic')

    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        if self.role is None:
            if self.email == current_app.config['FLASKY_ADMIN']:
                self.role = Role.query.filter_by(name='Administrator').first()
            if self.role is None:
                self.role = Role.query.filter_by(default=True).first()
            #db.session.add(self)
            #db.session.commit()
        if self.email is not None and self.avatar_hash is None:
            self.avatar_hash = self.gravatar_hash()
        self.follow(self)
        
    def to_json(self):
        json_user = {
            'url': url_for('api.get_user', id=self.id),
            'username': self.username,
            'member_since': self.member_since,
            'last_seen': self.last_seen,
            'posts_url': url_for('api.get_user_posts', id=self.id, _external=True),
            'followed_posts_url': url_for('api.get_user_followed_posts', id=self.id, _external=True),
            'post_count': self.posts.count()
        }
        return json_user
        
    @staticmethod
    def add_self_follows():
        for user in User.query.all():
            if not user.is_following(user):
                user.follow(user)
                db.session.add(user)
                db.session.commit()
            
    @property
    def followed_posts(self):
        return Post.query.join(Follow, Follow.followed_id == Post.author_id).filter(Follow.follower_id==self.id)
            
    def follow(self, user):
        if not self.is_following(user):
            f = Follow(follower=self, followed=user)
            db.session.add(f)
            db.session.commit()
            
    def unfollow(self, user):
        f = self.followed.filter_by(followed_id=user.id).first()
        if f:
            db.session.delete(f)
            db.session.commit()
            
    def is_following(self, user):
        return self.followed.filter_by(followed_id=user.id).first() is not None
        
    def is_followed_by(self, user):
        return self.followers.filter_by(follower_id=user.id).first() is not None
            
    def gravatar(self, size=100, default='identicon', rating='g'):
        url = 'https://secure.gravatar.com/avatar'
        hash = self.avatar_hash or self.gravatar_hash()
        return '{url}/{hash}?s={size}&d={default}&r={rating}'.format(url=url, hash=hash, size=size, default=default, rating=rating)
    
    def can(self, perm):
        return self.role is not None and self.role.has_permission(perm)
        
    def is_administrator(self):
        return self.can(Permission.ADMIN)
        
    def ping(self):
        self.last_seen = datetime.utcnow()
        db.session.add(self)
        db.session.commit()
    
    def generate_confirmation_token(self, expiration=3600):
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'confirm': self.id}).decode('utf-8')
    
    def confirm(self, token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token.encode('utf-8'))
        except:
            return False
        if data.get('confirm') != self.id:
            return False
        self.confirmed = True
        db.session.add(self)
        db.session.commit()
        return True
    
    def __repr__(self):
        return '<User %r>' % self.username

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

    def generate_reset_token(self, expiration=3600):
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'reset': self.id}).decode('utf-8')

    @staticmethod
    def reset_password(token, new_password):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token.encode('utf-8'))
        except:
            return False
        user = User.query.get(data.get('reset'))
        if user is None:
            return False
        user.password = new_password
        db.session.add(user)
        db.session.commit()
        return True

    def generate_email_change_token(self, new_email, expiration=3600):
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'change_email': self.id, 'new_email': new_email}).decode('utf-8')
        
    def generate_auth_token(self, expiration):
        s = Serializer(current_app.config['SECRET_KEY'], expires_in = expiration)
        return s.dumps({'id': self.id}).decode('utf-8')
        
    @staticmethod
    def verify_auth_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token.encode('utf-8'))
        except:
            return None
        return User.query.get(data['id'])

    def change_email(self, token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token.encode('utf-8'))
        except:
            return False
        if data.get('change_email') != self.id:
            return False
        new_email = data.get('new_email')
        if new_email is None:
            return False
        if self.query.filter_by(email=new_email).first() is not None:
            return False
        self.email = new_email
        self.avatar_hash = self.gravatar_hash()
        db.session.add(self)
        db.session.commit()
        return True

    def gravatar_hash(self):
        return hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
        
    '''@staticmethod
Example #4
0
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    type = db.Column(db.String(20))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    name = db.Column(db.String(50))
    phone = db.Column(db.String(10))
    experience = db.Column(db.String(1000))
    departments = db.Column(db.String(100))
    why = db.Column(db.String(1000))

    applied = db.relationship('User',
                              secondary=applicants,
                              primaryjoin=(applicants.c.applicant_id == id),
                              secondaryjoin=(applicants.c.applied_id == id),
                              backref=db.backref('applicants', lazy='dynamic'),
                              lazy='dynamic')
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def avatar(self, size):
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(
            digest, size)

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def apply(self, user):
        if not self.is_applied(user):
            self.applied.append(user)

    def unapply(self, user):
        if self.is_applied(user):
            self.applied.remove(user)

    def is_applied(self, user):
        return self.applied.filter(
            applicants.c.applied_id == user.id).count() > 0

    def followed_posts(self):
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def applied_applicants(self):
        applied = User.query.join(applicants,
                                  (applicants.c.applied_id == User.id)).filter(
                                      applicants.c.applicant_id == self.id)
        return applied

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {
                'reset_password': self.id,
                'exp': time() + expires_in
            },
            app.config['SECRET_KEY'],
            algorithm='HS256').decode('utf-8')

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(token,
                            app.config['SECRET_KEY'],
                            algorithms=['HS256'])['reset_password']
        except:
            return
        return User.query.get(id)
Example #5
0
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    followed = db.relationship(
        'User', secondary=followers,
        primaryjoin=(followers.c.follower_id == id),
        secondaryjoin=(followers.c.followed_id == id),
        backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')

    # This method tells python how to print objects of this class, which
    # is going to be useful for debugging.
    def __repr__(self):
        return '<User {}>'.format(self.username)

    # With these two methods of generate and check password hashes, the
    # user is now able to do secure password generation and store passwords
    # Example: u = User(username = '******', email = '*****@*****.**')
    #          u.check_password('mypassword')
    #          u.set_password('mypassword')
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    # The new avatar() method returns the URL of the user's avatar image,
    # scaled to the requestsed size in pixels. For users that don't have an
    # avatar registered, an "identicon" image will be generated.
    def avatar(self, size):
        # To generate the MD5 hash, we first have to convert the emails to
        # lower case. then encode the string into bytes before passing it on
        # to the hash function.
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(digest, size)

    # Declare many to many relationships in the users table
    followed = db.relationship(
        # User is the right side entity of the relationship, left side entity
        # is the parent class. we use the same class on both sides since this is
        # a self referential relationship
        'User', secondary=followers,
        # Primaryjoin indicates the condition that links the left side entity with
        # the association table. The followers.c.follower_id expression reference the
        # follower_id column on the association table
        primaryjoin=(followers.c.follower_id == id),
        # Similar for primary join where we join followed id
        secondaryjoin=(followers.c.followed_id == id),
        # Backref defines how this relationship will be access from the right side entity.
        # From the left side, the relationship is named followed so from the right side
        # we are going to use the name followers to reperesent al the left side users
        # that are linked to the target user in the right side.
        backref=db.backref('followers', lazy='dynamic'), lazy='dynamic'
    )

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    # The is_following method isses a query on the followed relationship to
    # check if a link between two users already exists. It looks for items in
    # the association table that have the left side foreign key set to the self user,
    # and the right side set to the user argument.
    # The argument will either return a 0 or a 1
    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    #
    def followed_posts(self):
        # In the join operation on the posts table, the first argument is the
        # followers association table, and the second argument is the join
        # condition. This call creates a temp table that combines data from
        # posts and followers data tables. The data is going to be merged according
        # to the condition that we have passed as argument
        # The followed_id field must equal to the user_id of the posts table.
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
            followers.c.follower_id == self.id)
        # This does not apply to your own posts, so we query the posts table
        # and find id's for ourselves, then we union the table and order the
        # timestamp by time descending
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    # Generate a JWT token as a string, we need to decode the string in UTF-8 because
    # the encode function returns as a byte sequence, but its more convenient to have
    # the token as a string

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {'reset-password': self.id, 'exp': time() + expires_in},
            app.config['SECRET_KEY'], algorithm='HS256').decode('utf-8')

    # A static method can be invoked direct from the class. A static method is similar
    # to a class method, with the only difference being static methods do not receive
    # class as a first argument.
    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(token, app.config['SECRET_KEY'],
                            algorithms=['HS256'])['reset_password']
        except:
            return
        return User.query.get(id)
Example #6
0
class User(UserMixin, db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship("Post", backref='author', lazy="dynamic")
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.datetime.now())
    followed = db.relationship(
        'User', secondary=followers,
        primaryjoin=(followers.c.follower_id == id),
        secondaryjoin=(followers.c.followed_id == id),
        backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
    message_sent = db.relationship('Message',
                                   foreign_keys='Message.sender_id',
                                   backref='author', lazy='dynamic')
    message_received = db.relationship('Message',
                                       foreign_keys='Message.recipient_id',
                                       backref='recipient', lazy='dynamic')
    last_message_read_time = db.Column(db.DateTime)
    notifications = db.relationship('Notification', backref='user', lazy='dynamic')

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def set_password(self, password):
        """设置密码"""
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        """验证密码"""
        return check_password_hash(self.password_hash, password)

    def avatar(self, size):
        """头像设置"""
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(digest, size)

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id
        ).count() > 0

    def followed_posts(self):
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
            followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {'reset_password': self.id, 'exp': time() + expires_in},
            current_app.config['SECRET_KEY'], algorithm='HS256').decode('utf-8')

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(token, current_app.config['SECRET_KEY'],
                            algorithms=['HS256'])['reset_password']
        except:
            return
        return User.query.get(id)

    def new_message(self):
        last_read_time = self.last_message_read_time or datetime.datetime(1900, 1, 1)
        return Message.query.filter_by(recipient=self).filter(
            Message.timestamp > last_read_time
        ).count()

    def add_notification(self, name, data):
        self.notifications.filter_by(name=name).delete()
        n = Notification(name=name, payload_json=json.dumps(data), user=self)
        db.session.commit()
        return n
Example #7
0
class User(UserMixin, PaginatedAPIMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    longposts = db.relationship('LP', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    token = db.Column(db.String(32), index=True, unique=True)
    verified = db.Column(db.Boolean)
    darkmode = db.Column(db.Boolean)
    Staff = db.Column(db.Boolean)
    hindi = db.Column(db.Boolean)
    token_expiration = db.Column(db.DateTime)
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')
    messages_sent = db.relationship('Message',
                                    foreign_keys='Message.sender_id',
                                    backref='author',
                                    lazy='dynamic')
    messages_received = db.relationship('Message',
                                        foreign_keys='Message.recipient_id',
                                        backref='recipient',
                                        lazy='dynamic')
    last_message_read_time = db.Column(db.DateTime)
    notifications = db.relationship('Notification',
                                    backref='user',
                                    lazy='dynamic')
    tasks = db.relationship('Task', backref='user', lazy='dynamic')

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def avatar(self, size):
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(
            digest, size)

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {
                'reset_password': self.id,
                'exp': time() + expires_in
            },
            current_app.config['SECRET_KEY'],
            algorithm='HS256').decode('utf-8')

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(token,
                            current_app.config['SECRET_KEY'],
                            algorithms=['HS256'])['reset_password']
        except:
            return
        return User.query.get(id)

    def new_messages(self):
        last_read_time = self.last_message_read_time or datetime(1900, 1, 1)
        return Message.query.filter_by(recipient=self).filter(
            Message.timestamp > last_read_time).count()

    def add_notification(self, name, data):
        self.notifications.filter_by(name=name).delete()
        n = Notification(name=name, payload_json=json.dumps(data), user=self)
        db.session.add(n)
        return n

    def launch_task(self, name, description, *args, **kwargs):
        rq_job = current_app.task_queue.enqueue('app.tasks.' + name, self.id,
                                                *args, **kwargs)
        task = Task(id=rq_job.get_id(),
                    name=name,
                    description=description,
                    user=self)
        db.session.add(task)
        return task

    def get_tasks_in_progress(self):
        return Task.query.filter_by(user=self, complete=False).all()

    def get_task_in_progress(self, name):
        return Task.query.filter_by(name=name, user=self,
                                    complete=False).first()

    def to_dict(self, include_email=False):
        data = {
            'id': self.id,
            'username': self.username,
            'last_seen': self.last_seen.isoformat() + 'Z',
            'about_me': self.about_me,
            'post_count': self.posts.count(),
            'follower_count': self.followers.count(),
            'followed_count': self.followed.count(),
            'verified?': self.verified,
            'staff': self.Staff,
            'darkmode': self.darkmode,
            'hindi': self.hindi,
            '_links': {
                'self': url_for('api.get_user', id=self.id),
                'followers': url_for('api.get_followers', id=self.id),
                'followed': url_for('api.get_followed', id=self.id),
                'avatar': self.avatar(128)
            }
        }
        if include_email:
            data['email'] = self.email
        return data

    def from_dict(self, data, new_user=False):
        for field in ['username', 'email', 'about_me']:
            if field in data:
                setattr(self, field, data[field])
        if new_user and 'password' in data:
            self.set_password(data['password'])

    def get_token(self, expires_in=3600):
        now = datetime.utcnow()
        if self.token and self.token_expiration > now + timedelta(seconds=60):
            return self.token
        self.token = base64.b64encode(os.urandom(24)).decode('utf-8')
        self.token_expiration = now + timedelta(seconds=expires_in)
        db.session.add(self)
        return self.token

    def revoke_token(self):
        self.token_expiration = datetime.utcnow() - timedelta(seconds=1)

    @staticmethod
    def check_token(token):
        user = User.query.filter_by(token=token).first()
        if user is None or user.token_expiration < datetime.utcnow():
            return None
        return user
Example #8
0
class Post(db.Model, BaseMixin, DateTimeMixin):
    __tablename__ = 'posts'
    __searchable__ = ['title']
    __analyzer__ = SimpleAnalyzer()
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(64))
    body = db.Column(db.Text)
    author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    favorites = db.relationship('PostFavorite',
                                backref='post',
                                lazy='dynamic',
                                foreign_keys=[PostFavorite.post_id],
                                cascade='all,delete-orphan')
    tags = db.relationship('PostTag',
                           backref='post',
                           foreign_keys=[PostTag.post_id],
                           lazy='dynamic')  # 标签
    disable_comment = db.Column(db.Boolean, default=False)  # 是否禁止评论
    comments = db.relationship('Comment', backref='post',
                               lazy='dynamic')  # 属于文章的评论
    browse_count = db.Column(db.Integer, default=0)  # 文章被浏览了多少次
    liked_posts = db.relationship('LikePost',
                                  backref=db.backref('post_liked',
                                                     lazy='joined'),
                                  lazy='dynamic',
                                  foreign_keys=[LikePost.post_liked_id],
                                  cascade='all,delete-orphan')
    liked_count = db.Column(db.Integer, default=0)  # 以点赞数排序
    tag_count = db.Column(db.Integer)  # 标签数量
    comments_count = db.Column(db.Integer)

    def disable(self):
        self.disable_comment = True
        db.session.add(self)

    def browsed(self):
        count = self.browse_count
        count += 1
        self.browse_count = count
        db.session.add(self)

    def add_tag(self, tag):
        if not self.has_tag(tag):
            PostTag.create(post=self, tag=tag)
            return True
        return False

    def remove_tag(self, tag):
        f = self.tags.filter_by(tag_id=tag.id).first()
        if f:
            db.session.delete(f)
            return True
        return False

    def has_tag(self, tag):
        return self.tags.filter_by(tag_id=tag.id).first() is not None

    def is_liked_by(self, user):
        return self.liked_posts.filter_by(
            like_post_id=user.id).first() is not None

    @property
    def undelete_comments(self):
        return self.comments.filter(Comment.was_delete == False)
Example #9
0
class Favorite(db.Model, BaseMixin, DateTimeMixin):
    """收藏夹"""
    __tablename__ = 'favorites'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(64))  # 收藏夹的名称
    description = db.Column(db.Text)  # 描述
    public = db.Column(db.Boolean, default=True)  # 是否公开
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))  # 用户
    questions = db.relationship('QuestionFavorite',
                                backref='favorite',
                                lazy='dynamic',
                                foreign_keys=[QuestionFavorite.favorite_id],
                                cascade='all,delete-orphan')
    answers = db.relationship('AnswerFavorite',
                              backref='favorite',
                              lazy='dynamic',
                              foreign_keys=[AnswerFavorite.favorite_id],
                              cascade='all,delete-orphan')
    posts = db.relationship('PostFavorite',
                            backref='favorite',
                            lazy='dynamic',
                            foreign_keys=[PostFavorite.favorite_id],
                            cascade='all,delete-orphan')
    comments = db.relationship('Comment', backref='favorite', lazy='dynamic')
    followers = db.relationship('FollowFavorite',
                                backref=db.backref('followed', lazy='joined'),
                                lazy='dynamic',
                                foreign_keys=[FollowFavorite.followed_id],
                                cascade='all,delete-orphan')
    answers_count = db.Column(db.Integer)
    posts_count = db.Column(db.Integer)
    comments_count = db.Column(db.Integer)
    followers_count = db.Column(db.Integer)

    def collect(self, item):
        if isinstance(item, Answer):
            self.collect_answer(item)
        elif isinstance(item, Question):
            self.collect_question(item)
        elif isinstance(item, Post):
            self.collect_post(item)

    def uncollect(self, item):
        if isinstance(item, Answer):
            self.uncollect_answer(item)
        elif isinstance(item, Question):
            self.uncollect_question(item)
        elif isinstance(item, Post):
            self.uncollect_post(item)

    def collect_question(self, question):
        if not self.has_collect_question(question):
            QuestionFavorite.create(favorite=self, question=question)

    def uncollect_question(self, question):
        f = self.questions.filter_by(question_id=question.id).first()
        if f:
            db.session.delete(f)
            db.session.commit()

    def has_collect_question(self, question):
        return self.questions.filter_by(
            question_id=question.id).first() is not None

    def collect_answer(self, answer):
        if not self.has_collect_answer(answer):
            AnswerFavorite.create(favorite=self, answer=answer)

    def uncollect_answer(self, answer):
        f = self.answers.filter_by(answer_id=answer.id).first()
        if f:
            db.session.delete(f)
            db.session.commit()

    def has_collect_answer(self, answer):
        return self.answers.filter_by(answer_id=answer.id).first() is not None

    def collect_post(self, post):
        if not self.has_collect_post(post):
            PostFavorite.create(favorite=self, post=post)

    def uncollect_post(self, post):
        f = self.posts.filter_by(post_id=post.id).first()
        if f:
            db.session.delete(f)
            db.session.commit()

    def has_collect_post(self, post):
        return self.posts.filter_by(post_id=post.id).first() is not None

    def is_followed_by(self, user):
        return self.followers.filter_by(
            follower_id=user.id).first() is not None

    @property
    def undelete_comments(self):
        return self.comments.filter(Comment.was_delete == False)
Example #10
0
class User(ModelMixin, db.Model):
    __tablename__ = 'user'

    # FIXME unique?
    public_name = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(80), unique=True)
    password = db.Column(db.String(255))
    active = db.Column(db.Boolean(), default=False)
    confirmed_at = db.Column(db.DateTime())

    last_login_at = db.Column(db.DateTime())
    current_login_at = db.Column(db.DateTime())
    last_login_ip = db.Column(db.String(80))  # FIXME set to IP type?
    current_login_ip = db.Column(db.String(80))  # FIXME set to IP type?
    login_count = db.Column(db.Integer(), default=0)

    roles = db.relationship('Role',
                            secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))

    originating_shares = db.relationship('Share',
                                         backref='originator',
                                         lazy='dynamic',
                                         foreign_keys='Share.originator_id')

    receiving_shares = db.relationship('Share',
                                       backref='receiver',
                                       lazy='dynamic',
                                       foreign_keys='Share.receiver_id')

    def __init__(self, email, password=None, active=False, roles=[]):
        super(User, self).__init__()
        self.email = email

        if password:
            self.set_password(password, False)

        self.active = active
        self.roles += roles

    def get_name(self):
        return self.email

    def set_password(self, password, save=True):
        pwd_hash = pwd_context.encrypt(password)
        self.password = pwd_hash

        if save:
            self.save()

    def check_password(self, password, request=None):
        ok = pwd_context.verify(password, self.password)

        if not ok:
            return False

        if request:
            self.last_login_at = self.current_login_at
            self.current_login_at = datetime.utcnow()
            self.last_login_ip = self.current_login_ip
            self.current_login_ip = request.remote_addr
            self.login_count += 1
            self.save()

        return True

    def is_active(self):
        return self.active

    def get_id(self):
        return self.id

    @classmethod
    def get_by_username(cls, username):
        return cls.query.filter(cls.email == username).first()

    def is_authenticated(self):
        return True

    def is_anonymous(self):
        return False
Example #11
0
class Role(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), default='normal')
    users = db.relationship('Role',
                            secondary=user_roles,
                            backref=db.backref('User'))
Example #12
0
class User(db.Model, UserMixin):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)
    loginname = db.Column(db.String(10))
    username = db.Column(db.String(10))
    password = db.Column(db.String(16))
    active = db.Column(db.Boolean())
    roles = db.relationship('Role',
                            secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))

    def __init__(self, loginname, username, password, active, roles):
        self.loginname = loginname
        self.username = username
        self.password = password
        self.active = active
        self.roles = roles

    def __repr__(self):
        return '<User %r>' % self.username

    def to_json(self):
        return {
            'id': self.id,
            'username': self.username,
            'loginname': self.loginname,
            'active': self.active,
            'roles': self.get_roles(),
        }

    def get_roles(self):
        roleids = []
        if self.roles:
            for role in self.roles:
                role.to_json()
                roleids.append(role.id)
        return roleids

    def to_dict(self):
        return dict([(k, getattr(self, k)) for k in self.__dict__.keys()
                     if not k.startswith("_")])

    def verify_password(self, password):
        if self.password == password:
            return True
        return False

    @property
    def Is_Admin(self):
        if 'Administrator' in self.roles:
            return True
        return False

    @property
    def permissions(self):
        permissions = []
        if self.roles:
            for role in self.roles:
                if role.get_permissionIds():
                    permissions += role.get_permissionIds()
        return permissions

    # @property
    # def password_hash(self):
    #     raise AttributeError('password is not a readable attribute')
    #
    # @password_hash.setter
    # def password_hash(self, password):
    #     self.password = generate_password_hash(password)

    # def get_id(self):
    #     return unicode(self.id)

    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))
Example #13
0
class Organization(db.Model):
    __tablename__ = "organizations"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(200))
    active = db.Column(db.Boolean,
                       default=False,
                       server_default="0",
                       nullable=False)
    created_at = db.Column(db.DateTime(),
                           default=datetime.utcnow,
                           nullable=False)
    enable_shiftplanning_export = db.Column(db.Boolean,
                                            default=False,
                                            server_default="0",
                                            nullable=False)
    enable_timeclock_default = db.Column(db.Boolean,
                                         default=False,
                                         server_default="0",
                                         nullable=False)
    enable_time_off_requests_default = db.Column(db.Boolean,
                                                 default=False,
                                                 server_default="0",
                                                 nullable=False)
    day_week_starts = db.Column(db.String(200),
                                db.Enum("monday", "tuesday", "wednesday",
                                        "thursday", "friday", "saturday",
                                        "sunday"),
                                default="monday",
                                server_default="monday",
                                nullable=False)
    shifts_assigned_days_before_start = db.Column(db.Integer,
                                                  default=4,
                                                  server_default="4",
                                                  nullable=False)
    workers_can_claim_shifts_in_excess_of_max = db.Column(db.Boolean,
                                                          default=False,
                                                          server_default="0",
                                                          nullable=False)
    early_access = db.Column(db.Boolean,
                             default=False,
                             server_default="0",
                             nullable=False)

    enterprise_access = db.Column(db.Boolean,
                                  default=False,
                                  server_default="0",
                                  nullable=False)

    #
    # Billing
    #

    # Override service and let org be paid forever. Use this for contracts, demo accounts, etc
    # When does the org get locked out?
    paid_until = db.Column(db.DateTime(), default=None)
    # Who's the one paying
    billing_user_id = db.Column("billing_user_id",
                                db.Integer,
                                db.ForeignKey("users.id"),
                                index=True)
    # Stripe unique subscription id (for that user)
    stripe_customer_id = db.Column(db.String(200))

    # The id of the plan, both here in the app and on stripe
    plan = db.Column(db.String(200),
                     default="boss-v2",
                     server_default="per-seat-v1",
                     nullable=False)

    paid_labs_subscription_id = db.Column(db.String(200), nullable=True)

    # Can extend trial for certain people
    trial_days = db.Column(db.Integer,
                           default=30,
                           server_default="30",
                           nullable=False)

    def in_trial(self):
        if self.paid():
            return False

        return self.created_at + timedelta(
            days=self.trial_days) > datetime.now()

    def trial_days_remaining(self):
        if self.paid():
            return 0
        delta = self.created_at + timedelta(
            days=self.trial_days) - datetime.now()
        # This takes floor, so add 1 to round up
        days = delta.days + 1
        if days < 0:
            return 0
        return days

    def active_billing_plan(self):
        """ Keep it simple """
        return self.paid_labs_subscription_id is not None

    def paid(self):
        """ Return whether the account is in good standings """
        if self.paid_until is None:
            return False
        return self.paid_until >= datetime.now()

    def worker_count(self):
        """ Return the number of workers in the account """
        query = select([func.count(distinct(
            user_model.User.id))])\
            .where(Organization.id == self.id)\
            .where(RoleToUser.archived == False)\
            .where(Organization.id == Location.organization_id)\
            .where(Location.id == Role.location_id)\
            .where(Role.id == RoleToUser.role_id)\
            .where(RoleToUser.user_id == user_model.User.id)\
            .select_from(RoleToUser)\
            .select_from(Role)\
            .select_from(Location)\
            .select_from(Organization)\
            .select_from(user_model.User)

        # Filter out demo account and Staffjoy emails
        for email in current_app.config.get("KPI_EMAILS_TO_EXCLUDE"):
            query = query.where(not_(user_model.User.email.like(email)))

        workers = db.session.execute(query).fetchone()[0]
        return workers

    def set_paid_days(self, days):
        if self.paid_until is None:
            start = datetime.now()
        elif self.paid_until < datetime.now():
            start = datetime.now()
        else:
            start = self.paid_until

        self.paid_until = start + timedelta(days=days)

    admins = db.relationship("User",
                             secondary=organization_admins,
                             backref=db.backref("admin_of", lazy="dynamic"),
                             lazy="dynamic")

    locations = db.relationship("Location", backref=db.backref("organization"))

    def intercom_settings(self):
        """ Data for Intercom to pass to user model IN BROWSER"""

        data = {
            "company_id": str(self.id),
            "name": self.name,
            "remote_created_at": int(self.created_at.strftime("%s")),
            "custom_attributes": {
                "active": self.active,
                "paid": self.paid(),
                "plan": self.plan,
                "enterprise_access": self.enterprise_access,
            }
        }

        return data

    def get_ordered_week(self):
        """returns a list of the days of the week where day_week_starts is first"""

        wrap_index = DAYS_OF_WEEK.index(self.day_week_starts)
        return DAYS_OF_WEEK[wrap_index:] + DAYS_OF_WEEK[:wrap_index]

    def is_plan_boss(self):
        """determines if the current plan is part of Staffjoy Boss"""
        return self.plan in boss_plans

    def is_plan_flex(self):
        """determines if the current plan is part of Staffjoy Flex"""
        return self.plan in flex_plans

    def get_week_start_from_datetime(self, current_day):
        """
        takes current_day and the org_id and returns a datetime for the start of
        the week

        DISCLAIMER - this does not normalize to midnight or pay attention to tzinfo
        """

        week_start_index = DAYS_OF_WEEK.index(self.day_week_starts)
        adjust_days = (WEEK_LENGTH - week_start_index +
                       current_day.weekday()) % WEEK_LENGTH
        return current_day - timedelta(days=adjust_days)
Example #14
0
class User(db.Model, UserMixin, ModeloComIdMixin):
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))  # Password criptografado
    posts = db.relationship("Post", backref="author", lazy="dynamic")
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    # Followed -> Sendo seguido
    # Follower -> Seguindo alguém
    followed = db.relationship(
        "User",
        secondary=followers,
        primaryjoin=("followers.c.follower_id == User.id"),
        secondaryjoin=("followers.c.followed_id == User.id"),
        backref=db.backref("followers", lazy="dynamic"),
        lazy="dynamic",
    )

    def __repr__(self):
        return "<User {}>".format(self.username)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def avatar(self, size):
        digest = md5(self.email.lower().encode("utf-8")).hexdigest()
        return "https://www.gravatar.com/avatar/{}?d=identicon&s={}".format(
            digest, size
        )

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user) -> bool:
        return self.followed.filter(followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)
        ).filter(followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def followed_posts_v2(self):
        query = """
        select * from post p
        join followers f on f.followed_id == p.user_id
        join user u on u.id == p.user_id
        where f.follower_id == user.id
        order by p.timestamp desc;
        """
        resultado = db.session.execute(query)
        return resultado

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {"reset_password": self.id, "exp": time() + expires_in},
            app.config["SECRET_KEY"],
            algorithm="HS256",
        ).decode("utf-8")

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(token, app.config["SECRET_KEY"], algorithms=["HS256"])[
                "reset_password"
            ]
        except Exception:  # InvalidSignatureError:
            return
        return User.query.get(id)
Example #15
0
class Tournament(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.VARCHAR(64), unique=True)
    tasks = db.relationship('Task', secondary=association_task_table,
                            backref=db.backref('tasks', lazy='dynamic'))
Example #16
0
class User(db.Model, UserMixin, BaseMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64))
    email = db.Column(db.String(64))
    password_hash = db.Column(db.String(64))
    name = db.Column(db.String(64))
    location = db.Column(db.String(64))
    job = db.Column(db.String(64))
    about_me = db.Column(db.String(64))
    avatar_hash = db.Column(db.String(32))  # 默认头像
    avatar_url_sm = db.Column(db.String(256))  # 自定义头像缩略图
    avatar_url_nm = db.Column(db.String(256))  # 自定义头像正常图
    last_seen = db.Column(db.DateTime,
                          default=datetime.utcnow,
                          onupdate=datetime.utcnow)  # 最近登录
    member_since = db.Column(db.DateTime, default=datetime.utcnow)  # 注册日期
    visited_count = db.Column(db.Integer, default=0)  # 个人主页被浏览次数
    confirmed = db.Column(db.Boolean, default=False)  # 是否已经认证
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    questions = db.relationship('Question', backref='author',
                                lazy='dynamic')  # 提出的问题
    answers = db.relationship('Answer', backref='author',
                              lazy='dynamic')  # 用户的回答
    topics = db.relationship('Topic', backref='author',
                             lazy='dynamic')  # 创建的话题
    posts = db.relationship('Post', backref='author', lazy='dynamic')  # 我的文章
    comments = db.relationship('Comment',
                               backref='author',
                               lazy='dynamic',
                               foreign_keys=[Comment.author_id])  # 我的评论
    replies = db.relationship('Reply',
                              backref='author',
                              lazy='dynamic',
                              foreign_keys=[Reply.author_id])  # 我的回复
    received_replies = db.relationship('Reply',
                                       backref='user',
                                       lazy='dynamic',
                                       foreign_keys=[Reply.user_id])  # 回复我的
    private_messages = db.relationship('Message',
                                       backref='sender',
                                       lazy='dynamic',
                                       foreign_keys=[Message.sender_id
                                                     ])  # 我发送的私信
    private_messages_from = db.relationship('Message',
                                            backref='receiver',
                                            lazy='dynamic',
                                            foreign_keys=[Message.receiver_id
                                                          ])  # 我接收的私信

    favorites = db.relationship('Favorite', backref='user',
                                lazy='dynamic')  # 收藏夹
    followers = db.relationship('Follow',
                                backref=db.backref('followed', lazy='joined'),
                                foreign_keys=[Follow.followed_id],
                                lazy='dynamic',
                                cascade='all,delete-orphan')  # 关注者
    followed = db.relationship('Follow',
                               backref=db.backref('follower', lazy='joined'),
                               foreign_keys=[Follow.follower_id],
                               lazy='dynamic',
                               cascade='all,delete-orphan')  # 被关注者
    followed_questions = db.relationship(
        'FollowQuestion',
        backref=db.backref('follower', lazy='joined'),
        foreign_keys=[FollowQuestion.follower_id],
        lazy='dynamic',
        cascade='all,delete-orphan')  # 关注的问题
    followed_favorites = db.relationship(
        'FollowFavorite',
        backref=db.backref('follower', lazy='joined'),
        foreign_keys=[FollowFavorite.follower_id],
        lazy='dynamic',
        cascade='all,delete-orphan')  # 关注的收藏夹
    followed_topics = db.relationship('FollowTopic',
                                      backref=db.backref('follower',
                                                         lazy='joined'),
                                      foreign_keys=[FollowTopic.follower_id],
                                      lazy='dynamic',
                                      cascade='all,delete-orphan')  # 关注的收藏夹
    answer_likes = db.relationship('LikeAnswer',
                                   backref=db.backref('like_answer',
                                                      lazy='joined'),
                                   lazy='dynamic',
                                   foreign_keys=[LikeAnswer.like_answer_id],
                                   cascade='all,delete-orphan')  # 赞过的答案
    post_likes = db.relationship('LikePost',
                                 backref=db.backref('like_post',
                                                    lazy='joined'),
                                 lazy='dynamic',
                                 foreign_keys=[LikePost.like_post_id],
                                 cascade='all,delete-orphan')
    comment_likes = db.relationship('LikeComment',
                                    backref=db.backref('like_comment',
                                                       lazy='joined'),
                                    lazy='dynamic',
                                    foreign_keys=[LikeComment.like_comment_id],
                                    cascade='all,delete-orphan')
    reply_likes = db.relationship('LikeReply',
                                  backref=db.backref('like_reply',
                                                     lazy='joined'),
                                  lazy='dynamic',
                                  foreign_keys=[LikeReply.like_reply_id],
                                  cascade='all,delete-orphan')

    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        if self.role is None:
            if self.email == current_app.config['ZHIDAO_ADMIN']:
                self.role = Role.query.filter_by(permission=0xff).first()
            if self.role is None:
                self.role = Role.query.filter_by(default=True).first()
        if self.email is not None and self.avatar_hash is None:
            self.avatar_hash = hashlib.md5(
                self.email.encode('utf-8')).hexdigest()
        User.add_self_follows()

    def follow(self, item):
        if isinstance(item, User):
            self.follow_user(item)
        elif isinstance(item, Question):
            self.follow_question(item)
        elif isinstance(item, Favorite):
            self.follow_favorite(item)
        elif isinstance(item, Topic):
            self.follow_topic(item)
        else:
            raise TypeError('参数错误')

    def unfollow(self, item):
        if isinstance(item, User):
            self.unfollow_user(item)
        elif isinstance(item, Question):
            self.unfollow_question(item)
        elif isinstance(item, Favorite):
            self.unfollow_favorite(item)
        elif isinstance(item, Topic):
            self.unfollow_topic(item)
        else:
            raise TypeError('参数错误')

    @staticmethod
    def add_self_follows():
        for user in User.query.all():
            if not user.is_following_user(user):
                user.follow(user)
                db.session.add(user)

    def follow_user(self, user):
        if not self.is_following_user(user):
            Follow.create(follower=self, followed=user)

    def unfollow_user(self, user):
        f = self.followed.filter_by(followed_id=user.id).first()
        if f:
            db.session.delete(f)
            db.session.commit()

    def is_following_user(self, user):
        return self.followed.filter_by(followed_id=user.id).first() is not None

    def is_followed_by_user(self, user):
        return self.followers.filter_by(
            follower_id=user.id).first() is not None

    def is_friend(self, user):
        return self.is_following_user(user) and self.is_followed_by_user(user)

    @property
    def friends(self):
        followers = [i.follower for i in self.followers]
        followed = [i.followed for i in self.followed]
        users = list(set(followers + followed))
        friends = [i for i in users if self.is_friend(i)]
        return friends

    def follow_question(self, question):
        if not self.is_following_question(question):
            FollowQuestion.create(follower=self, followed=question)

    def unfollow_question(self, question):
        f = self.followed_questions.filter_by(followed_id=question.id).first()
        if f:
            db.session.delete(f)
            db.session.commit()

    def is_following_question(self, question):
        return self.followed_questions.filter_by(
            followed_id=question.id).first() is not None

    def follow_favorite(self, favorite):
        if not self.is_following_favorite(favorite):
            FollowFavorite.create(follower=self, followed=favorite)

    def unfollow_favorite(self, favorite):
        f = self.followed_favorites.filter_by(followed_id=favorite.id).first()
        if f:
            db.session.delete(f)
            db.session.commit()

    def is_following_favorite(self, favorite):
        return self.followed_favorites.filter_by(
            followed_id=favorite.id).first() is not None

    def follow_topic(self, topic):
        if not self.is_following_topic(topic):
            FollowTopic.create(follower=self, followed=topic)

    def unfollow_topic(self, topic):
        f = self.followed_topics.filter_by(followed_id=topic.id).first()
        if f:
            db.session.delete(f)
            db.session.commit()

    def is_following_topic(self, topic):
        return self.followed_topics.filter_by(
            followed_id=topic.id).first() is not None

    @property
    def followed_user_questions(self):
        """除了自己的关注的人的提问"""
        return Question.query.join(Follow,
                                   db.and_(Follow.followed_id == Question.author_id, Follow.followed_id != self.id)). \
            filter(Follow.follower_id == self.id)

    @property
    def followed_user_answers(self):
        """
        除了自己的关注的人的回答
        :return:
        """
        return Answer.query.join(Follow,
                                 db.and_(Follow.followed_id == Answer.author_id, Follow.followed_id != self.id)). \
            filter(Follow.follower_id == self.id)

    @property
    def followed_user_posts(self):
        return Post.query.join(Follow, db.and_(Follow.followed_id == Post.author_id, Follow.followed_id != self.id)). \
            filter(Follow.follower_id == self.id)

    @property
    def followed_user_favorites(self):
        return Favorite.query.join(Follow,
                                   db.and_(Follow.followed_id == Favorite.user_id, Follow.followed_id != self.id)). \
            filter(Follow.follower_id == self.id)

    def who_is_following_current_user_in_my_followed(self, user):
        """
        除自己之外我关注的人里面谁也关注了他,返回两条数据
        :param user:
        :return:
        """

        return [
            i.followed
            for i in self.followed.order_by(Follow.timestamp.desc()).all()
            if i.followed.is_following_user(user) and user != i.followed
        ][:2]

    def like(self, item):
        if isinstance(item, Answer):
            self.like_answer(item)
        elif isinstance(item, Post):
            self.like_post(item)
        elif isinstance(item, Comment):
            self.like_comment(item)
        elif isinstance(item, Reply):
            self.like_reply(item)

    def unlike(self, item):
        if isinstance(item, Answer):
            self.unlike_answer(item)
        elif isinstance(item, Post):
            self.unlike_post(item)
        elif isinstance(item, Comment):
            self.unlike_comment(item)
        elif isinstance(item, Reply):
            self.unlike_reply(item)

    def like_answer(self, answer):
        if not self.is_like_answer(answer):
            f = LikeAnswer.create(like_answer=self, answer_liked=answer)

    def like_post(self, post):
        if not self.is_like_post(post):
            f = LikePost.create(like_post=self, post_liked=post)

    def like_comment(self, comment):
        if not self.is_like_comment(comment):
            LikeComment.create(like_comment=self, comment_liked=comment)

    def like_reply(self, reply):
        if not self.is_like_reply(reply):
            LikeReply.create(like_reply=self, reply_liked=reply)

    def unlike_answer(self, answer):
        f = self.answer_likes.filter_by(answer_liked_id=answer.id).first()
        if f:
            db.session.delete(f)

    def unlike_post(self, post):
        f = self.post_likes.filter_by(post_liked_id=post.id).first()
        if f:
            db.session.delete(f)

    def unlike_comment(self, comment):
        f = self.comment_likes.filter_by(comment_liked_id=comment.id).first()
        if f:
            db.session.delete(f)

    def unlike_reply(self, reply):
        f = self.reply_likes.filter_by(reply_liked_id=reply.id).first()
        if f:
            db.session.delete(f)

    def is_like_answer(self, answer):
        return self.answer_likes.filter_by(
            answer_liked_id=answer.id).first() is not None

    def is_like_post(self, post):
        return self.post_likes.filter_by(
            post_liked_id=post.id).first() is not None

    def is_like_comment(self, comment):
        return self.comment_likes.filter_by(
            comment_liked_id=comment.id).first() is not None

    def is_like_reply(self, reply):
        return self.reply_likes.filter_by(
            reply_liked_id=reply.id).first() is not None

    def has_collect_question(self, question):

        return any(quest.question == question for favorite in self.favorites
                   for quest in favorite.questions.all())

    def has_collect_answer(self, answer):
        return any(ans.answer == answer for favorite in self.favorites
                   for ans in favorite.answers.all())

    def has_collect_post(self, post):
        return any(pos.post == post for favorite in self.favorites
                   for pos in favorite.posts.all())

    def has_collect(self, item):
        if isinstance(item, Answer):
            return self.has_collect_answer(item)
        elif isinstance(item, Question):
            return self.has_collect_question(item)
        elif isinstance(item, Post):
            return self.has_collect_post(item)
        else:
            raise TypeError('参数类型错误')

    def collect(self, item, favorite):
        if isinstance(item, Answer):
            fa = self.favorites.filter_by(id=favorite.id).first()
            if fa:
                fa.collect_answer(item)
        if isinstance(item, Question):
            fa = self.favorites.filter_by(id=favorite.id).first()
            if fa:
                fa.collect_question(item)
        if isinstance(item, Post):
            fa = self.favorites.filter_by(id=favorite.id).first()
            if fa:
                fa.collect_post(item)

    def uncollect(self, item, favorite):
        if isinstance(item, Answer):
            fa = self.favorites.filter_by(id=favorite.id).first()
            if fa:
                fa.uncollect_answer(item)
        if isinstance(item, Question):
            fa = self.favorites.filter_by(id=favorite.id).first()
            if fa:
                fa.uncollect_question(item)
        if isinstance(item, Post):
            fa = self.favorites.filter_by(id=favorite.id).first()
            if fa:
                fa.uncollect_post(item)

    def gravatar(self, size=100, default='identicon', rating='g'):
        if request.is_secure:
            url = 'https://secure.gravatar.com/avatar'
        else:
            url = 'http://secure.gravatar.com/avatar'
        hash = self.avatar_hash or hashlib.md5(
            self.email.encode('utf-8')).hexdigest()
        return '{url}/{hash}?s={size}&d={default}&r={rating}'.format(
            url=url, hash=hash, size=size, default=default, rating=rating)

    def send_standard_message_to(self, content, to):
        Message.create(sender=self, receiver=to, message_content=content)

    def send_system_message(self, content):
        for user in User.query.all():
            if user.id == self.id:
                Message.create(sender=self,
                               receiver=user,
                               message_content=content,
                               message_type=MessageType.system)
            Message.create(sender=self,
                           receiver=user,
                           message_content=content,
                           message_type=MessageType.system,
                           delete_status=DeleteStatus.author_delete)

    def delete_send_box_msg(self, msg):
        """
        如果删除状态为标准,设置为author_delete
        如果删除状态为user_delete,说明接收者已经将其删除,可以真正删除
        :param msg:
        :return:
        """
        message = self.private_messages.filter_by(id=msg.id).first()
        if message.delete_status == DeleteStatus.standard:
            message.delete_status = DeleteStatus.author_delete
            db.session.add(message)
        elif message.delete_status == DeleteStatus.user_delete:
            db.session.delete(message)
        db.session.commit()

    def delete_in_box_msg(self, msg):
        """
        如果删除状态为标准,设置为user_delete
        如果删除状态为author_delete,说明发送者已经删除该消息,可以将其真正删除
        :param msg:
        :return:
        """
        message = self.private_messages_from.filter_by(id=msg.id).first()
        if message.delete_status == DeleteStatus.standard:
            message.delete_status = DeleteStatus.user_delete
            db.session.add(message)
        elif message.delete_status == DeleteStatus.author_delete:
            db.session.delete(message)
        db.session.commit()

    @property
    def send_box_messages(self):
        """
        :return:
        """
        return self.private_messages.filter(
            Message.delete_status != DeleteStatus.author_delete)

    @property
    def in_box_messages(self):
        """
        收件箱全部信息,包括未读的
        :return:
        """
        return self.private_messages_from.filter(
            Message.delete_status != DeleteStatus.user_delete)

    def set_messages_read(self):
        for message in self.in_box_messages.all():
            message.status = MessageStatus.read
            db.session.add(message)
        db.session.commit()

    @property
    def in_box_message_unread_count(self):
        return self.private_messages_from.filter(
            Message.delete_status != DeleteStatus.user_delete,
            Message.status == MessageStatus.unread).count()

    def dialogue_with(self, whom):
        """
        双方都删除也会保留对话内容
        :param whom:对话人物
        :return:
        """

        return Message.query.filter(
            db.or_(db.and_(Message.sender == self, Message.receiver == whom),
                   db.and_(Message.sender == whom, Message.receiver == self)),
            Message.message_type == MessageType.standard)

    @property
    def total_liked_answers_count(self):
        """
        所有答案的总赞同
        :return:
        """
        return sum(i.liked_answers.count() for i in self.answers)

    def delete_comment(self, comment):
        if comment.replies.count() == 0:
            db.session.delete(comment)
        else:
            comment.was_delete = True
            db.session.add(comment)
        db.session.commit()

    def delete_reply(self, reply):
        if reply.comment.was_delete == True and reply.comment.replies.count(
        ) == 1:
            db.session.delete(reply.comment)
        else:
            db.session.delete(reply)
        db.session.commit()

    @hybrid_property
    def visited(self):
        return self.visited_count

    @visited.setter
    def visited(self, val):
        self.visited_count = val
        db.session.add(self)
        db.session.commit()

    def can(self, permissions):
        return self.role is not None and (self.role.permissions
                                          & permissions) == permissions

    def is_administrator(self):
        return self.can(Permission.ADMINISTER)

    @property
    def password(self):
        raise AttributeError('禁止访问')

    @password.setter
    def password(self, password):
        self.password_hash = bcrypt.generate_password_hash(password)

    def verify_password(self, password):
        return bcrypt.check_password_hash(self.password_hash, password)

    def generate_confirm_token(self, experition=3600):
        """生成token"""
        s = Serializer(current_app.config['SECRET_KEY'], expires_in=experition)
        return s.dumps({'confirm.txt': self.id})

    def generate_reset_token(self, experition=60 * 60):
        """生成token"""
        s = Serializer(current_app.config['SECRET_KEY'], expires_in=experition)
        return s.dumps({'reset': self.id})

    def generate_email_change_token(self, new_email, expiration=3600):
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'change_email': self.id, 'new_email': new_email})

    def reset_password(self, token, password):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except:
            return False
        if data and data.get('reset') == self.id:
            self.password = password
            db.session.add(self)
            return True
        else:
            return False

    def change_email(self, token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except:
            return False
        if data.get('change_email') != self.id:
            return False
        new_email = data.get('new_email')
        if new_email is None:
            return False
        if self.query.filter_by(email=new_email).first() is not None:
            return False
        self.email = new_email
        self.avatar_hash = hashlib.md5(self.email.encode('utf-8')).hexdigest()
        db.session.add(self)
        return True

    def confirm(self, token):
        """令牌验证,反序列化,数据存在并且令牌没被篡改,返回True"""
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except:
            return False
        if data and data.get('confirm.txt') == self.id:
            self.confirmed = True
            db.session.add(self)
            return True
        else:
            return False

    @staticmethod
    def verify_api_auth_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except SignatureExpired:
            return None
        except BadSignature:
            return None
        user = User.query.get(data.get('id'))
        return user

    def __repr__(self):
        return '<User{}>'.format(self.username)
Example #17
0
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    role = db.Column(db.SmallInteger, default=ROLE_USER)
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(1024))
    last_seen = db.Column(db.DateTime)
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')

    def __repr__(self):
        return '<User %r>' % (self.nickname)

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return unicode(self.id)

    def avatar(self, size):
        return 'http://www.gravatar.com/avatar/' + md5(
            self.email).hexdigest() + '?d=mm&s=' + str(size)

    @staticmethod
    def make_unique_nickname(nickname):
        if User.query.filter_by(nickname=nickname).first() == None:
            return nickname
        version = 2
        while True:
            new_nickname = nickname + str(version)
            if User.query.filter_by(nickname=new_nickname).first() == None:
                break
            version += 1
        return new_nickname

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)
            return self

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)
            return self

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        return Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id).order_by(
                    Post.timestamp.desc())
Example #18
0
class User(UserMixin, db.Model):
    """
    Represetns a system User
    """
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    posts = db.relationship('Post', backref='author', lazy='dynamic')

    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')

    def followed_posts(self):
        """
        followed posts
        """
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def follow(self, user):
        """
        follow
        """
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        """
        unfollow
        """
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        """
        is following
        """
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def __repr__(self):
        """
        repr
        """
        return '<User {}, {}>'.format(self.username, self.email)

    def set_password(self, password):
        """
        Set password to generated password hash
        """
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        """
        Check if password hash matches set password
        """
        current_app.logger.debug("Checking password {}".format(password))
        return check_password_hash(self.password_hash, password)

    @staticmethod
    @login.user_loader
    def load_user(id_):
        """
        Return user base on id.
        """
        return User.query.get(int(id_))

    def avatar(self, size="80"):
        """
        Return Gravatar URL based on email
        """
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        url = 'https://www.gravatar.com/avatar/{}?d=retro&s={}'.format(
            digest, size)
        current_app.logger.debug("Get gravatar {}".format(url))
        return url
Example #19
0
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(62), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    image = db.Column(db.String(120))
    # relationship with posts
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    # for followers
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')

    def follow(self, user):
        """for follow user"""
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        """for unfollow user"""
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        """check user is following or not"""
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        """to get all the followed user posts and own posts"""
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    # for password reset
    def get_reset_password_token(self, expires_in=600):
        """for generating reset token"""
        return jwt.encode(
            {
                'reset_password': self.id,
                'exp': time() + expires_in
            },
            app.config['SECRET_KEY'],
            algorithm='HS256').decode('utf-8')

    @staticmethod
    def verify_reset_password_token(token):
        """for verifying reset token"""
        try:
            id = jwt.decode(token,
                            app.config['SECRET_KEY'],
                            algorithms=['HS256'])['reset_password']
        except:
            return
        return User.query.get(id)

    def avatar(self, size):
        """getting default avtar for every new user"""
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(
            digest, size)

    def set_password(self, password):
        """hashing password"""
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        """checking password"""
        return check_password_hash(self.password_hash, password)

    def __repr__(self):
        return "{0.id} - {0.email}".format(self)
Example #20
0
class Node(SearchableMixin,db.Model):
    __tablename__ = 'node'
    __searchable__ = {
        'name': {"type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart"}
    }
    id = db.Column(db.Integer, primary_key=True)
    # 对外id
    uuid = db.Column(db.String(64), default=shortuuid.uuid, unique=True)
    # 名称
    name = db.Column(db.String(32))
    # 创建时间
    create_at = db.Column(db.DateTime(), default=datetime.utcnow())
    # 类型 0正常型 1热搜型[正常型:按照发布时间排序。热搜型:首页显示条目按照posiniton显示,历史数据按照update时间排序]
    type = db.Column(db.Integer, default=0)
    # 首页显示条目数
    num = db.Column(db.Integer, default=15)
    # 来源
    source_id = db.Column(db.Integer, db.ForeignKey('source.id'))
    # 爬虫内容
    tops = db.relationship('Top', backref=db.backref('node'))
    # 归属topic
    topic_id = db.Column(db.Integer, db.ForeignKey('topic.id'))

    # 联合唯一
    __table_args__ = (
        db.UniqueConstraint('name', 'source_id'),
    )

    def admin_to_json(self):
        return {
            "topic_id": self.topic.id,
            "source_id": self.source.id,
            "name": self.name,
            "type": self.type,
            "num": self.num,
        }

    def tiny_to_json(self):
        return {
            "topic_name": self.topic.name,
            "id": self.uuid,
            "source_name": self.source.name,
            "source_img": self.source.img_url,
            "name": self.name,

        }

    def to_json(self, need_history=False):
        json_node = {
            "topic_name":self.topic.name,
            "id": self.uuid,
            "source_name": self.source.name,
            "source_img": self.source.img_url,
            "name": self.name,
            "tops": get_tops(self.id, self.num, self.type, need_history)

        }
        return json_node

    def __repr__(self):
        return '<Node id={0},name={1}>'.format(self.uuid, self.name)
Example #21
0
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)

    followed = db.relationship("User",
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref("followers", lazy="dynamic"),
                               lazy="dynamic")

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def avatar(self, size):
        digest = md5(self.email.lower().encode("utf-8")).hexdigest()
        return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(
            digest, size)

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {
                "reset_password": self.id,
                "exp": time() + expires_in
            },
            app.config["SECRET_KEY"],
            algorithm="HS256").decode("utf-8")

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(token,
                            app.config["SECRET_KEY"],
                            algorithms=["HS256"])["reset_password"]
        except:
            return
        return User.query.get(id)
Example #22
0
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime)
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')

    @staticmethod
    def make_valid_nickname(nickname):
        return re.sub('[^a-zA-Z0-9_\.]', '', nickname)

    @staticmethod
    def make_unique_nickname(nickname):
        if User.query.filter_by(nickname=nickname).first() is None:
            return nickname
        version = 2
        while True:
            new_nickname = nickname + str(version)
            if User.query.filter_by(nickname=new_nickname).first() is None:
                break
            version += 1
        return new_nickname

    @property
    def is_authenticated(self):
        return True

    @property
    def is_active(self):
        return True

    @property
    def is_anonymous(self):
        return False

    def get_id(self):
        try:
            return unicode(self.id)  # python 2
        except NameError:
            return str(self.id)  # python 3

    def avatar(self, size):
        return 'http://www.gravatar.com/avatar/%s?d=mm&s=%d' % \
            (md5(self.email.encode('utf-8')).hexdigest(), size)

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)
            return self

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)
            return self

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        return Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id).order_by(
                    Post.timestamp.desc())

    def __repr__(self):  # pragma: no cover
        return '<User %r>' % (self.nickname)
Example #23
0
class ItemModel(db.Model):
    """
    Item model
    """

    __tablename__ = 'item'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    description = db.Column(db.TEXT)
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
    category = db.relationship(CategoryModel,
                               backref=db.backref('items', lazy=True))
    created_date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    slug = db.Column(db.String(80))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship(UserModel, backref=db.backref('items', lazy=True))

    __table_args__ = (db.UniqueConstraint('slug', name='slug_unique_1'), )

    @staticmethod
    def find(item_id):
        """
        Find an item by its id
        :param item_id: item id
        :return: an Item or None
        """

        return db.session.query(ItemModel).filter_by(id=item_id).one_or_none()

    @staticmethod
    def get_user_item(item_id, user_id):
        """
        Find an item by its it and its user id
        :param item_id: item id
        :param user_id: user id
        :return: an Item or None
        """

        return db.session.query(ItemModel).filter_by(
            id=item_id, user_id=user_id).one_or_none()

    @staticmethod
    def validate_slug(slug):
        """
        Validate an item by its slug
        :param slug: item slug
        :return: True if valid and False if invalid
        """

        item = db.session.query(ItemModel) \
            .filter_by(slug=slug).one_or_none()

        if item is not None:
            return False
        else:
            return True

    @staticmethod
    def get_last_n_items(n=10):
        return db.session.query(ItemModel).order_by(
            ItemModel.created_date.desc()).limit(n).all()

    @staticmethod
    def get_all_items():
        return db.session.query(ItemModel).all()
Example #24
0
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    pwdhash = db.Column(db.String(54))
    activation_status = db.Column(db.Boolean, index=True)
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    flwrs = db.Column(db.Integer, index=True)
    tokens = db.Column(db.Text)
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')

    def __init__(self, nickname, email, password):
        self.nickname = nickname
        self.email = email.lower()
        self.set_password(password)
        self.activation_status = False
        self.flwrs = -1

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)
            user.flwrs = user.flwrs + 1
            db.session.commit()
            return self

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)
            user.flwrs = user.flwrs - 1
            db.session.commit()
            return self

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def has_liked(self, post_id):
        post = Post.query.filter_by(id=post_id).first()
        res = Like.query.filter_by(post_id=post_id).all()
        for r in res:
            if r.user_id == self.id:
                return True
            else:
                return False

    def set_password(self, password):
        self.pwdhash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.pwdhash, password)

    def make_dirs(self):
        os.makedirs('/home/manan/Programs/EduTech/app/static/userdata/' +
                    str(self.nickname),
                    exist_ok=True)

    def avatar(self):
        for root, dirs, files in os.walk(
                "/home/manan/Programs/EduTech/app/static/userdata/" +
                str(self.nickname)):
            for file in files:
                if file.endswith(".jpg") or file.endswith(".png"):
                    return "/static/userdata/" + str(
                        self.nickname) + '/' + str(file)
        return "/static/userdata/avatar.png"

    def __repr__(self):
        return '<User %s, Email %s, Activation %s>' % (
            self.nickname, self.email, self.activation_status)
Example #25
0
class Image(db.Model):
    __tablename__ = 'image'
    id = db.Column(db.Integer, primary_key=True)
    filename = db.Column(db.String(200), nullable=False)
    uploader_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    uploader = db.relationship('User', backref=db.backref('images'))
Example #26
0
class User(PaginatedAPIMixin, UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    # backref is how the relationship will be accessed from the RS entity
    # lazy="dynamic" -> NO run until explicitly requested
    posts = db.relationship("Post", backref="author", lazy="dynamic")
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    # secondary configures the association table for this relationship
    # primaryjoin links the LS entity(follower) with the association table
    # followers.c(olumn).follower_id references the follower_id column
    # secondaryjoin links the RS entity(followed) with the association table
    # relationship from LS entity - followed, from RS entity - followers
    followed = db.relationship(
        "User",
        secondary=followers,
        primaryjoin=(followers.c.follower_id == id),
        secondaryjoin=(followers.c.followed_id == id),
        backref=db.backref("followers", lazy="dynamic"),
        lazy="dynamic",
    )
    notifications = db.relationship(
        "Notification", backref="user", lazy="dynamic"
    )
    messages_sent = db.relationship(
        "Message",
        foreign_keys="Message.sender_id",
        backref="author",
        lazy="dynamic",
    )

    messages_received = db.relationship(
        "Message",
        foreign_keys="Message.recipient_id",
        backref="recipient",
        lazy="dynamic",
    )
    last_message_read_time = db.Column(db.DateTime)
    tasks = db.relationship("Task", backref="user", lazy="dynamic")
    token = db.Column(db.String(32), index=True, unique=True)
    token_expiration = db.Column(db.DateTime)

    def __repr__(self):
        return f"<User {self.username}>"

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def avatar(self, size):
        # MD5 support works on bytes and not strings, encode the string
        digest = md5(self.email.lower().encode("utf-8")).hexdigest()
        return f"https://www.gravatar.com/avatar/{digest}?d=identicon&s={size}"

    def is_following(self, user):
        return (
            self.followed.filter(followers.c.followed_id == user.id).count()
            > 0
        )

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def followed_posts(self):
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)
        ).filter(followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {"reset_password": self.id, "exp": time() + expires_in},
            current_app.config["SECRET_KEY"],
            algorithm="HS256",
        ).decode("utf-8")

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(
                token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
            )["reset_password"]
        except Exception:
            return
        return User.query.get(id)

    def new_messages(self):
        last_read_time = self.last_message_read_time or datetime(1900, 1, 1)
        return (
            Message.query.filter_by(recipient=self)
            .filter(Message.timestamp > last_read_time)
            .count()
        )

    def add_notification(self, name, data):
        self.notifications.filter_by(name=name).delete()
        n = Notification(name=name, payload_json=json.dumps(data), user=self)
        db.session.add(n)
        return n

    def launch_task(self, name, description, *args, **kwargs):
        rq_job = current_app.task_queue.enqueue(
            "app.tasks." + name, self.id, *args, **kwargs
        )
        task = Task(
            id=rq_job.get_id(), name=name, description=description, user=self
        )
        db.session.add(task)
        return task

    def get_tasks_in_progress(self):
        return Task.query.filter_by(user=self, complete=False).all()

    def get_task_in_progress(self, name):
        return Task.query.filter_by(
            name=name, user=self, complete=False
        ).first()

    def to_dict(self, include_email=False):
        data = {
            "id": self.id,
            "username": self.username,
            "last_seen": (self.last_seen.isoformat() + "Z")
            if self.last_seen
            else "no_log",
            "about_me": self.about_me,
            "post_count": self.posts.count(),
            "follower_count": self.followers.count(),
            "followed_count": self.followed.count(),
            "_links": {
                "self": url_for("api.get_user", id=self.id),
                "followers": url_for("api.get_followers", id=self.id),
                "followed": url_for("api.get_followed", id=self.id),
                "avatar": self.avatar(128),
            },
        }
        if include_email:
            data["email"] = self.email
        return data

    def from_dict(self, data, new_user=False):
        for field in ["username", "email", "about_me"]:
            if field in data:
                setattr(self, field, data[field])
        if new_user and "password" in data:
            self.set_password(data["password"])

    def get_token(self, expires_in=3600):
        now = datetime.utcnow()
        if self.token and self.token_expiration > now + timedelta(seconds=60):
            return self.token
        self.token = base64.b64encode(os.urandom(24)).decode("utf-8")
        self.token_expiration = now + timedelta(seconds=expires_in)
        db.session.add(self)
        return self.token

    def revoke_token(self):
        self.token_expiration = datetime.utcnow() - timedelta(seconds=1)

    @staticmethod
    def check_token(token):
        user = User.query.filter_by(token=token).first()
        if user is None or user.token_expiration < datetime.utcnow():
            return None
        return user
Example #27
0
class User(
        UserMixin, db.Model
):  #User class inherits from db.Model, a base class for all models from Flask-SQLAlchemy
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True,
                         unique=True)  #indexing allows efficient search
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship(
        'Post', backref='author', lazy='dynamic'
    )  #not a db field, but a high-level representatn btw users and posts.
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(
        self
    ):  #tells Python how to print objects of this class, and useful for debugging
        return '<User {}>'.format(self.username)

    def avatar(self, size):
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        return 'https://www.gravatar.com/avatar/{}?d=identicons&s={}'.format(
            digest, size)

    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id),
                               secondaryjoin=(followers.c.followed_id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')
    #many-to-many relationship:
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')

    #password hashing mechanism: user can now do secure password verification, without a need to store original passwords
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    #to add(follow) and remove relationships(unfollow)
    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {
                'reset_password': self.id,
                'exp': time() + expires_in
            },
            app.config['SECRET_KEY'],
            algorithm='HS256').decode('utf-8')

    @staticmethod  #does not need an object, and so can be invoked directly from d class
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(token,
                            app.config['SECRET_KEY'],
                            algorithms=['HS256'])['reset_password']  #load user
        except:
            return
        return User.query.get(id)
Example #28
0
class User(UserMixin, db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(128), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship('Purchase', backref='author', lazy='dynamic')
    purchases = db.relationship(
        'Purchase',
        secondary=purchases_table,
        backref='user_purchase',
        lazy='dynamic'
    )
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    remindings = db.Column(db.String(140))
    followed = db.relationship(
        'User', secondary=followers,
        primaryjoin=(followers.c.follower_id == id),
        secondaryjoin=(followers.c.followed_id == id),
        backref=db.backref('followers', lazy='dynamic'),
        lazy='dynamic'
    )

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
        
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def avatar(self, size):
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(
            digest, size
        )

    def add_purchase(self, purchase):
        if not self.bought(purchase):
            self.purchases.append(purchase)

    def rm_purchase(self, purchase):
        if self.bought(purchase):
            self.purchases.remove(purchase)

    def bought(self, purchase):
        return self.purchases.filter(
            purchases_table.c.purchase_id == purchase.id
        ).count() > 0

    def bought_purchases(self):
        purchased = Purchase.query.join(
            purchases_table,
            (purchases_table.c.purchase_id == Purchase.id)).filter(
            purchases_table.c.purchaser_id == self.id
        )
        return purchased.order_by(Purchase.timestamp.desc())

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id
        ).count() > 0

    def followed_purchases(self):
        followed = Purchase.query.join(
            followers, (followers.c.followed_id == Purchase.user_id)
        ).filter(
            followers.c.follower_id == self.id
        )
        own = Purchase.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Purchase.timestamp.desc())

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {
                'reset_password': self.id,
                'exp': time() + expires_in
            },
            current_app.config['SECRET_KEY'],
            algorithm='HS256'
        ).decode('utf-8')

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(
                token,
                current_app.config['SECRET_KEY'],
                algorithms='HS256'
            )['reset_password']
        except:
            return
        return User.query.get(id)

    @classmethod
    def get_user_list(cls):
        return User.query.order_by(User.username)

    def __repr__(self):
        return "<User {}>".format(self.username)
Example #29
0
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)
    followed = db.relationship('User',
                               secondary=followers,
                               primaryjoin=(followers.c.follower_id == id),
                               secondaryjoin=(followers.c.followed_id == id),
                               backref=db.backref('followers', lazy='dynamic'),
                               lazy='dynamic')

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def get_reset_password_token(self, expires_in=600):
        payload = {
            'reset_password': self.id,
            'exp': time() + expires_in,
        }

        return jwt.encode(payload,
                          current_app.config['SECRET_KEY'],
                          algorithm='HS256').decode('utf-8')

    @staticmethod
    def verify_reset_password_token(token):
        try:
            user_id = jwt.decode(token,
                                 current_app.config['SECRET_KEY'],
                                 algorithms=['HS256'])['reset_password']
        except (jwt.exceptions.DecodeError,
                jwt.exceptions.ExpiredSignatureError):
            return

        return User.query.get(user_id)

    def avatar(self, size):
        digest = md5(self.email.lower().encode('utf-8')).hexdigest()
        return f'https://www.gravatar.com/avatar/{digest}?d=identicon&s={size}'

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        followed = Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id)
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    def __repr__(self):
        return f'<User id={self.id} username={self.username} email={self.email} ' \
               f'password_hash={self.password_hash}>'
Example #30
0
class User(UserMixin, db.Model):
    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), index=True, unique=True)
    username = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    streak = db.Column(db.Integer, default=0)

    daily_meditations = db.relationship("DailyMeditation", backref="user", lazy="dynamic")

    daily_meditations = db.relationship(
        "DailyMeditation",
        secondary=user_daily_meditation,
        back_populates="users",
        lazy="dynamic"
    )

    followed = db.relationship(
        'User', secondary=followers,
        primaryjoin=(followers.c.follower_id == id),
        secondaryjoin=(followers.c.followed_id == id),
        backref=db.backref('followers', lazy='dynamic'), lazy='dynamic'
    )

    def __repr__(self):
        return "<User id: {} email: {}>".format(self.id, self.email)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def is_streak(self):
        yesterday = datetime.date.today()-datetime.timedelta(days=1)
        yesterdays_meditation = DailyMeditation.query.filter_by(date=yesterday)

        if yesterdays_meditation in self.daily_meditations:
            return True

        return False

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def get_highscore(self):
        friends = [friend for friend in self.followed]
        friends.append(self)
        friends.sort(key=lambda current_friend: current_friend.streak, reverse=True)

        return friends
 def sample_annotations(self):
     return db.relationship('ProjectSampleAnnotations', backref=db.backref('project'), lazy='dynamic',
                            cascade='save-update, merge, delete, expunge')
Example #32
0
class User(UserMixin, db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(256), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    confirmed = db.Column(db.Boolean, default="0")
    sudo = db.Column(db.Boolean, default=False, server_default="0")
    name = db.Column(db.String(256))
    password_hash = db.Column(db.String(128))
    member_since = db.Column(db.DateTime(), default=datetime.utcnow)
    # The "ping" function updates this
    last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
    active = db.Column(db.Boolean, default=True,
                       server_default="1")  # used for invitation

    roles = db.relationship("RoleToUser", backref=db.backref("user"))
    enable_notification_emails = db.Column(db.Boolean,
                                           nullable=False,
                                           default=True,
                                           server_default="1")
    enable_timeclock_notification_sms = db.Column(db.Boolean,
                                                  nullable=False,
                                                  default=True,
                                                  server_default="1")
    phone_country_code = db.Column(db.String(3), index=True)
    phone_national_number = db.Column(db.String(15), index=True)

    SMS_VERIFICATION_PIN_LENGTH = 6

    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        self.session_id = None

    @property
    def password(self):
        raise AttributeError("password is not a readable attribute")

    def __eq__(self, other):
        """Checks equality of user objects. Necessary to override UserMixin one."""
        return self.id == other.id

    @property
    def first_name(self):
        """Guess first name for friendly notifications"""
        name_list = self.name.partition(" ")
        return name_list[0]

    @password.setter
    def password(self, password):
        """Store plaintext password securely on the model, and send alerts + flush sessions"""

        # See whether there was a password before
        existing_password = self.password_hash is None

        # Actually update the password
        self.password_hash = generate_password_hash(password)
        db.session.commit()

        if self.id is not None:  # This is used before user is committed
            current_app.logger.info(
                "Password for user id %s (%s) has been updated" %
                (self.id, self.email))

        # If password is changed, lot user out of other
        if not existing_password:
            # Email users for security purposes
            self.send_email("[Alert] Your Password Has Changed",
                            render_template(
                                "email/password-changed.html",
                                user=self,
                            ),
                            force_send=True)

            # Logout all sessions
            self.logout_all_sessions()

    def ping(self, org_id=None):
        """Update the last-seen time, and send tracking info to intercom"""
        if not PingLimiter.allowed_to_send(self):
            return

        if not self.active or not self.confirmed:
            current_app.logger.debug(
                "Did not send tracking for inactive user %s" % self.id)
            return

        PingLimiter.mark_sent(self)
        try:
            self.last_seen = datetime.utcnow()
            db.session.add(self)
            db.session.commit()
        except:
            db.session.rollback()

        # Now update intercom
        if current_app.config.get("ENV") == "dev":
            return

        @copy_current_request_context
        def async_callback(resp):
            if resp.code != 200:
                current_app.logger.info("Failed intercom update - header %s" %
                                        (resp.code))

        headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

        # Stuff we want to tack on, so decode then reencode
        data = {
            "user_id": str(self.id),
            "name": self.name,
            "custom_attributes": {
                "sudo": self.sudo,
                "username": self.username,
                "phone_number": self.pretty_phone_number,
            },
            "email": self.email,
            "created_at": int(self.member_since.strftime("%s")),
            "last_request_at": int(time.time()),
            "new_session": True,
            "companies": [],
        }

        if org_id is not None and not self.is_sudo():
            # Don't add users to any org
            org = organization_model.Organization.query.get(org_id)
            if org is not None:
                data["companies"].append(org.intercom_settings())

        unirest.post(
            "https://api.intercom.io/users",
            params=json.dumps(data),
            headers=headers,
            auth=(
                current_app.config.get("INTERCOM_ID"),
                current_app.config.get("INTERCOM_API_KEY"),
            ),
            callback=async_callback,
        )
        return

    def track_event(self, event):
        """ Send events to intercom """

        if event is None:
            current_app.logger.warning("Null event")
            return

        if not self.active or not self.confirmed:
            current_app.logger.debug(
                "Did not track event %s for inactive user %s" %
                (event, self.id))

        if current_app.config.get("ENV") == "dev":
            current_app.logger.debug("Intercepted event for user %s - %s" %
                                     (self.id, event))
            return

        @copy_current_request_context
        def async_callback(resp):
            if resp.code != 202:
                current_app.logger.info("bad intercom event - header %s" %
                                        (resp.code))

        headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

        # Stuff we want to tack on, so decode then reencode
        data = {
            "user_id": str(self.id),
            "email": self.email,
            "created_at": int(time.time()),
            "event_name": event,
        }

        unirest.post(
            "https://api.intercom.io/events",
            params=json.dumps(data),
            headers=headers,
            auth=(
                current_app.config.get("INTERCOM_ID"),
                current_app.config.get("INTERCOM_API_KEY"),
            ),
            callback=async_callback,
        )
        return

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

    def generate_confirmation_token(self,
                                    expiration=SECONDS_PER_DAY,
                                    trial=False):
        s = Serializer(current_app.config["SECRET_KEY"], expiration)
        return s.dumps({"confirm": self.id, "trial": trial})

    def generate_reset_token(self, expiration=SECONDS_PER_HOUR):
        s = Serializer(current_app.config["SECRET_KEY"], expiration)
        return s.dumps({"reset": self.id})

    def generate_activation_token(self, expiration=SECONDS_PER_DAY):
        """ Used for account invitations """
        s = Serializer(current_app.config["SECRET_KEY"], expiration)
        return s.dumps({"activation": self.id})

    def reset_password(self, token, new_password):
        s = Serializer(current_app.config["SECRET_KEY"])
        try:
            data = s.loads(token)
        except:
            return False
        if data.get("reset") != self.id:
            return False
        self.password = new_password
        self.logout_all_sessions()
        db.session.add(self)
        self.track_event("reset_password")
        return True

    @staticmethod
    def get_id_from_activate_token(token):
        s = Serializer(current_app.config["SECRET_KEY"])
        try:
            data = s.loads(token)
        except:
            return False
        user_id = data.get("activation")
        if user_id is None:
            return False
        return user_id

    def activate_account(self, token, name, password, username):
        s = Serializer(current_app.config["SECRET_KEY"])
        try:
            data = s.loads(token)
        except:
            return False
        if data.get("activation") != self.id:
            return False
        self.password = password
        self.name = name
        self.username = username
        self.confirmed = True
        self.active = True
        db.session.add(self)
        current_app.logger.info("User account activated: user id %s (%s)" %
                                (self.id, self.email))
        self.track_event("activated_account")
        return True

    def __repr__(self):
        return "<User %r - %s - id %r>" % (self.email, self.name, self.id)

    def confirm(self, token):
        s = Serializer(current_app.config["SECRET_KEY"])
        try:
            data = s.loads(token)
        except:
            return False
        if data.get("confirm") != self.id:
            return False

        self.confirmed = True
        self.active = True
        db.session.add(self)
        db.session.commit()
        current_app.logger.info("User account confirmed: user id %s (%s)" %
                                (self.id, self.email))
        self.track_event("confirmed_account")
        if data.get("trial") is True:
            self.track_event("started_free_trial")
        return True

    def generate_email_change_token(self,
                                    new_email,
                                    expiration=SECONDS_PER_HOUR):
        s = Serializer(current_app.config["SECRET_KEY"], expiration)
        return s.dumps({"change_email": self.id, "new_email": new_email})

    def change_email(self, token):
        s = Serializer(current_app.config["SECRET_KEY"])
        try:
            data = s.loads(token)
        except:
            return False
        if data.get("change_email") != self.id:
            return False
        new_email = data.get("new_email")
        if new_email is None:
            return False
        if self.query.filter_by(email=new_email).first() is not None:
            return False
        self.email = new_email
        try:
            db.session.add(self)
            db.session.commit()
        except:
            db.session.rollback()
            raise Exception("Dirty session")

        self.track_event("changed_email")
        return True

    def is_sudo(self):
        """checks if sudo"""
        return self.sudo is True

    def is_org_admin_or_location_manager(self, org_id, location_id):
        """checks if an admin or manager in the org/location"""
        return self.is_org_admin(org_id) or self.is_location_manager(
            location_id)

    def is_org_admin(self, org_id):
        """checks if an org admin"""

        admin = User.query \
            .join(User.admin_of) \
            .filter(
                organization_model.Organization.id == org_id,
                User.id == self.id
            ) \
            .first()

        return admin is not None

    def is_manager_in_org(self, org_id):
        """checks if current user manages a location in the org"""

        manager = User.query \
            .join(User.manager_of) \
            .filter(
                User.id == self.id,
                Location.organization_id == org_id
            ) \
            .first()

        return manager is not None

    def is_location_manager(self, location_id):
        """checks if current user managers location in kwargs"""

        manager = User.query \
            .join(User.manager_of) \
            .filter(
                User.id == self.id,
                Location.id == location_id
            ) \
            .first()

        return manager is not None

    def is_location_worker(self, location_id):
        """checks if the user is a worker of a role in the location"""

        assoc = User.query \
            .join(User.roles) \
            .join(Role) \
            .filter(
                RoleToUser.user_id == self.id,
                RoleToUser.archived == False,
                RoleToUser.role_id == Role.id,
                Role.location_id == location_id
            ) \
            .first()

        return assoc is not None

    def is_active(self):
        return self.active

    def manager_accounts(self):
        """
        returns a list of all organizations that the user has Manager access to
        """

        # start with org admins
        result = self.admin_of.all()

        # add in additional location admins, but avoid duplicates
        for location in self.manager_of.all():
            org = location.organization
            if org not in result:
                result.append(org)

        return result

    def memberships(self):
        """ Return org ids that this user is an active member of """

        memberships = db.session.execute(
            select([organization_model.Organization.id, organization_model.Organization.name, Location.id, Location.name, RoleToUser.role_id, Role.name]).\
            where(RoleToUser.user_id == self.id).\
            where(RoleToUser.archived == False).\
            where(RoleToUser.role_id == Role.id).\
            where(Role.location_id == Location.id).\
            where(Location.organization_id == organization_model.Organization.id).\
            select_from(RoleToUser).\
            select_from(Role).\
            select_from(Location).\
            select_from(organization_model.Organization)
        ).fetchall()

        result = []
        for entry in memberships:

            # order is defined in select statement
            result.append({
                "organization_id": entry[0],
                "organization_name": entry[1],
                "location_id": entry[2],
                "location_name": entry[3],
                "role_id": entry[4],
                "role_name": entry[5],
            })

        return result

    def membership_ids(self):
        memberships = db.session.execute(
            select([organization_model.Organization.id, Location.id, RoleToUser.role_id]).\
            where(RoleToUser.user_id == self.id).\
            where(RoleToUser.archived == False).\
            where(RoleToUser.role_id == Role.id).\
            where(Role.location_id == Location.id).\
            where(Location.organization_id == organization_model.Organization.id).\
            select_from(RoleToUser).\
            select_from(Role).\
            select_from(Location).\
            select_from(organization_model.Organization)
        ).fetchall()

        result = []
        for entry in memberships:

            # order is defined in select statement
            result.append({
                "organization_id": entry[0],
                "location_id": entry[1],
                "role_id": entry[2],
                "user_id": self.id,
            })

        return result

    def intercom_settings(self, org_id=None):
        """ Data for Intercom, json-encoded """

        data = {
            "user_id":
            str(self.id),
            "name":
            self.name,
            "sudo":
            self.sudo,
            "username":
            self.username,
            "email":
            self.email,
            "created_at":
            int(self.member_since.strftime("%s")),
            "app_id":
            current_app.config.get("INTERCOM_ID"),
            "user_hash":
            hmac.new(
                current_app.config.get("INTERCOM_SECRET"),
                str(self.id),
                hashlib.sha256,
            ).hexdigest(),
            "is_org_admin": (len(self.admin_of.all()) > 0),
            "is_location_manager": (len(self.manager_of.all()) > 0),
            "is_org_member": (len(self.memberships()) > 0),
        }

        if org_id is not None and not self.is_sudo():
            # Don't add users to any org
            org = organization_model.Organization.query.get(org_id)
            if org is not None:
                data["companies"] = [
                    org.intercom_settings(),
                ]

        return json.dumps(data)

    def flush_associated_shift_caches(self):
        schedules2 = schedule2_model.Schedule2.query.join(Role).join(
            RoleToUser).filter(RoleToUser.user_id == self.id,
                               RoleToUser.archived == False).all()
        for schedule in schedules2:
            Shifts2Cache.delete(schedule.id)

    @staticmethod
    def create_and_invite(email, name="", inviter_name=None, silent=False):
        """ Create a new user and return that account """

        if "@" not in email:
            raise Exception("Invalid email %s passed to create function." %
                            email)

        user = User(
            email=email.lower().strip(),
            name=name,
            active=False,
            confirmed=False,
        )

        try:
            db.session.add(user)
            db.session.commit()
        except:
            db.session.rollback()
            raise Exception("Dirty session")

        if not silent:
            User.send_activation_email(user, inviter_name)
        return user

    @staticmethod
    def send_activation_reminder(user, inviter_name=None):
        if user.active:
            raise Exception("User %s is alredy active" % user.id)

        if UserActivationReminderLimiter.allowed_to_send(user):
            User.send_activation_email(user, inviter_name)

    @staticmethod
    def send_activation_email(user, inviter_name=None):
        if inviter_name is None or len(inviter_name) == 0:
            subject = "[Action Required] Activate your Staffjoy Account"
        else:
            subject = "[Action Required] Set up your %s shift scheduling account" % (
                inviter_name)

        token = user.generate_activation_token()

        user.send_email(
            subject,
            render_template("email/activate-account.html",
                            user=user,
                            token=token), True)

        UserActivationReminderLimiter.mark_sent(user)

    def generate_api_token(self, expiration=None):
        """ Create a time-based token for single page apps"""
        if not self.is_authenticated:
            raise Exception("User not authenticated")

        s = Serializer(current_app.config["SECRET_KEY"],
                       current_app.config.get("SESSION_EXPIRATION"))

        session_id = self.session_id

        return s.dumps({
            "id": self.id,
            "session_id": session_id
        }).decode("ascii")

    @staticmethod
    def verify_api_token(token):
        """Validate a time-based token (from single page apps)"""
        s = Serializer(current_app.config["SECRET_KEY"])
        try:
            data = s.loads(token)
        except:
            return None

        if not data.get("id") or not data.get("session_id"):
            return None

        if SessionCache.validate_session(data.get("id"),
                                         data.get("session_id")):
            user = User.query.get(data.get("id"))
            if user is not None:
                user.set_session_id(data.get("session_id"))
                return user

        return None

    def send_email(self, subject, html_body, force_send=False):
        """ Send the user an email if they are active
        Allow overriding for things like account activation
        """
        if self is None:
            current_app.logger.error(
                "Could not send email becuase could not find user - subject '%s'"
                % (subject))
            return

        if not (self.active and self.confirmed) and not force_send:
            current_app.logger.info(
                "Did not send email to id %s (%s) because account inactive - subject '%s'"
                % (self.id, self.email, subject))
            return

        if not self.enable_notification_emails and not force_send:
            current_app.logger.info(
                "Did not send email to id %s (%s) because account disabled notification emails - subject '%s'"
                % (self.id, self.email, subject))
            return

        send_email(self.email, subject, html_body)

    @staticmethod
    @login_manager.token_loader
    def load_session_token(token):
        """Load cookie session"""
        s = Serializer(current_app.config["SECRET_KEY"],
                       current_app.config.get("SESSION_EXPIRATION"))
        try:
            data = s.loads(token)
        except:
            return None

        if SessionCache.validate_session(data.get("user_id", -1),
                                         data.get("session_id", "-1")):
            user = User.query.get(data["user_id"])
            user.set_session_id(data["session_id"])
            current_app.logger.debug("Loading user %s from cookie session %s" %
                                     (user.id, user.session_id))
            return user
        return None

    def get_auth_token(self):
        """Cookie info. Must be secure."""
        s = Serializer(current_app.config["SECRET_KEY"],
                       current_app.config["COOKIE_EXPIRATION"])
        current_app.logger.debug("Generating auth token for user %s" % self.id)

        if not self.is_authenticated:
            raise Exception("User not authenticated")

        return s.dumps({
            "user_id":
            self.id,
            "session_id":
            SessionCache.create_session(
                self.id, expiration=current_app.config["COOKIE_EXPIRATION"])
        })

    def get_id(self, expiration=None):
        """Returns the id and session id used to identify a logged-in user"""
        # Used exclusively by flask-login

        if not expiration:
            expiration = current_app.config.get("SESSION_EXPIRATION")

        if not self.is_authenticated:
            current_app.logger.info(
                "Cannot generate token because user not authenticated")
            return None

        try:
            session_id = self.session_id
            if not session_id:
                session_id = SessionCache.create_session(
                    self.id,
                    expiration=current_app.config.get("SESSION_EXPIRATION"))
        except:
            session_id = SessionCache.create_session(
                self.id,
                expiration=current_app.config.get("SESSION_EXPIRATION"))

        # flask-login handles encryption of this
        token = "%(user_id)s-%(session_id)s" % {
            "user_id": self.id,
            "session_id": session_id
        }
        return token

    @staticmethod
    @login_manager.user_loader
    def load_session_id(token):
        """Load user session as opposite of get_id function"""
        # used exclusively by flask-login
        try:
            user_id, session_id = token.split("-")
        except:
            return None

        if not user_id or not session_id:
            return None

        if SessionCache.validate_session(user_id, session_id):
            user = User.query.get(user_id)
            user.set_session_id(session_id)
            return user
        return None

    def logout_session(self):
        """Delete user's current session and cookie"""
        # Delete the session from Redis
        try:
            if self.session_id:
                SessionCache.delete_session(self.id, self.session_id)
        except:
            current_app.logger.warning("User without session_id")

        # If there is a cookie, try deleting the session in redis
        cookie_data = request.cookies.get(
            current_app.config.get("REMEMBER_COOKIE_NAME"))
        if cookie_data:
            s = Serializer(current_app.config["SECRET_KEY"])
            try:
                data = s.loads(cookie_data)
                user_id = data["user_id"]
                session_id = data["session_id"]
            except:
                current_app.logger.info("Corrupt cookie for user %s" % self.id)
                return

            if user_id and session_id:
                SessionCache.delete_session(user_id, session_id)
                current_app.logger.debug("Deleted cookie session %s" %
                                         session_id)

    def logout_target_session(self, session_id):
        """Delete user's current session and cookie"""
        SessionCache.delete_session(self.id, session_id)

    def get_target_session(self, session_id):
        """Returns info from a target session key"""
        return SessionCache.get_session_info(self.id, session_id)

    def get_all_sessions(self):

        # {session_id => {"remote_ip": "str", "last_used": utctimestamp}}

        # Get keys
        keys = SessionCache.get_all_sessions(self.id)

        output = {}
        for key in keys:
            # Key not guaranteed to be unique across users
            output[key] = SessionCache.get_session_info(self.id, key)

        return output

    def logout_all_sessions(self):
        """Log out all user sessions"""
        current_app.logger.info("Logged user %s out of all sessions" % self.id)
        SessionCache.delete_all_sessions(self.id)

    def set_session_id(self, session_id):
        self.session_id = session_id

    @property
    def phone_number(self):
        """Return phone number for API and such"""

        if not (self.phone_country_code and self.phone_national_number):
            return None

        p = phonenumbers.parse("+" + self.phone_country_code +
                               self.phone_national_number)

        # Default to show it for computers - E164 format.
        return phonenumbers.format_number(p,
                                          phonenumbers.PhoneNumberFormat.E164)

    @property
    def pretty_phone_number(self):
        """Return phone number in a readable format"""
        if not self.phone_number:
            return None

        p = phonenumbers.parse(self.phone_number)
        return phonenumbers.format_number(
            p, phonenumbers.PhoneNumberFormat.INTERNATIONAL)

    @phone_number.setter
    def phone_number(self):
        raise Exception(
            "Use verify_phone_number instead of directly setting the phone number"
        )

    def send_sms(self, message):
        """Send an SMS to the user"""
        if not self.active:
            # Stop communications to inactive users
            return

        if not self.phone_country_code and self.phone_national_number:
            raise Exception("User lacks a verified phone number")

        current_app.logger.info("Sending sms to user %s : %s" %
                                (self, message))

        send_sms(self.phone_country_code, self.phone_national_number, message)

    def get_phone_data_pending_verification(self):
        return PhoneVerificationCache.get(self.id)

    def set_phone_number(self, phone_country_code, phone_national_number):
        """Send a verification pin for a new phone number"""
        if not self.active:
            raise Exception(
                "Cannot confirm a phone number for an inactive user")

        if self.phone_number:
            # This prevents race conditions
            raise Exception(
                "Remove existing phone number before setting a new one")

        # Filter to pure digits
        phone_country_code = ''.join(i for i in phone_country_code
                                     if i.isdigit())
        phone_national_number = ''.join(i for i in phone_national_number
                                        if i.isdigit())

        # First, verify that it's a valid phone number format
        p = phonenumbers.parse("+" + str(phone_country_code) +
                               str(phone_national_number))
        if not phonenumbers.is_possible_number(p):
            raise Exception("Invalid phone number")

        # check if we registered this number with somebody else.
        if User.get_user_from_phone_number(phone_country_code,
                                           phone_national_number):
            current_app.logger.info(
                "Unable to verify user %s (%s) phone number because it already belongs to another user"
                % (self.id, self.email))
            raise Exception("Phone number already belongs to a user")

        current_app.logger.info(
            "User %s (%s) is attempting to verify phone number %s" %
            (self.id, self.email,
             phonenumbers.format_number(p,
                                        phonenumbers.PhoneNumberFormat.E164)))

        # At this point, we think we can send a message to the number

        # Generate a verification pin
        # (comes as a string due to leading zeros)
        verification_pin = ''.join(
            random.choice(string.digits)
            for x in range(self.SMS_VERIFICATION_PIN_LENGTH))

        # Set cache (before sms)
        PhoneVerificationCache.set(
            self.id, {
                "verification_pin": verification_pin,
                "phone_country_code": phone_country_code,
                "phone_national_number": phone_national_number,
            })

        # Send verification pin
        message = "Hi %s! Your Staffjoy verification pin is %s" % (
            self.first_name, verification_pin)
        send_sms(phone_country_code, phone_national_number, message)

    def verify_phone_number(self, pin):
        """Return true if new phone set, otherwise returns false."""
        data = self.get_phone_data_pending_verification()
        if not data:
            # Probably an error - might be a race condition where
            # cache decided to drop the data after too long
            raise Exception("No data pending verification")

        if data["verification_pin"] != pin:
            current_app.logger.info(
                "User %s (%s) entered incorrect phone verification pin" %
                (self.id, self.email))
            return False

        # check if we registered this number with somebody else.
        if User.get_user_from_phone_number(data["phone_country_code"],
                                           data["phone_national_number"]):
            current_app.logger.info(
                "Unable to verify user %s (%s) phone number because it already belongs to another user"
                % (self.id, self.email))

        # Pin confirmed! Put it in the database
        self.phone_national_number = data["phone_national_number"]
        self.phone_country_code = data["phone_country_code"]
        db.session.commit()

        PhoneVerificationCache.delete(self.id)
        current_app.logger.info("User %s (%s) verified their phone number" %
                                (self.id, self.email))
        return True

    def remove_phone_number(self):
        """Null out phone number fields"""
        self.phone_national_number = None
        self.phone_country_code = None
        PhoneVerificationCache.delete(self.id)
        db.session.commit()

    @classmethod
    def get_user_from_phone_number(cls, phone_country_code,
                                   phone_national_number):
        return cls.query.filter_by(
            phone_country_code=phone_country_code,
            phone_national_number=phone_national_number,
        ).first()