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