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 jsonfrom 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 licenceinfo_url = db.Column(db.String(1024), nullable=False) # the URL to a page with general information about the licenceurl = 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_textself.info_url = info_urlself.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 annotationsdb.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.jsonfor 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>