roundabout,
created on Thursday, 5 September 2024, 17:48:13 (1725558493),
received on Friday, 6 September 2024, 19:35:56 (1725651356)
Author identity: vlad <vlad.muntoiu@gmail.com>
fc56ad0762fabf2f2cefb0d80666b57de1b05568
app.py
@@ -1,3 +1,4 @@
import json
from datetime import datetime
from email.policy import default
@@ -63,18 +64,18 @@ with app.app_context():
id = db.Column(db.String(64), primary_key=True) # SPDX identifier
title = db.Column(db.UnicodeText, nullable=False) # the official name of the licence
description = db.Column(db.UnicodeText, nullable=False) # brief description of its permissions and restrictions
legal_text = db.Column(db.UnicodeText, nullable=False) # the full legal text of the licence
info_url = db.Column(db.String(1024), nullable=False) # the URL to a page with general information about the licence
url = db.Column(db.String(1024), nullable=True) # the URL to a page with the full text of the licence and more information
pictures = db.relationship("PictureLicence", back_populates="licence")
free = db.Column(db.Boolean, nullable=False, default=False) # whether the licence is free or not
logo_url = db.Column(db.String(1024), nullable=True) # URL to the logo of the licence
pinned = db.Column(db.Boolean, nullable=False, default=False) # whether the licence should be shown at the top of the list
def __init__(self, id, title, description, legal_text, url, free, logo_url=None, pinned=False):
def __init__(self, id, title, description, info_url, url, free, logo_url=None, pinned=False):
self.id = id
self.title = title
self.description = description
self.legal_text = legal_text
self.info_url = info_url
self.url = url
self.free = free
self.logo_url = logo_url
@@ -209,6 +210,22 @@ with app.app_context():
self.replaces = replaces
replaces.replaced_by = self
def put_annotations(self, json):
# Delete all previous annotations
db.session.query(PictureRegion).filter_by(resource_id=self.id).delete()
for region in json:
object_id = region["object"]
picture_object = db.session.get(PictureObject, object_id)
region_data = {
"type": region["type"],
"shape": region["shape"],
}
region_row = PictureRegion(region_data, self, picture_object)
db.session.add(region_row)
@app.route("/")
def index():
@@ -358,6 +375,13 @@ def upload_post():
db.session.commit()
file.save(path.join(config.DATA_PATH, "pictures", str(resource.id)))
if flask.request.form.get("annotations"):
try:
resource.put_annotations(json.loads(flask.request.form.get("annotations")))
db.session.commit()
except json.JSONDecodeError:
flask.flash("Invalid annotations")
flask.flash("Picture uploaded successfully")
return flask.redirect("/picture/" + str(resource.id))
@@ -380,45 +404,67 @@ def picture(id):
@app.route("/picture/<int:id>/annotate")
def annotate_picture(id):
resource = db.session.get(PictureResource, id)
if resource is None:
flask.abort(404)
current_user = db.session.get(User, flask.session.get("username"))
if current_user is None:
flask.abort(401)
if resource.author != current_user and not current_user.admin:
flask.abort(403)
return flask.render_template("picture-annotation.html", resource=resource,
file_extension=mimetypes.guess_extension(resource.file_format))
@app.route("/picture/<int:id>/put-annotations-form")
def put_annotations_form(id):
resource = db.session.get(PictureResource, id)
if resource is None:
flask.abort(404)
return flask.render_template("picture-annotation.html", resource=resource,
file_extension=mimetypes.guess_extension(resource.file_format))
current_user = db.session.get(User, flask.session.get("username"))
if current_user is None:
flask.abort(401)
if resource.author != current_user and not current_user.admin:
flask.abort(403)
@app.route("/picture/<int:id>/save-annotations", methods=["POST"])
def save_annotations(id):
return flask.render_template("put-annotations-form.html", resource=resource)
@app.route("/picture/<int:id>/put-annotations-form", methods=["POST"])
def put_annotations_form_post(id):
resource = db.session.get(PictureResource, id)
if resource is None:
flask.abort(404)
current_user = db.session.get(User, flask.session.get("username"))
if current_user is None:
flask.abort(401)
if resource.author != current_user and not current_user.admin:
flask.abort(403)
# Delete all previous annotations
db.session.query(PictureRegion).filter_by(resource_id=id).delete()
resource.put_annotations(json.loads(flask.request.form["annotations"]))
db.session.commit()
return flask.redirect("/picture/" + str(resource.id))
json = flask.request.json
for region in json:
object_id = region["object"]
picture_object = db.session.get(PictureObject, object_id)
region_data = {
"type": region["type"],
"shape": region["shape"],
}
@app.route("/picture/<int:id>/save-annotations", methods=["POST"])
def save_annotations(id):
resource = db.session.get(PictureResource, id)
if resource is None:
flask.abort(404)
region_row = PictureRegion(region_data, resource, picture_object)
db.session.add(region_row)
current_user = db.session.get(User, flask.session.get("username"))
if resource.author != current_user and not current_user.admin:
flask.abort(403)
resource.put_annotations(flask.request.json)
db.session.commit()
templates/picture.html
@@ -17,7 +17,8 @@
<a href="{{ resource.origin_url }}">Original source</a> |
<a href="/raw/picture/{{ resource.id }}">View</a> |
<a href="/raw/picture/{{ resource.id }}" download="GigadataPicture_{{ resource.id }}{{ file_extension }}">Download</a> |
<a href="/picture/{{ resource.id }}/annotate">Annotate</a>
<a href="/picture/{{ resource.id }}/annotate">Annotate</a> |
<a href="/picture/{{ resource.id }}/put-annotations-form">Submit JSON annotations</a>
</p>
<div id="annotation-zone">
<img id="annotation-image" src="/raw/picture/{{ resource.id }}" alt="{{ resource.title }}">
@@ -87,11 +88,11 @@
<span>{{ resource.regions | selectattr("object_id") | list | length }}</span>
</div>
Contains objects: {{ contains | join(", ") }}
<x-hbox>
<x-hbox style="justify-content: space-between">
<small class="picture-licensing-info">
Available under:
{% for licence in licences %}
<a href="{{ licence.url }}" target="_blank">
<a href="{{ licence.info_url }}" target="_blank">
{{ licence.title }}
</a>
{% if not loop.last %}, {% endif %}
@@ -100,10 +101,14 @@
<x-vbox class="picture-licence-logos">
{% for licence in licences %}
{% if licence.logo_url %}
<a href="{{ licence.url }}" target="_blank" tabindex="-1">
{# An equivalent link already exists, only one is focusable #}
{% if licence.info_url %}
<a href="{{ licence.info_url }}" target="_blank" tabindex="-1">
{# An equivalent link already exists, only one is focusable #}
<img src="{{ licence.logo_url }}" alt="{{ licence.title }}" class="licence-logo">
</a>
{% else %}
<img src="{{ licence.logo_url }}" alt="{{ licence.title }}" class="licence-logo">
</a>
{% endif %}
{% endif %}
{% endfor %}
</x-vbox>
templates/put-annotations-form.html
@@ -0,0 +1,18 @@
{% extends "default.html" %}
{% block title %}Submit JSON annotations | gigadata{% endblock %}
{% block content %}
<x-frame style="--width: 768px">
<form method="POST" class="vbox">
<h1>Submit JSON annotations: {{ resource.title }}</h1>
<p>
Submit JSON annotations for the picture <a href="/picture/{{ resource.id }}">{{ resource.title }}</a>.
Check the documentation for the expected format.
</p>
<label>
<span class="required-asterisk">JSON annotations</span>
<textarea name="annotations" required rows="30"></textarea>
</label>
<button type="submit">Submit</button>
</form>
</x-frame>
{% endblock %}
templates/upload.html
@@ -63,8 +63,8 @@
</label>
<div class="licence-selection-info">
{% if licence.logo_url %}
{% if licence.url %}
<a href="{{ licence.url }}" target="_blank" tabindex="-1"> {# An equivalent link already exists, only one is focusable #}
{% if licence.info_url %}
<a href="{{ licence.info_url }}" target="_blank" tabindex="-1"> {# An equivalent link already exists, only one is focusable #}
<img src="{{ licence.logo_url }}" alt="{{ licence.title }}" class="licence-logo">
</a>
{% else %}
@@ -92,6 +92,12 @@
<span class="required-asterisk">File</span>
<input type="file" name="file" required>
</label>
<label>
<span>Prefill annotations</span>
<textarea name="annotations"></textarea>
You may add JSON annotations here. The object IDs must exist on the server. For
more information about the format, check the documentation.
</label>
<button type="submit">Upload</button>
</form>
</x-frame>