models.py
Python script, ASCII text executable
1from app import app, db 2import git 3from datetime import datetime 4from enum import Enum 5from PIL import Image 6from cairosvg import svg2png 7 8__all__ = [ 9"RepoAccess", 10"RepoFavourite", 11"Repo", 12"UserFollow", 13"UserNotification", 14"User", 15"Notification", 16"PostVote", 17"Post", 18"Commit", 19] 20 21with app.app_context(): 22class RepoAccess(db.Model): 23id = db.Column(db.Integer, primary_key=True) 24userUsername = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 25repoRoute = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False) 26accessLevel = db.Column(db.SmallInteger(), nullable=False) # 0 read-only, 1 read-write, 2 admin 27 28user = db.relationship("User", back_populates="repoAccess") 29repo = db.relationship("Repo", back_populates="repoAccess") 30 31__table_args__ = (db.UniqueConstraint("userUsername", "repoRoute", name="_user_repo_uc"),) 32 33def __init__(self, user, repo, level): 34self.userUsername = user.username 35self.repoRoute = repo.route 36self.accessLevel = level 37 38 39class RepoFavourite(db.Model): 40id = db.Column(db.Integer, primary_key=True) 41userUsername = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 42repoRoute = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False) 43 44user = db.relationship("User", back_populates="favourites") 45repo = db.relationship("Repo", back_populates="favourites") 46 47__table_args__ = (db.UniqueConstraint("userUsername", "repoRoute", name="_user_repo_uc1"),) 48 49def __init__(self, user, repo): 50self.userUsername = user.username 51self.repoRoute = repo.route 52 53 54class PostVote(db.Model): 55id = db.Column(db.Integer, primary_key=True) 56userUsername = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 57postIdentifier = db.Column(db.String(109), db.ForeignKey("post.identifier"), nullable=False) 58voteScore = db.Column(db.SmallInteger(), nullable=False) 59 60user = db.relationship("User", back_populates="votes") 61post = db.relationship("Post", back_populates="votes") 62 63__table_args__ = (db.UniqueConstraint("userUsername", "postIdentifier", name="_user_post_uc"),) 64 65def __init__(self, user, post, score): 66self.userUsername = user.username 67self.postIdentifier = post.identifier 68self.voteScore = score 69 70 71class User(db.Model): 72username = db.Column(db.String(32), unique=True, nullable=False, primary_key=True) 73displayName = db.Column(db.Unicode(128), unique=False, nullable=True) 74bio = db.Column(db.Unicode(512), unique=False, nullable=True) 75passwordHashed = db.Column(db.String(60), nullable=False) 76email = db.Column(db.String(254), nullable=True) 77company = db.Column(db.Unicode(64), nullable=True) 78companyURL = db.Column(db.String(256), nullable=True) 79URL = db.Column(db.String(256), nullable=True) 80showMail = db.Column(db.Boolean, default=False, nullable=False) 81location = db.Column(db.Unicode(64), nullable=True) 82creationDate = db.Column(db.DateTime, default=datetime.utcnow) 83 84repositories = db.relationship("Repo", back_populates="owner") 85followers = db.relationship("UserFollow", back_populates="followed", foreign_keys="[UserFollow.followedUsername]") 86follows = db.relationship("UserFollow", back_populates="follower", foreign_keys="[UserFollow.followerUsername]") 87repoAccess = db.relationship("RepoAccess", back_populates="user") 88votes = db.relationship("PostVote", back_populates="user") 89favourites = db.relationship("RepoFavourite", back_populates="user") 90 91commits = db.relationship("Commit", back_populates="owner") 92posts = db.relationship("Post", back_populates="owner") 93notifications = db.relationship("UserNotification", back_populates="user") 94 95def __init__(self, username, password, email=None, displayName=None): 96self.username = username 97self.passwordHashed = bcrypt.generate_password_hash(password, config.HASHING_ROUNDS).decode("utf-8") 98self.email = email 99self.displayName = displayName 100 101# Create the user's directory 102if not os.path.exists(os.path.join(config.REPOS_PATH, username)): 103os.makedirs(os.path.join(config.REPOS_PATH, username)) 104if not os.path.exists(os.path.join(config.USERDATA_PATH, username)): 105os.makedirs(os.path.join(config.USERDATA_PATH, username)) 106 107avatarName = random.choice(os.listdir(config.DEFAULT_AVATARS_PATH)) 108if os.path.join(config.DEFAULT_AVATARS_PATH, avatarName).endswith(".svg"): 109cairosvg.svg2png(url=os.path.join(config.DEFAULT_AVATARS_PATH, avatarName), 110write_to="/tmp/roundabout-avatar.png") 111avatar = Image.open("/tmp/roundabout-avatar.png") 112else: 113avatar = Image.open(os.path.join(config.DEFAULT_AVATARS_PATH, avatarName)) 114avatar.thumbnail(config.AVATAR_SIZE) 115avatar.save(os.path.join(config.USERDATA_PATH, username, "avatar.png")) 116 117 118class Repo(db.Model): 119route = db.Column(db.String(98), unique=True, nullable=False, primary_key=True) 120ownerName = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 121name = db.Column(db.String(64), nullable=False) 122owner = db.relationship("User", back_populates="repositories") 123visibility = db.Column(db.SmallInteger(), nullable=False) 124info = db.Column(db.Unicode(512), nullable=True) 125URL = db.Column(db.String(256), nullable=True) 126creationDate = db.Column(db.DateTime, default=datetime.utcnow) 127 128defaultBranch = db.Column(db.String(64), nullable=True, default="") 129 130commits = db.relationship("Commit", back_populates="repo") 131posts = db.relationship("Post", back_populates="repo") 132repoAccess = db.relationship("RepoAccess", back_populates="repo") 133favourites = db.relationship("RepoFavourite", back_populates="repo") 134 135lastPostID = db.Column(db.Integer, nullable=False, default=0) 136 137def __init__(self, owner, name, visibility): 138self.route = f"/{owner.username}/{name}" 139self.name = name 140self.ownerName = owner.username 141self.owner = owner 142self.visibility = visibility 143 144# Add the owner as an admin 145repoAccess = RepoAccess(owner, self, 2) 146db.session.add(repoAccess) 147 148 149class Commit(db.Model): 150identifier = db.Column(db.String(227), unique=True, nullable=False, primary_key=True) 151sha = db.Column(db.String(128), nullable=False) 152repoName = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False) 153ownerName = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 154ownerIdentity = db.Column(db.String(321)) 155receiveDate = db.Column(db.DateTime, default=datetime.now) 156authorDate = db.Column(db.DateTime) 157message = db.Column(db.UnicodeText) 158repo = db.relationship("Repo", back_populates="commits") 159owner = db.relationship("User", back_populates="commits") 160 161def __init__(self, sha, owner, repo, date, message, ownerIdentity): 162self.identifier = f"{repo.route}/{sha}" 163self.sha = sha 164self.repoName = repo.route 165self.repo = repo 166self.ownerName = owner.username 167self.owner = owner 168self.authorDate = datetime.fromtimestamp(int(date)) 169self.message = message 170self.ownerIdentity = ownerIdentity 171 172 173class Post(db.Model): 174identifier = db.Column(db.String(109), unique=True, nullable=False, primary_key=True) 175number = db.Column(db.Integer, nullable=False) 176repoName = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False) 177ownerName = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 178votes = db.relationship("PostVote", back_populates="post") 179voteSum = db.Column(db.Integer, nullable=False, default=0) 180 181parentID = db.Column(db.String(109), db.ForeignKey("post.identifier"), nullable=True) 182state = db.Column(db.SmallInteger, nullable=True, default=1) 183 184date = db.Column(db.DateTime, default=datetime.now) 185lastUpdated = db.Column(db.DateTime, default=datetime.now) 186subject = db.Column(db.Unicode(384)) 187message = db.Column(db.UnicodeText) 188repo = db.relationship("Repo", back_populates="posts") 189owner = db.relationship("User", back_populates="posts") 190parent = db.relationship("Post", back_populates="children", remote_side="Post.identifier") 191children = db.relationship("Post", back_populates="parent", remote_side="Post.parentID") 192 193def __init__(self, owner, repo, parent, subject, message): 194self.identifier = f"{repo.route}/{repo.lastPostID}" 195self.number = repo.lastPostID 196self.repoName = repo.route 197self.repo = repo 198self.ownerName = owner.username 199self.owner = owner 200self.subject = subject 201self.message = message 202self.parent = parent 203repo.lastPostID += 1 204 205def updateDate(self): 206self.lastUpdated = datetime.now() 207with db.session.no_autoflush: 208if self.parent is not None: 209self.parent.updateDate() 210 211 212class UserNotification(db.Model): 213id = db.Column(db.Integer, primary_key=True) 214userUsername = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 215notificationID = db.Column(db.BigInteger, db.ForeignKey("notification.id")) 216attentionLevel = db.Column(db.SmallInteger, nullable=False) # 0 is read 217readTime = db.Column(db.DateTime, nullable=True) 218 219user = db.relationship("User", back_populates="notifications") 220notification = db.relationship("Notification", back_populates="notifications") 221 222__table_args__ = (db.UniqueConstraint("userUsername", "notificationID", name="_user_notification_uc"),) 223 224def __init__(self, user, notification, level): 225self.userUsername = user.username 226self.notificationID = notification.id 227self.attentionLevel = level 228 229def read(self): 230self.readTime = datetime.utcnow 231self.attentionLevel = 0 232 233 234class UserFollow(db.Model): 235id = db.Column(db.Integer, primary_key=True) 236followerUsername = db.Column(db.String(32), db.ForeignKey("user.username", ondelete="CASCADE"), nullable=False) 237followedUsername = db.Column(db.String(32), db.ForeignKey("user.username", ondelete="CASCADE"), nullable=False) 238 239follower = db.relationship("User", back_populates="follows", foreign_keys=[followedUsername]) 240followed = db.relationship("User", back_populates="followers", foreign_keys=[followerUsername]) 241 242def __init__(self, followerUsername, followedUsername): 243self.followerUsername = followerUsername 244self.followedUsername = followedUsername 245 246 247class Notification(db.Model): 248id = db.Column(db.BigInteger, primary_key=True, autoincrement=True) 249data = db.Column(db.dialects.postgresql.JSONB, nullable=False, default={}) 250notifications = db.relationship("UserNotification", back_populates="notification") 251timestamp = db.Column(db.DateTime, nullable=False, default=datetime.now) 252 253def __init__(self, json): 254self.data = json 255 256# def sendTo(self, users, level): 257# for user in users: 258# db.session.add(UserNotification(user, self, level)) 259 260