By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 models.py

View raw Download
text/x-script.python • 14.74 kiB
Python script, ASCII text executable
        
            
1
import subprocess
2
3
from app import app, db, bcrypt
4
import git
5
from datetime import datetime
6
from enum import Enum
7
from PIL import Image
8
from cairosvg import svg2png
9
import os
10
import config
11
import cairosvg
12
import random
13
14
__all__ = [
15
"RepoAccess",
16
"RepoFavourite",
17
"Repo",
18
"UserFollow",
19
"UserNotification",
20
"User",
21
"Notification",
22
"PostVote",
23
"Post",
24
"Commit",
25
"PullRequest",
26
]
27
28
with (app.app_context()):
29
class RepoAccess(db.Model):
30
id = db.Column(db.Integer, primary_key=True)
31
user_username = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
32
repo_route = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False)
33
access_level = db.Column(db.SmallInteger(), nullable=False) # 0 read-only, 1 read-write, 2 admin
34
35
user = db.relationship("User", back_populates="repo_access")
36
repo = db.relationship("Repo", back_populates="repo_access")
37
38
__table_args__ = (db.UniqueConstraint("user_username", "repo_route", name="_user_repo_uc"),)
39
40
def __init__(self, user, repo, level):
41
self.user_username = user.username
42
self.repo_route = repo.route
43
self.access_level = level
44
45
46
class RepoFavourite(db.Model):
47
id = db.Column(db.Integer, primary_key=True)
48
user_username = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
49
repo_route = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False)
50
51
notify_commit = db.Column(db.Boolean)
52
notify_forum = db.Column(db.Boolean)
53
notify_pr = db.Column(db.Boolean)
54
notify_admin = db.Column(db.Boolean)
55
56
user = db.relationship("User", back_populates="favourites")
57
repo = db.relationship("Repo", back_populates="favourites")
58
59
__table_args__ = (db.UniqueConstraint("user_username", "repo_route", name="_user_repo_uc1"),)
60
61
def __init__(self, user, repo):
62
self.user_username = user.username
63
self.repo_route = repo.route
64
65
66
class PostVote(db.Model):
67
id = db.Column(db.Integer, primary_key=True)
68
user_username = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
69
post_identifier = db.Column(db.String(109), db.ForeignKey("post.identifier"), nullable=False)
70
vote_score = db.Column(db.SmallInteger(), nullable=False)
71
72
user = db.relationship("User", back_populates="votes")
73
post = db.relationship("Post", back_populates="votes")
74
75
__table_args__ = (db.UniqueConstraint("user_username", "post_identifier", name="_user_post_uc"),)
76
77
def __init__(self, user, post, score):
78
self.user_username = user.username
79
self.post_identifier = post.identifier
80
self.vote_score = score
81
82
83
class User(db.Model):
84
username = db.Column(db.String(32), unique=True, nullable=False, primary_key=True)
85
display_name = db.Column(db.Unicode(128), unique=False, nullable=True)
86
bio = db.Column(db.Unicode(16384), unique=False, nullable=True)
87
password_hashed = db.Column(db.String(60), nullable=False)
88
email = db.Column(db.String(254), nullable=True)
89
company = db.Column(db.Unicode(64), nullable=True)
90
company_url = db.Column(db.String(256), nullable=True)
91
url = db.Column(db.String(256), nullable=True)
92
show_mail = db.Column(db.Boolean, default=False, nullable=False)
93
location = db.Column(db.Unicode(64), nullable=True)
94
creation_date = db.Column(db.DateTime, default=datetime.utcnow)
95
96
repositories = db.relationship("Repo", back_populates="owner")
97
followers = db.relationship("UserFollow", back_populates="followed", foreign_keys="[UserFollow.followed_username]")
98
follows = db.relationship("UserFollow", back_populates="follower", foreign_keys="[UserFollow.follower_username]")
99
repo_access = db.relationship("RepoAccess", back_populates="user")
100
votes = db.relationship("PostVote", back_populates="user")
101
favourites = db.relationship("RepoFavourite", back_populates="user")
102
103
commits = db.relationship("Commit", back_populates="owner")
104
posts = db.relationship("Post", back_populates="owner")
105
prs = db.relationship("PullRequest", back_populates="owner")
106
notifications = db.relationship("UserNotification", back_populates="user")
107
108
def __init__(self, username, password, email=None, display_name=None):
109
self.username = username
110
self.password_hashed = bcrypt.generate_password_hash(password, config.HASHING_ROUNDS).decode("utf-8")
111
self.email = email
112
self.display_name = display_name
113
114
# Create the user's directory
115
if not os.path.exists(os.path.join(config.REPOS_PATH, username)):
116
os.makedirs(os.path.join(config.REPOS_PATH, username))
117
if not os.path.exists(os.path.join(config.USERDATA_PATH, username)):
118
os.makedirs(os.path.join(config.USERDATA_PATH, username))
119
120
avatar_name = random.choice(os.listdir(config.DEFAULT_AVATARS_PATH))
121
if os.path.join(config.DEFAULT_AVATARS_PATH, avatar_name).endswith(".svg"):
122
cairosvg.svg2png(url=os.path.join(config.DEFAULT_AVATARS_PATH, avatar_name),
123
write_to="/tmp/roundabout-avatar.png")
124
avatar = Image.open("/tmp/roundabout-avatar.png")
125
else:
126
avatar = Image.open(os.path.join(config.DEFAULT_AVATARS_PATH, avatar_name))
127
avatar.thumbnail(config.AVATAR_SIZE)
128
avatar.save(os.path.join(config.USERDATA_PATH, username, "avatar.png"))
129
130
# Create the configuration repo
131
config_repo = Repo(self, ".config", 0)
132
db.session.add(config_repo)
133
db.session.commit()
134
135
136
class Repo(db.Model):
137
route = db.Column(db.String(98), unique=True, nullable=False, primary_key=True)
138
owner_name = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
139
name = db.Column(db.String(64), nullable=False)
140
owner = db.relationship("User", back_populates="repositories")
141
visibility = db.Column(db.SmallInteger(), nullable=False)
142
info = db.Column(db.Unicode(512), nullable=True)
143
url = db.Column(db.String(256), nullable=True)
144
creation_date = db.Column(db.DateTime, default=datetime.utcnow)
145
146
default_branch = db.Column(db.String(64), nullable=True, default="")
147
148
commits = db.relationship("Commit", back_populates="repo")
149
posts = db.relationship("Post", back_populates="repo")
150
repo_access = db.relationship("RepoAccess", back_populates="repo")
151
favourites = db.relationship("RepoFavourite", back_populates="repo")
152
heads = db.relationship("PullRequest", back_populates="head", foreign_keys="[PullRequest.head_route]")
153
bases = db.relationship("PullRequest", back_populates="base", foreign_keys="[PullRequest.base_route]")
154
155
last_post_id = db.Column(db.Integer, nullable=False, default=0)
156
157
def __init__(self, owner, name, visibility):
158
self.route = f"/{owner.username}/{name}"
159
self.name = name
160
self.owner_name = owner.username
161
self.owner = owner
162
self.visibility = visibility
163
164
# Add the owner as an admin
165
repo_access = RepoAccess(owner, self, 2)
166
db.session.add(repo_access)
167
168
# Create the directory
169
if not os.path.exists(os.path.join(config.REPOS_PATH, self.owner_name, self.name)):
170
subprocess.run(["git", "init", self.name],
171
cwd=os.path.join(config.REPOS_PATH, self.owner_name))
172
173
174
class Commit(db.Model):
175
identifier = db.Column(db.String(227), unique=True, nullable=False, primary_key=True)
176
sha = db.Column(db.String(128), nullable=False)
177
repo_name = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False)
178
owner_name = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
179
owner_identity = db.Column(db.String(321))
180
receive_date = db.Column(db.DateTime, default=datetime.now)
181
author_date = db.Column(db.DateTime)
182
message = db.Column(db.UnicodeText)
183
repo = db.relationship("Repo", back_populates="commits")
184
owner = db.relationship("User", back_populates="commits")
185
186
def __init__(self, sha, owner, repo, date, message, owner_identity):
187
self.identifier = f"{repo.route}/{sha}"
188
self.sha = sha
189
self.repo_name = repo.route
190
self.repo = repo
191
self.owner_name = owner.username
192
self.owner = owner
193
self.author_date = datetime.fromtimestamp(int(date))
194
self.message = message
195
self.owner_identity = owner_identity
196
197
198
class Post(db.Model):
199
identifier = db.Column(db.String(109), unique=True, nullable=False, primary_key=True)
200
number = db.Column(db.Integer, nullable=False)
201
repo_name = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False)
202
owner_name = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
203
votes = db.relationship("PostVote", back_populates="post")
204
vote_sum = db.Column(db.Integer, nullable=False, default=0)
205
206
parent_id = db.Column(db.String(109), db.ForeignKey("post.identifier"), nullable=True)
207
root_id = db.Column(db.String(109), db.ForeignKey("post.identifier"), nullable=True)
208
state = db.Column(db.SmallInteger, nullable=True, default=1)
209
210
date = db.Column(db.DateTime, default=datetime.now)
211
last_updated = db.Column(db.DateTime, default=datetime.now)
212
subject = db.Column(db.Unicode(384))
213
message = db.Column(db.UnicodeText)
214
repo = db.relationship("Repo", back_populates="posts")
215
owner = db.relationship("User", back_populates="posts")
216
parent = db.relationship("Post", back_populates="children",
217
primaryjoin="Post.parent_id==Post.identifier",
218
foreign_keys="[Post.parent_id]", remote_side="Post.identifier")
219
root = db.relationship("Post",
220
primaryjoin="Post.root_id==Post.identifier",
221
foreign_keys="[Post.root_id]", remote_side="Post.identifier")
222
children = db.relationship("Post",
223
remote_side="Post.parent_id",
224
primaryjoin="Post.identifier==Post.parent_id",
225
foreign_keys="[Post.parent_id]")
226
227
def __init__(self, owner, repo, parent, subject, message):
228
self.identifier = f"{repo.route}/{repo.last_post_id}"
229
self.number = repo.last_post_id
230
self.repo_name = repo.route
231
self.repo = repo
232
self.owner_name = owner.username
233
self.owner = owner
234
self.subject = subject
235
self.message = message
236
self.parent = parent
237
if parent and parent.parent:
238
self.root = parent.parent
239
elif parent:
240
self.root = parent
241
else:
242
self.root = None
243
repo.last_post_id += 1
244
245
def update_date(self):
246
self.last_updated = datetime.now()
247
with db.session.no_autoflush:
248
if self.parent is not None:
249
self.parent.update_date()
250
251
252
class UserNotification(db.Model):
253
id = db.Column(db.Integer, primary_key=True)
254
user_username = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
255
notification_id = db.Column(db.BigInteger, db.ForeignKey("notification.id"))
256
attention_level = db.Column(db.SmallInteger, nullable=False) # 0 is read
257
read_time = db.Column(db.DateTime, nullable=True)
258
259
user = db.relationship("User", back_populates="notifications")
260
notification = db.relationship("Notification", back_populates="notifications")
261
262
__table_args__ = (db.UniqueConstraint("user_username", "notification_id", name="_user_notification_uc"),)
263
264
def __init__(self, user, notification, level):
265
self.user_username = user.username
266
self.notification_id = notification.id
267
self.attention_level = level
268
269
def mark_read(self):
270
self.read_time = datetime.utcnow()
271
self.attention_level = 0
272
273
def mark_unread(self):
274
self.attention_level = 4
275
276
277
class UserFollow(db.Model):
278
id = db.Column(db.Integer, primary_key=True)
279
follower_username = db.Column(db.String(32), db.ForeignKey("user.username", ondelete="CASCADE"), nullable=False)
280
followed_username = db.Column(db.String(32), db.ForeignKey("user.username", ondelete="CASCADE"), nullable=False)
281
282
follower = db.relationship("User", back_populates="followers", foreign_keys=[follower_username])
283
followed = db.relationship("User", back_populates="follows", foreign_keys=[followed_username])
284
285
def __init__(self, follower_username, followed_username):
286
self.follower_username = follower_username
287
self.followed_username = followed_username
288
289
290
class Notification(db.Model):
291
id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
292
data = db.Column(db.dialects.postgresql.JSONB, nullable=False, default={})
293
notifications = db.relationship("UserNotification", back_populates="notification")
294
timestamp = db.Column(db.DateTime, nullable=False, default=datetime.now)
295
296
def __init__(self, json):
297
self.data = json
298
299
300
class PullRequest(db.Model):
301
id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
302
head_route = db.Column(db.String(98), db.ForeignKey("repo.route", ondelete="CASCADE"), nullable=False)
303
base_route = db.Column(db.String(98), db.ForeignKey("repo.route", ondelete="CASCADE"), nullable=False)
304
owner_name = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
305
state = db.Column(db.SmallInteger, nullable=False, default=0) # 0 pending, 1 merged, 2 rejected
306
307
head = db.relationship("Repo", back_populates="heads", foreign_keys=[head_route])
308
base = db.relationship("Repo", back_populates="bases", foreign_keys=[base_route])
309
310
head_branch = db.Column(db.String(64), nullable=False)
311
base_branch = db.Column(db.String(64), nullable=False)
312
313
owner = db.relationship("User", back_populates="prs")
314
timestamp = db.Column(db.DateTime, nullable=False, default=datetime.now)
315
316
def __init__(self, head, head_branch, base, base_branch, owner):
317
self.head = head
318
self.base = base
319
self.head_branch = head_branch
320
self.base_branch = base_branch
321
self.owner = owner
322