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