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