diff --git a/gallery/api.py b/gallery/api.py index 04533d9..0852064 100644 --- a/gallery/api.py +++ b/gallery/api.py @@ -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) diff --git a/gallery/auth.py b/gallery/auth.py index 3db96bf..c0ba7be 100644 --- a/gallery/auth.py +++ b/gallery/auth.py @@ -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) diff --git a/gallery/db.py b/gallery/db.py index 31a43c6..ae336ca 100644 --- a/gallery/db.py +++ b/gallery/db.py @@ -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) diff --git a/gallery/metadata/__init__.py b/gallery/metadata/__init__.py index 8bb4e6e..114cbaa 100644 --- a/gallery/metadata/__init__.py +++ b/gallery/metadata/__init__.py @@ -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': diff --git a/gallery/metadata/mapping.py b/gallery/metadata/mapping.py index d99c1df..625dfac 100644 --- a/gallery/metadata/mapping.py +++ b/gallery/metadata/mapping.py @@ -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'], } diff --git a/gallery/routing.py b/gallery/routing.py index 9389bcf..eeec90a 100644 --- a/gallery/routing.py +++ b/gallery/routing.py @@ -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(): diff --git a/gallery/static/js/upload.js b/gallery/static/js/upload.js index c1b9b50..6da0b91 100644 --- a/gallery/static/js/upload.js +++ b/gallery/static/js/upload.js @@ -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"); } }); diff --git a/gallery/templates/image.html b/gallery/templates/image.html index ddee033..6507482 100644 --- a/gallery/templates/image.html +++ b/gallery/templates/image.html @@ -73,7 +73,7 @@
- {% if image['alt'] != '' %} + {% if image['post_alt'] %}
@@ -87,11 +87,11 @@

Alt

-

{{ image['alt'] }}

+

{{ image['post_alt'] }}

{% endif %} - {% if image['description'] != '' %} + {% if image['post_description'] %}
@@ -105,7 +105,7 @@

Description

-

{{ image['description'] }}

+

{{ image['post_description'] }}

{% endif %}