site/backend/app/models.py

116 lines
4.8 KiB
Python
Raw Normal View History

2024-08-03 09:14:20 +00:00
import os
2024-08-01 13:35:28 +00:00
from datetime import datetime, timezone
from typing import Optional
import sqlalchemy as sa
import sqlalchemy.orm as so
2024-08-01 18:10:49 +00:00
from werkzeug.security import generate_password_hash, check_password_hash
2024-08-03 09:14:20 +00:00
import pydenticon, hashlib, base64
from app import db, login
2024-08-01 18:10:49 +00:00
from flask_login import UserMixin
2024-08-01 13:35:28 +00:00
2024-08-04 12:44:20 +00:00
followers = sa.Table(
'followers',
db.metadata,
sa.Column('follower_id', sa.Integer, sa.ForeignKey('user.id'), primary_key=True),
sa.Column('followed_id', sa.Integer, sa.ForeignKey('user.id'), primary_key=True)
)
2024-08-01 18:10:49 +00:00
class User(UserMixin, db.Model):
2024-08-01 13:35:28 +00:00
id: so.Mapped[int] = so.mapped_column(primary_key=True)
username: so.Mapped[str] = so.mapped_column(sa.String(64), index=True, unique=True)
email: so.Mapped[str] = so.mapped_column(sa.String(120), index=True, unique=True)
password_hash: so.Mapped[Optional[str]] = so.mapped_column(sa.String(256))
posts: so.WriteOnlyMapped['Post'] = so.relationship(back_populates='author')
2024-08-03 11:59:05 +00:00
about_me: so.Mapped[Optional[str]] = so.mapped_column(sa.String(140))
last_seen: so.Mapped[Optional[datetime]] = so.mapped_column(default=lambda: datetime.now(timezone.utc))
2024-08-04 12:44:20 +00:00
following: so.WriteOnlyMapped['User'] = so.relationship(
secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
back_populates='followers')
followers: so.WriteOnlyMapped['User'] = so.relationship(
secondary=followers,
primaryjoin=(followers.c.followed_id == id),
secondaryjoin=(followers.c.follower_id == id),
back_populates='following')
2024-08-01 13:35:28 +00:00
2024-08-01 18:10:49 +00:00
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)
2024-08-03 09:14:20 +00:00
def gen_avatar(self, write_png=True):
foreground = [ "rgb(45,79,255)",
"rgb(254,180,44)",
"rgb(226,121,234)",
"rgb(30,179,253)",
"rgb(232,77,65)",
"rgb(49,203,115)",
"rgb(141,69,170)" ]
background = "rgb(22,22,22)"
digest = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
basedir = os.path.abspath(os.path.dirname(__file__))
pngloc = os.path.join(basedir, 'usercontent', 'identicon', str(digest) + '.png')
icongen = pydenticon.Generator(5, 5, digest=hashlib.md5, foreground=foreground, background=background)
pngicon = icongen.generate(self.email, 120, 120, padding=(10, 10, 10, 10), inverted=False, output_format="png")
if write_png:
pngfile = open(pngloc, "wb")
pngfile.write(pngicon)
pngfile.close()
else:
return str(base64.b64encode(pngicon))[2:-1]
2024-08-03 11:59:05 +00:00
def avatar_path(self):
2024-08-03 09:14:20 +00:00
digest = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
basedir = os.path.abspath(os.path.dirname(__file__))
pngloc = os.path.join(basedir, 'usercontent', 'identicon', digest + '.png')
return pngloc
2024-08-04 12:44:20 +00:00
def follow(self, user):
if not self.is_following(user):
self.following.add(user)
def unfollow(self, user):
if self.is_following(user):
self.following.remove(user)
def is_following(self, user):
query = self.following.select().where(User.id == user.id)
return db.session.scalar(query) is not None
def followers_count(self):
query = sa.select(sa.func.count()).select_from(self.followers.select().subquery())
return db.session.scalar(query)
def following_count(self):
query = sa.select(sa.func.count()).select_from(self.following.select().subquery())
return db.session.scalar(query)
def following_posts(self):
Author = so.aliased(User)
Follower = so.aliased(User)
return (
sa.select(Post)
.join(Post.author.of_type(Author))
.join(Author.followers.of_type(Follower), isouter=True)
.where(sa.or_(
Follower.id == self.id
Author.id == self.id
))
.group_by(Post)
.order_by(Post.timestamp.desc())
)
2024-08-01 13:35:28 +00:00
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
body: so.Mapped[str] = so.mapped_column(sa.String(140))
timestamp: so.Mapped[datetime] = so.mapped_column(index=True, default=lambda: datetime.now(timezone.utc))
user_id: so.Mapped[int] = so.mapped_column(sa.ForeignKey(User.id), index=True)
author: so.Mapped[User] = so.relationship(back_populates='posts')
def __repr__(self):
return '<Post {}>'.format(self.body)
2024-08-01 18:10:49 +00:00
@login.user_loader
def load_user(id):
return db.session.get(User, int(id))