roundabout,
created on Saturday, 14 September 2024, 08:22:36 (1726302156),
received on Saturday, 14 September 2024, 09:36:45 (1726306605)
Author identity: vlad <vlad.muntoiu@gmail.com>
c692e16ebe9602c4a11fec8ba9f74abc4a1aceff
app.py
@@ -77,6 +77,7 @@ with app.app_context():
joined_timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) galleries = db.relationship("Gallery", back_populates="owner") galleries_joined = db.relationship("UserInGallery", back_populates="user") ratings = db.relationship("PictureRating", back_populates="user")def __init__(self, username, password): self.username = username
@@ -235,6 +236,7 @@ with app.app_context():
licences = db.relationship("PictureLicence", back_populates="resource") galleries = db.relationship("PictureInGallery", back_populates="resource") ratings = db.relationship("PictureRating", back_populates="resource")def __init__(self, title, author, description, origin_url, licence_ids, mime, nature=None):
@@ -267,6 +269,17 @@ with app.app_context():
region_row = PictureRegion(region_data, self, picture_object) db.session.add(region_row) @property def average_rating(self): if not self.ratings: return None return db.session.query(db.func.avg(PictureRating.rating)).filter_by(resource=self).scalar() @property def rating_totals(self): all_ratings = db.session.query(PictureRating.rating).filter_by(resource=self) return {rating: all_ratings.filter_by(rating=rating).count() for rating in range(1, 6)} class PictureInGallery(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True)
@@ -310,6 +323,22 @@ with app.app_context():
self.owner = owner class PictureRating(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) username = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) rating = db.Column(db.Integer, db.CheckConstraint("rating >= 1 AND rating <= 5"), nullable=False) resource = db.relationship("PictureResource", back_populates="ratings") user = db.relationship("User", back_populates="ratings") def __init__(self, resource, user, rating): self.resource = resource self.user = user self.rating = rating @app.route("/") def index(): return flask.render_template("home.html", resources=PictureResource.query.order_by(
@@ -509,10 +538,14 @@ def picture(id):
current_user = db.session.get(User, flask.session.get("username")) have_permission = current_user and (current_user == resource.author or current_user.admin) own_rating = None if current_user: own_rating = PictureRating.query.filter_by(resource=resource, user=current_user).first() return flask.render_template("picture.html", resource=resource, file_extension=mimetypes.guess_extension(resource.file_format), size=image.size, copies=resource.copies, have_permission=have_permission)have_permission=have_permission, own_rating=own_rating)@app.route("/picture/<int:id>/annotate")
@@ -702,6 +735,42 @@ def edit_picture(id):
PictureLicence=PictureLicence) @app.route("/picture/<int:id>/rate", methods=["POST"]) def rate_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) rating = int(flask.request.form.get("rating")) if not rating: # Delete the existing rating if PictureRating.query.filter_by(resource=resource, user=current_user).first(): db.session.delete(PictureRating.query.filter_by(resource=resource, user=current_user).first()) db.session.commit() return flask.redirect("/picture/" + str(resource.id)) if not 1 <= rating <= 5: flask.flash("Invalid rating") return flask.redirect("/picture/" + str(resource.id)) if PictureRating.query.filter_by(resource=resource, user=current_user).first(): PictureRating.query.filter_by(resource=resource, user=current_user).first().rating = rating else: # Create a new rating db.session.add(PictureRating(resource, current_user, rating)) db.session.commit() return flask.redirect("/picture/" + str(resource.id)) @app.route("/picture/<int:id>/edit-metadata", methods=["POST"]) def edit_picture_post(id): resource = db.session.get(PictureResource, id)
static/style.css
@@ -6,6 +6,8 @@
--text-soft: #000000C0; --text-softer: #0000009A; --text-faint: #00000066; --text-softest: #00000033; --color-star: #FFC107;--color-shape-label: #0097A7; --color-shape-label-text: #ffffff; --shadow-card-inset: inset 0 3px 6px -4px rgba(0, 0, 0, 0.12), inset 0 3px 6px 0 rgba(0, 0, 0, 0.24), inset 0 1px 4px 0 rgba(0, 0, 0, 0.12); /*view-transition-name: root;*/
@@ -415,3 +417,35 @@ dd {
text-decoration: none; color: var(--color-card-text); } .star-rating-container { display: flex; flex-direction: row-reverse; gap: 0.5ch; font-size: 1.25em; align-items: center; justify-content: flex-end; /* In this case, it means LEFT, not right, because of the row-reverse */ } .star-rating-container > input { width: 0 !important; height: 0 !important; visibility: hidden; } .star-rating-container > label { cursor: pointer; color: var(--text-softest); transition: color 0.25s cubic-bezier(0.37, 0, 0.63, 1), filter 0.25s cubic-bezier(0.37, 0, 0.63, 1); } .star-rating-container > label:hover, .star-rating-container > label:hover ~ label { color: var(--color-star); filter: drop-shadow(0 0 3px #00000040); } .star-rating-container > input:checked ~ label { color: var(--color-star); }
templates/picture.html
@@ -91,6 +91,30 @@
{% set licences = resource.licences | map(attribute="licence") | list %} {% set contains = resource.regions | map(attribute="object_id") | set | select | sort | list %} <x-vbox> <p> {{ resource.rating_totals }} ratings, average {{ resource.average_rating }} </p> {% if current_user %} <form id="rating-form" method="POST" action="/picture/{{ resource.id }}/rate"> <label> <input name="rating" type="radio" value="0" {% if not own_rating.rating %}checked{% endif %}> Clear rating </label> <div class="star-rating-container"> <input type="radio" id="stars-5" name="rating" value="5" title="Perfect" {% if own_rating.rating == 5 %}checked{% endif %}> <label for="stars-5" tabindex="0"><iconify-icon icon="mdi:star" class="star">5 stars</iconify-icon></label> <input type="radio" id="stars-4" name="rating" value="4" title="Good" {% if own_rating.rating == 4 %}checked{% endif %}> <label for="stars-4" tabindex="0"><iconify-icon icon="mdi:star" class="star">4 stars</iconify-icon></label> <input type="radio" id="stars-3" name="rating" value="3" title="OK" {% if own_rating.rating == 3 %}checked{% endif %}> <label for="stars-3" tabindex="0"><iconify-icon icon="mdi:star" class="star">3 stars</iconify-icon></label> <input type="radio" id="stars-2" name="rating" value="2" title="Poor" {% if own_rating.rating == 2 %}checked{% endif %}> <label for="stars-2" tabindex="0"><iconify-icon icon="mdi:star" class="star">2 stars</iconify-icon></label> <input type="radio" id="stars-1" name="rating" value="1" title="Awful" {% if own_rating.rating == 1 %}checked{% endif %}> <label for="stars-1" tabindex="0"><iconify-icon icon="mdi:star" class="star">1 star</iconify-icon></label> </div> <button type="submit">Rate</button> </form> {% endif %}<div class="icon-explainer"> <span>Type</span> <span>{{ resource.nature.id }}</span>
@@ -175,6 +199,7 @@
</div> </li> {% endfor %} </ul></x-vbox> </x-frame> {% endblock %}