Web platform for sharing free data for ML and research

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

 app.py

View raw Download
text/x-script.python • 11.44 kiB
Python script, ASCII text executable
        
            
1
from datetime import datetime
2
from email.policy import default
3
4
import flask
5
from flask_sqlalchemy import SQLAlchemy
6
from flask_bcrypt import Bcrypt
7
from flask_httpauth import HTTPBasicAuth
8
from markupsafe import escape, Markup
9
from flask_migrate import Migrate
10
from jinja2_fragments.flask import render_block
11
from sqlalchemy.orm import backref
12
import sqlalchemy.dialects.postgresql
13
from os import path
14
15
import config
16
import markdown
17
18
19
app = flask.Flask(__name__)
20
bcrypt = Bcrypt(app)
21
22
23
app.config["SQLALCHEMY_DATABASE_URI"] = config.DB_URI
24
app.config["SECRET_KEY"] = config.DB_PASSWORD
25
26
27
db = SQLAlchemy(app)
28
migrate = Migrate(app, db)
29
30
31
@app.template_filter("split")
32
def split(value, separator=None, maxsplit=-1):
33
return value.split(separator, maxsplit)
34
35
36
37
with app.app_context():
38
class User(db.Model):
39
username = db.Column(db.String(32), unique=True, nullable=False, primary_key=True)
40
password_hashed = db.Column(db.String(60), nullable=False)
41
admin = db.Column(db.Boolean, nullable=False, default=False, server_default="false")
42
43
def __init__(self, username, password):
44
self.username = username
45
self.password_hashed = bcrypt.generate_password_hash(password).decode("utf-8")
46
47
48
class Licence(db.Model):
49
id = db.Column(db.String(32), primary_key=True) # SPDX identifier
50
title = db.Column(db.UnicodeText, nullable=False) # the official name of the licence
51
description = db.Column(db.UnicodeText, nullable=False) # brief description of its permissions and restrictions
52
legal_text = db.Column(db.UnicodeText, nullable=False) # the full legal text of the licence
53
url = db.Column(db.String(2048), nullable=True) # the URL to a page with the full text of the licence and more information
54
pictures = db.relationship("PictureLicence", back_populates="licence")
55
56
def __init__(self, id, title, description, legal_text, url):
57
self.id = id
58
self.title = title
59
self.description = description
60
self.legal_text = legal_text
61
self.url = url
62
63
64
class PictureLicence(db.Model):
65
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
66
67
resource_id = db.Column(db.Integer, db.ForeignKey("picture_resource.id"))
68
licence_id = db.Column(db.String(32), db.ForeignKey("licence.id"))
69
70
resource = db.relationship("PictureResource", back_populates="licences")
71
licence = db.relationship("Licence", back_populates="pictures")
72
73
def __init__(self, resource_id, licence_id):
74
self.resource_id = resource_id
75
self.licence_id = licence_id
76
77
78
class Resource(db.Model):
79
__abstract__ = True
80
81
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
82
title = db.Column(db.UnicodeText, nullable=False)
83
description = db.Column(db.UnicodeText, nullable=False)
84
timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
85
origin_url = db.Column(db.String(2048), nullable=True) # should be left empty if it's original or the source is unknown but public domain
86
87
88
class PictureNature(db.Model):
89
# Examples:
90
# "photo", "paper-scan", "2d-art-photo", "sculpture-photo", "computer-3d", "computer-painting",
91
# "computer-line-art", "diagram", "infographic", "text", "map", "chart-graph", "screen-capture",
92
# "screen-photo", "pattern", "collage", "ai", and so on
93
id = db.Column(db.String(64), primary_key=True)
94
description = db.Column(db.UnicodeText, nullable=False)
95
resources = db.relationship("PictureResource", back_populates="nature")
96
97
def __init__(self, id, description):
98
self.id = id
99
self.description = description
100
101
102
class PictureObjectInheritance(db.Model):
103
parent_id = db.Column(db.String(64), db.ForeignKey("picture_object.id"),
104
primary_key=True)
105
child_id = db.Column(db.String(64), db.ForeignKey("picture_object.id"),
106
primary_key=True)
107
108
parent = db.relationship("PictureObject", foreign_keys=[parent_id],
109
back_populates="child_links")
110
child = db.relationship("PictureObject", foreign_keys=[child_id],
111
back_populates="parent_links")
112
113
def __init__(self, parent, child):
114
self.parent = parent
115
self.child = child
116
117
118
class PictureObject(db.Model):
119
id = db.Column(db.String(64), primary_key=True)
120
description = db.Column(db.UnicodeText, nullable=False)
121
122
child_links = db.relationship("PictureObjectInheritance",
123
foreign_keys=[PictureObjectInheritance.parent_id],
124
back_populates="parent")
125
parent_links = db.relationship("PictureObjectInheritance",
126
foreign_keys=[PictureObjectInheritance.child_id],
127
back_populates="child")
128
129
def __init__(self, id, description):
130
self.id = id
131
self.description = description
132
133
134
class PictureRegion(db.Model):
135
# This is for picture region annotations
136
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
137
json = db.Column(sqlalchemy.dialects.postgresql.JSONB, nullable=False)
138
139
resource_id = db.Column(db.Integer, db.ForeignKey("picture_resource.id"), nullable=False)
140
object_id = db.Column(db.String(64), db.ForeignKey("picture_object.id"), nullable=False)
141
142
resource = db.relationship("PictureResource", backref="regions")
143
object = db.relationship("PictureObject", backref="regions")
144
145
def __init__(self, json, resource, object):
146
self.json = json
147
self.resource = resource
148
self.object = object
149
150
151
class PictureResource(Resource):
152
# This is only for bitmap pictures. Vectors will be stored under a different model
153
# File name is the ID in the picture directory under data, without an extension
154
file_format = db.Column(db.String(64), nullable=False) # MIME type
155
width = db.Column(db.Integer, nullable=False)
156
height = db.Column(db.Integer, nullable=False)
157
nature_id = db.Column(db.String(32), db.ForeignKey("picture_nature.id"), nullable=True)
158
159
nature = db.relationship("PictureNature", back_populates="resources")
160
161
replaces_id = db.Column(db.Integer, db.ForeignKey("picture_resource.id"), nullable=True)
162
replaced_by_id = db.Column(db.Integer, db.ForeignKey("picture_resource.id"),
163
nullable=True)
164
165
replaces = db.relationship("PictureResource", remote_side="PictureResource.id",
166
foreign_keys=[replaces_id], back_populates="replaced_by")
167
replaced_by = db.relationship("PictureResource", remote_side="PictureResource.id",
168
foreign_keys=[replaced_by_id])
169
170
licences = db.relationship("PictureLicence", back_populates="resource")
171
172
def __init__(self, title, description, origin_url, licence_ids, mime, nature=None,
173
replaces=None):
174
self.title = title
175
self.description = description
176
self.origin_url = origin_url
177
self.file_format = mime
178
self.width = self.height = 0
179
self.nature = nature
180
db.session.add(self)
181
db.session.commit()
182
for licence_id in licence_ids:
183
joiner = PictureLicence(self.id, licence_id)
184
db.session.add(joiner)
185
if replaces is not None:
186
self.replaces = replaces
187
replaces.replaced_by = self
188
189
190
@app.route("/")
191
def index():
192
return flask.render_template("home.html")
193
194
195
@app.route("/accounts")
196
def accounts():
197
return flask.render_template("login.html")
198
199
200
@app.route("/login", methods=["POST"])
201
def login():
202
username = flask.request.form["username"]
203
password = flask.request.form["password"]
204
205
user = db.session.get(User, username)
206
207
if user is None:
208
flask.flash("This username is not registered.")
209
return flask.redirect("/accounts")
210
211
if not bcrypt.check_password_hash(user.password_hashed, password):
212
flask.flash("Incorrect password.")
213
return flask.redirect("/accounts")
214
215
flask.flash("You have been logged in.")
216
217
flask.session["username"] = username
218
return flask.redirect("/")
219
220
221
@app.route("/logout")
222
def logout():
223
flask.session.pop("username", None)
224
flask.flash("You have been logged out.")
225
return flask.redirect("/")
226
227
228
@app.route("/signup", methods=["POST"])
229
def signup():
230
username = flask.request.form["username"]
231
password = flask.request.form["password"]
232
233
if db.session.get(User, username) is not None:
234
flask.flash("This username is already taken.")
235
return flask.redirect("/accounts")
236
237
if set(username) > set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"):
238
flask.flash("Usernames can only contain the Latin alphabet, digits, hyphens, and underscores.")
239
return flask.redirect("/accounts")
240
241
if len(username) < 3 or len(username) > 32:
242
flask.flash("Usernames must be between 3 and 32 characters long.")
243
return flask.redirect("/accounts")
244
245
if len(password) < 6:
246
flask.flash("Passwords must be at least 6 characters long.")
247
return flask.redirect("/accounts")
248
249
user = User(username, None, password)
250
db.session.add(user)
251
db.session.commit()
252
253
flask.session["username"] = username
254
255
flask.flash("You have been registered and logged in.")
256
257
return flask.redirect("/")
258
259
260
@app.route("/profile", defaults={"username": None})
261
@app.route("/profile/<username>")
262
def profile(username):
263
if username is None:
264
if "username" in flask.session:
265
return flask.redirect("/profile/" + flask.session["username"])
266
else:
267
flask.flash("Please log in to perform this action.")
268
return flask.redirect("/accounts")
269
270
user = db.session.get(User, username)
271
if user is None:
272
return flask.abort(404)
273
274
return flask.render_template("profile.html", user=user)
275
276
277
@app.route("/upload")
278
def upload():
279
return flask.render_template("upload.html")
280
281
282
@app.route("/upload", methods=["POST"])
283
def upload_post():
284
title = flask.request.form["title"]
285
description = flask.request.form["description"]
286
origin_url = flask.request.form["origin_url"]
287
288
file = flask.request.files["file"]
289
290
if not file or not file.filename:
291
flask.flash("No selected file")
292
return flask.redirect(flask.request.url)
293
294
resource = PictureResource(title, description, origin_url, ["CC0-1.0"], file.mimetype)
295
db.session.add(resource)
296
db.session.commit()
297
file.save(path.join(config.DATA_PATH, "pictures", str(resource.id)))
298
299
return flask.redirect("/picture/" + str(resource.id))
300
301
302
@app.route("/picture/<int:id>")
303
def picture(id):
304
resource = db.session.get(PictureResource, id)
305
if resource is None:
306
return flask.abort(404)
307
308
return flask.render_template("picture.html", resource=resource)
309
310
311
@app.route("/raw/picture/<int:id>")
312
def raw_picture(id):
313
resource = db.session.get(PictureResource, id)
314
if resource is None:
315
return flask.abort(404)
316
317
response = flask.send_from_directory(path.join(config.DATA_PATH, "pictures"), str(resource.id))
318
response.mimetype = resource.file_format
319
320
return response
321