Move most of the processing into the upload

This commit is contained in:
Michał 2023-03-05 16:22:11 +00:00
parent 970d3dcf66
commit 0a27d79a82
8 changed files with 107 additions and 100 deletions

View file

@ -6,11 +6,13 @@ from uuid import uuid4
import os
import io
import logging
from datetime import datetime as dt
from flask import (
Blueprint, current_app, send_from_directory, send_file, request, g, abort, flash, jsonify)
from werkzeug.utils import secure_filename
from colorthief import ColorThief
from PIL import Image, ImageOps # ImageFilter
from sqlalchemy.orm import sessionmaker
@ -103,12 +105,15 @@ def upload():
"""
form_file = request.files['file']
form = request.form
form_description = form['description']
form_alt = form['alt']
if not form_file:
return abort(404)
img_ext = os.path.splitext(form_file.filename)[-1].replace('.', '').lower()
img_name = f"GWAGWA_{str(uuid4())}.{img_ext}"
img_name = "GWAGWA_"+str(uuid4())
img_path = os.path.join(current_app.config['UPLOAD_FOLDER'], img_name+'.'+img_ext)
if not img_ext in current_app.config['ALLOWED_EXTENSIONS'].keys():
logging.info('File extension not allowed: %s', img_ext)
@ -117,22 +122,35 @@ def upload():
if os.path.isdir(current_app.config['UPLOAD_FOLDER']) is False:
os.mkdir(current_app.config['UPLOAD_FOLDER'])
# Save to database
try:
db_session.add(db.posts(img_name, form['description'], form['alt'], g.user.id))
db_session.commit()
except Exception as err:
logging.error('Could not save to database: %s', err)
abort(500)
# Save file
try:
form_file.save(
os.path.join(current_app.config['UPLOAD_FOLDER'], img_name))
form_file.save(img_path)
except Exception as err:
logging.error('Could not save file: %s', err)
abort(500)
# Get metadata and colors
img_exif = mt.Metadata(img_path).yoink()
img_colors = ColorThief(img_path).get_palette(color_count=3)
# Save to database
try:
query = db.Posts(author_id = g.user.id,
created_at = dt.now(),
file_name = img_name+'.'+img_ext,
file_type = img_ext,
image_exif = img_exif,
image_colours = img_colors,
post_description = form_description,
post_alt = form_alt)
db_session.add(query)
db_session.commit()
except Exception as err:
logging.error('Could not save to database: %s', err)
abort(500)
return 'Gwa Gwa'
@ -142,7 +160,7 @@ def remove(img_id):
"""
Deletes an image from the server and database
"""
img = db_session.query(db.posts).filter_by(id=img_id).first()
img = db_session.query(db.Posts).filter_by(id=img_id).first()
if img is None:
abort(404)
@ -159,7 +177,7 @@ def remove(img_id):
abort(500)
try:
db_session.query(db.posts).filter_by(id=img_id).delete()
db_session.query(db.Posts).filter_by(id=img_id).delete()
db_session.commit()
except Exception as err:
logging.error('Could not remove from database: %s', err)
@ -175,7 +193,7 @@ def metadata(img_id):
"""
Yoinks metadata from an image
"""
img = db_session.query(db.posts).filter_by(id=img_id).first()
img = db_session.query(db.Posts).filter_by(id=img_id).first()
if img is None:
abort(404)

View file

@ -5,6 +5,7 @@ User registration, login and logout and locking access to pages behind a login
import re
import uuid
import logging
from datetime import datetime as dt
import functools
from flask import Blueprint, flash, g, redirect, request, session, url_for, abort, jsonify
@ -49,14 +50,14 @@ def load_logged_in_user():
g.user = None
session.clear()
else:
is_alive = db_session.query(db.sessions).filter_by(session_uuid=user_uuid).first()
is_alive = db_session.query(db.Sessions).filter_by(session_uuid=user_uuid).first()
if is_alive is None:
logging.info('Session expired')
flash(['Session expired!', '3'])
session.clear()
else:
g.user = db_session.query(db.users).filter_by(id=user_id).first()
g.user = db_session.query(db.Users).filter_by(id=user_id).first()
@blueprint.route('/register', methods=['POST'])
@ -96,7 +97,11 @@ def register():
try:
db_session.add(db.users(username, email, generate_password_hash(password)))
register_user = db.Users(username=username,
email=email,
password=generate_password_hash(password),
created_at=dt.now())
db_session.add(register_user)
db_session.commit()
except exc.IntegrityError:
return f'User {username} is already registered!'
@ -116,7 +121,7 @@ def login():
username = request.form['username']
password = request.form['password']
user = db_session.query(db.users).filter_by(username=username).first()
user = db_session.query(db.Users).filter_by(username=username).first()
error = []
@ -138,11 +143,14 @@ def login():
session['user_id'] = user.id
session['uuid'] = str(uuid.uuid4())
db_session.add(db.sessions(user.id,
session.get('uuid'),
request.remote_addr,
request.user_agent.string,
1))
session_query = db.Sessions(user_id=user.id,
session_uuid=session.get('uuid'),
ip_address=request.remote_addr,
user_agent=request.user_agent.string,
active=True,
created_at=dt.now())
db_session.add(session_query)
db_session.commit()
except Exception as err:
logging.error('User %s could not be logged in: %s', username, err)

View file

@ -6,8 +6,8 @@ import os
from datetime import datetime
import platformdirs
from sqlalchemy import create_engine, Column, Integer, String, Boolean, DateTime, ForeignKey
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy import create_engine, Column, Integer, String, Boolean, DateTime, ForeignKey, PickleType
from sqlalchemy.orm import declarative_base, relationship, backref, mapped_column
path_to_db = os.path.join(platformdirs.user_config_dir('onlylegs'), 'gallery.sqlite')
@ -15,7 +15,7 @@ engine = create_engine(f'sqlite:///{path_to_db}', echo=False)
base = declarative_base()
class users (base): # pylint: disable=too-few-public-methods, C0103
class Users (base): # pylint: disable=too-few-public-methods, C0103
"""
User table
Joins with post, groups, session and log
@ -28,19 +28,13 @@ class users (base): # pylint: disable=too-few-public-methods, C0103
password = Column(String, nullable=False)
created_at = Column(DateTime, nullable=False)
posts = relationship('posts')
groups = relationship('groups')
session = relationship('sessions')
log = relationship('logs')
def __init__(self, username, email, password):
self.username = username
self.email = email
self.password = password
self.created_at = datetime.now()
posts = relationship('Posts', backref='users')
groups = relationship('Groups', backref='users')
session = relationship('Sessions', backref='users')
log = relationship('Logs', backref='users')
class posts (base): # pylint: disable=too-few-public-methods, C0103
class Posts (base): # pylint: disable=too-few-public-methods, C0103
"""
Post table
Joins with group_junction
@ -48,23 +42,22 @@ class posts (base): # pylint: disable=too-few-public-methods, C0103
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
file_name = Column(String, unique=True, nullable=False)
description = Column(String, nullable=False)
alt = Column(String, nullable=False)
author_id = Column(Integer, ForeignKey('users.id'))
created_at = Column(DateTime, nullable=False)
file_name = Column(String, unique=True, nullable=False)
file_type = Column(String, nullable=False)
image_exif = Column(PickleType, nullable=False)
image_colours = Column(PickleType, nullable=False)
post_description = Column(String, nullable=False)
post_alt = Column(String, nullable=False)
junction = relationship('group_junction')
def __init__(self, file_name, description, alt, author_id):
self.file_name = file_name
self.description = description
self.alt = alt
self.author_id = author_id
self.created_at = datetime.now()
junction = relationship('GroupJunction', backref='posts')
class groups (base): # pylint: disable=too-few-public-methods, C0103
class Groups (base): # pylint: disable=too-few-public-methods, C0103
"""
Group table
Joins with group_junction
@ -77,16 +70,10 @@ class groups (base): # pylint: disable=too-few-public-methods, C0103
author_id = Column(Integer, ForeignKey('users.id'))
created_at = Column(DateTime, nullable=False)
junction = relationship('group_junction')
def __init__(self, name, description, author_id):
self.name = name
self.description = description
self.author_id = author_id
self.created_at = datetime.now()
junction = relationship('GroupJunction', backref='groups')
class group_junction (base): # pylint: disable=too-few-public-methods, C0103
class GroupJunction (base): # pylint: disable=too-few-public-methods, C0103
"""
Junction table for posts and groups
Joins with posts and groups
@ -97,12 +84,8 @@ class group_junction (base): # pylint: disable=too-few-public-methods, C0103
group_id = Column(Integer, ForeignKey('groups.id'))
post_id = Column(Integer, ForeignKey('posts.id'))
def __init__(self, group_id, post_id):
self.group_id = group_id
self.post_id = post_id
class sessions (base): # pylint: disable=too-few-public-methods, C0103
class Sessions (base): # pylint: disable=too-few-public-methods, C0103
"""
Session table
Joins with user
@ -117,16 +100,8 @@ class sessions (base): # pylint: disable=too-few-public-methods, C0103
active = Column(Boolean, nullable=False)
created_at = Column(DateTime, nullable=False)
def __init__(self, user_id, session_uuid, ip_address, user_agent, active): # pylint: disable=too-many-arguments, C0103
self.user_id = user_id
self.session_uuid = session_uuid
self.ip_address = ip_address
self.user_agent = user_agent
self.active = active
self.created_at = datetime.now()
class logs (base): # pylint: disable=too-few-public-methods, C0103
class Logs (base): # pylint: disable=too-few-public-methods, C0103
"""
Log table
Joins with user
@ -140,15 +115,8 @@ class logs (base): # pylint: disable=too-few-public-methods, C0103
msg = Column(String, nullable=False)
created_at = Column(DateTime, nullable=False)
def __init__(self, user_id, ip_address, code, msg):
self.user_id = user_id
self.ip_address = ip_address
self.code = code
self.msg = msg
self.created_at = datetime.now()
class bans (base): # pylint: disable=too-few-public-methods, C0103
class Bans (base): # pylint: disable=too-few-public-methods, C0103
"""
Bans table
"""
@ -160,11 +128,5 @@ class bans (base): # pylint: disable=too-few-public-methods, C0103
msg = Column(String, nullable=False)
created_at = Column(DateTime, nullable=False)
def __init__(self, ip_address, code, msg):
self.ip_address = ip_address
self.code = code
self.msg = msg
self.created_at = datetime.now()
base.metadata.create_all(engine)

View file

@ -4,10 +4,9 @@ Parse metadata from images if available
otherwise get some basic information from the file
"""
import os
import logging
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
from PIL.ExifTags import TAGS
from .helpers import *
from .mapping import *
@ -72,6 +71,7 @@ class Metadata:
}
elif data in CAMERA_MAPPING:
if len(CAMERA_MAPPING[data]) == 2:
# Camera - Exif Tag name
exif['Camera'][CAMERA_MAPPING[data][0]] = {
'raw': encoded_exif[data],
'formatted':

View file

@ -41,8 +41,6 @@ CAMERA_MAPPING = {
'LightSource': ['Light Source', 'light_source'],
'SceneCaptureType': ['Scene Capture Type', 'scene_capture_type'],
'SceneType': ['Scene Type', 'scene_type'],
'Rating': ['Rating', 'rating'],
'RatingPercent': ['Rating Percent', 'rating_percent'],
}
SOFTWARE_MAPPING = {
'Software': ['Software'],
@ -59,4 +57,6 @@ FILE_MAPPING = {
'XResolution': ['X-resolution'],
'YResolution': ['Y-resolution'],
'ResolutionUnit': ['Resolution Units', 'resolution_unit'],
'Rating': ['Rating', 'rating'],
'RatingPercent': ['Rating Percent', 'rating_percent'],
}

View file

@ -22,8 +22,8 @@ def index():
"""
Home page of the website, shows the feed of latest images
"""
images = db_session.query(db.posts).order_by(db.posts.id.desc()).all()
images = db_session.query(db.Posts.file_name, db.Posts.id, db.Posts.created_at).order_by(db.Posts.id.desc()).all()
return render_template('index.html',
images=images,
image_count=len(images),
@ -35,15 +35,12 @@ def image(image_id):
"""
Image view, shows the image and its metadata
"""
img = db_session.query(db.posts).filter_by(id=image_id).first()
img = db_session.query(db.Posts).filter_by(id=image_id).first()
if img is None:
abort(404)
img_path = os.path.join(current_app.config['UPLOAD_FOLDER'], img.file_name)
exif = mt.Metadata(img_path).yoink()
return render_template('image.html', image=img, exif=exif)
return render_template('image.html', image=img, exif=img.image_exif)
@blueprint.route('/group')
def groups():

View file

@ -20,13 +20,15 @@ function uploadFile(){
addNotification("Please select a file to upload", 2);
} else {
// Make form
var formData = new FormData();
let formData = new FormData();
formData.append("file", $("#file").prop("files")[0]);
formData.append("alt", $("#alt").val());
formData.append("description", $("#description").val());
formData.append("tags", $("#tags").val());
formData.append("submit", $("#submit").val());
//let bar = $('.bar');
// Upload the information
$.ajax({
url: '/api/upload',
@ -34,9 +36,25 @@ function uploadFile(){
data: formData,
contentType: false,
processData: false,
beforeSend: function() {
//bar.width('0%');
var percentVal = 0;
console.log("Uploading...");
},
uploadProgress: function(event, position, total, percentComplete) {
//bar.width(percentComplete + '%');
percentVal = percentComplete;
console.log(percentVal);
},
complete: function(xhr) {
//bar.width('100%');
//bar.class += " loading";
console.log("Upload complete");
},
success: function (response) {
addNotification("File uploaded successfully!", 1);
// popupDissmiss(); // Close popup
console.log('File processed successfully');
},
error: function (response) {
switch (response.status) {
@ -57,6 +75,10 @@ function uploadFile(){
addNotification('Error uploading file, blame someone', 2);
break;
}
},
always: function (response) {
//bar.class += "";
console.log("Upload complete");
}
});

View file

@ -73,7 +73,7 @@
</div>
<div class="image-info__container">
{% if image['alt'] != '' %}
{% if image['post_alt'] %}
<div class="image-info">
<span class="image-info__collapse" id="collapse-info">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
@ -87,11 +87,11 @@
<h2>Alt</h2>
</div>
<div class="image-info__content">
<p>{{ image['alt'] }}</p>
<p>{{ image['post_alt'] }}</p>
</div>
</div>
{% endif %}
{% if image['description'] != '' %}
{% if image['post_description'] %}
<div class="image-info">
<span class="image-info__collapse" id="collapse-info">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
@ -105,7 +105,7 @@
<h2>Description</h2>
</div>
<div class="image-info__content">
<p>{{ image['description'] }}</p>
<p>{{ image['post_description'] }}</p>
</div>
</div>
{% endif %}