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