roundabout,
created on Monday, 9 September 2024, 14:10:29 (1725891029),
received on Monday, 9 September 2024, 14:10:32 (1725891032)
Author identity: vlad <vlad.muntoiu@gmail.com>
40f27cd24041b1d4408482743783551a3d0a525a
app.py
@@ -76,6 +76,7 @@ with app.app_context():
admin = db.Column(db.Boolean, nullable=False, default=False, server_default="false")
pictures = db.relationship("PictureResource", back_populates="author")
joined_timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
galleries = db.relationship("Gallery", back_populates="owner")
def __init__(self, username, password):
self.username = username
@@ -224,6 +225,7 @@ with app.app_context():
backref="copies", foreign_keys=[copied_from_id])
licences = db.relationship("PictureLicence", back_populates="resource")
galleries = db.relationship("PictureInGallery", back_populates="resource")
def __init__(self, title, author, description, origin_url, licence_ids, mime, nature=None):
self.title = title
@@ -256,6 +258,34 @@ with app.app_context():
db.session.add(region_row)
class PictureInGallery(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
resource_id = db.Column(db.Integer, db.ForeignKey("picture_resource.id"), nullable=False)
gallery_id = db.Column(db.Integer, db.ForeignKey("gallery.id"), nullable=False)
resource = db.relationship("PictureResource")
gallery = db.relationship("Gallery")
def __init__(self, resource, gallery):
self.resource = resource
self.gallery = gallery
class Gallery(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.UnicodeText, nullable=False)
description = db.Column(db.UnicodeText, nullable=False)
pictures = db.relationship("PictureInGallery", back_populates="gallery")
owner_name = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False)
owner = db.relationship("User", back_populates="galleries")
def __init__(self, title, description, owner):
self.title = title
self.description = description
self.owner = owner
@app.route("/")
def index():
return flask.render_template("home.html", resources=PictureResource.query.order_by(db.func.random()).limit(10).all())
@@ -575,7 +605,7 @@ def delete_picture(id):
@app.route("/picture/<int:id>/mark-replacement", methods=["POST"])
def mark_replacement(id):
def mark_picture_replacement(id):
resource = db.session.get(PictureResource, id)
if resource is None:
flask.abort(404)
@@ -596,7 +626,7 @@ def mark_replacement(id):
@app.route("/picture/<int:id>/remove-replacement", methods=["POST"])
def remove_replacement(id):
def remove_picture_replacement(id):
resource = db.session.get(PictureResource, id)
if resource is None:
flask.abort(404)
@@ -722,6 +752,78 @@ def copy_picture(id):
return flask.redirect("/picture/" + str(new_resource.id))
@app.route("/gallery/<int:id>/")
def gallery(id):
gallery = db.session.get(Gallery, id)
if gallery is None:
flask.abort(404)
return flask.render_template("gallery.html", gallery=gallery)
@app.route("/create-gallery")
def create_gallery():
if "username" not in flask.session:
flask.flash("Log in to create galleries.")
return flask.redirect("/accounts")
return flask.render_template("create-gallery.html")
@app.route("/create-gallery", methods=["POST"])
def create_gallery_post():
if not flask.session.get("username"):
flask.abort(401)
if not flask.request.form.get("title"):
flask.flash("Enter a title")
return flask.redirect(flask.request.url)
description = flask.request.form.get("description", "")
gallery = Gallery(flask.request.form["title"], description, db.session.get(User, flask.session["username"]))
db.session.add(gallery)
db.session.commit()
return flask.redirect("/gallery/" + str(gallery.id))
@app.route("/gallery/<int:id>/add-picture", methods=["POST"])
def gallery_add_picture(id):
gallery = db.session.get(Gallery, id)
if gallery is None:
flask.abort(404)
if "username" not in flask.session:
flask.abort(401)
if flask.session["username"] != gallery.owner_name and not db.session.get(User, flask.session["username"]).admin:
flask.abort(403)
picture_id = flask.request.form.get("picture_id")
if "/" in picture_id: # also allow full URLs
picture_id = picture_id.rstrip("/").rpartition("/")[1]
if not picture_id:
flask.flash("Select a picture")
return flask.redirect("/gallery/" + str(gallery.id))
picture_id = int(picture_id)
picture = db.session.get(PictureResource, picture_id)
if picture is None:
flask.flash("Invalid picture")
return flask.redirect("/gallery/" + str(gallery.id))
if PictureInGallery.query.filter_by(resource=picture, gallery=gallery).first():
flask.flash("This picture is already in the gallery")
return flask.redirect("/gallery/" + str(gallery.id))
db.session.add(PictureInGallery(picture, gallery))
db.session.commit()
return flask.redirect("/gallery/" + str(gallery.id))
def get_picture_query(query_data):
query = db.session.query(PictureResource)
templates/create-gallery.html
@@ -0,0 +1,23 @@
{% extends "default.html" %}
{% block title %}Create gallery | gigadata{% endblock %}
{% block content %}
<x-frame style="--width: 768px">
<form method="POST" class="vbox">
<h1>Create gallery</h1>
<p>
You can add content later.
</p>
<label>
<span class="required-asterisk">Title</span>
<input type="text" name="title" required>
</label>
<label>
Description
<textarea name="description" rows="4"></textarea>
</label>
<button type="submit">Create</button>
</form>
</x-frame>
{% endblock %}
templates/gallery.html
@@ -0,0 +1,53 @@
{% extends "default.html" %}
{% block title %}Gallery {{ gallery.title }} | gigadata{% endblock %}
{% block content %}
<x-frame style="--width: 768px" class="vbox">
<h1>{{ gallery.title }}</h1>
<p>{{ gallery.description }}</p>
<form class="buttonbox" method="POST" action="/gallery/{{ gallery.id }}/add-picture">
<input name="picture_id" type="text" placeholder="Picture ID" required aria-label="Picture ID">
<button type="submit">Add picture</button>
</form>
<h2>Pictures</h2>
<ul class="thumbnail-list">
{% for picture in gallery.pictures %}
<li>
<a href="/picture/{{ picture.resource.id }}">
<div class="annotation-zone">
<img src="/raw/picture/{{ picture.resource.id }}" alt="{{ picture.resource.title }}">
{% for region in picture.resource.regions %}
{% if region.json.type == "bbox" %}
<svg class="shape-container" viewBox="0 0 {{ picture.resource.width }} {{ picture.resource.height }}">
<rect x="{{ region.json.shape.x * picture.resource.width }}"
y="{{ region.json.shape.y * picture.resource.height }}"
width="{{ region.json.shape.w * picture.resource.width }}"
height="{{ region.json.shape.h * picture.resource.height }}"
fill="none" class="shape-bbox shape"
></rect>
</svg>
{% elif region.json.type == "polygon" %}
<svg class="shape-container" viewBox="0 0 {{ picture.resource.width }} {{ picture.resource.height }}">
<polygon points="{% for point in region.json.shape %}{{ point.x * picture.resource.width }},{{ point.y * picture.resource.height }} {% endfor %}" fill="none" class="shape-polygon shape"></polygon>
</svg>
{% elif region.json.type == "polyline" %}
<svg class="shape-container" viewBox="0 0 {{ picture.resource.width }} {{ picture.resource.height }}">
<polyline points="{% for point in region.json.shape %}{{ point.x * picture.resource.width }},{{ point.y * picture.resource.height }} {% endfor %}" fill="none" class="shape-polyline shape"></polyline>
</svg>
{% elif region.json.type == "point" %}
<svg class="shape-container" viewBox="0 0 {{ picture.resource.width }} {{ picture.resource.height }}">
<circle cx="{{ region.json.shape.x * picture.resource.width }}" cy="{{ region.json.shape.y * picture.resource.height }}" r="0" fill="none" class="shape-point shape"></circle>
</svg>
{% endif %}
{% endfor %}
</div>
<div class="list-detail">
{{ picture.resource.title }}
</div>
</a>
</li>
{% endfor %}
</ul>
</x-frame>
{% endblock %}