Skip to content
Snippets Groups Projects
Commit fd07018b authored by Carl Schönfelder's avatar Carl Schönfelder
Browse files

Resolve "upload images"

parent 4f80ddd6
No related branches found
No related tags found
1 merge request!60Resolve "upload images"
Pipeline #40111 passed
...@@ -6,4 +6,5 @@ __pycache__ ...@@ -6,4 +6,5 @@ __pycache__
htmlcov htmlcov
.pytest_cache .pytest_cache
/.idea /.idea
.vs/ .vs/
\ No newline at end of file */static
\ No newline at end of file
from flask import Flask, redirect, request from flask import Flask, redirect, request
from flask_uploads import IMAGES, UploadSet, configure_uploads
import app.database.models as models import app.database.models as models
from app.core import bcrypt, db, jwt, ma from app.core import bcrypt, db, jwt, ma
from app.core.dto import MediaDTO
def create_app(config_name="configmodule.DevelopmentConfig"): def create_app(config_name="configmodule.DevelopmentConfig"):
...@@ -14,6 +16,7 @@ def create_app(config_name="configmodule.DevelopmentConfig"): ...@@ -14,6 +16,7 @@ def create_app(config_name="configmodule.DevelopmentConfig"):
jwt.init_app(app) jwt.init_app(app)
db.init_app(app) db.init_app(app)
ma.init_app(app) ma.init_app(app)
configure_uploads(app, (MediaDTO.image_set,))
from app.apis import flask_api from app.apis import flask_api
......
...@@ -44,6 +44,7 @@ from flask_restx import Api ...@@ -44,6 +44,7 @@ from flask_restx import Api
from .auth import api as auth_ns from .auth import api as auth_ns
from .competitions import api as comp_ns from .competitions import api as comp_ns
from .media import api as media_ns
from .misc import api as misc_ns from .misc import api as misc_ns
from .questions import api as question_ns from .questions import api as question_ns
from .slides import api as slide_ns from .slides import api as slide_ns
...@@ -51,6 +52,7 @@ from .teams import api as team_ns ...@@ -51,6 +52,7 @@ from .teams import api as team_ns
from .users import api as user_ns from .users import api as user_ns
flask_api = Api() flask_api = Api()
flask_api.add_namespace(media_ns, path="/api/media")
flask_api.add_namespace(misc_ns, path="/api/misc") flask_api.add_namespace(misc_ns, path="/api/misc")
flask_api.add_namespace(user_ns, path="/api/users") flask_api.add_namespace(user_ns, path="/api/users")
flask_api.add_namespace(auth_ns, path="/api/auth") flask_api.add_namespace(auth_ns, path="/api/auth")
...@@ -58,4 +60,4 @@ flask_api.add_namespace(comp_ns, path="/api/competitions") ...@@ -58,4 +60,4 @@ flask_api.add_namespace(comp_ns, path="/api/competitions")
flask_api.add_namespace(slide_ns, path="/api/competitions/<CID>/slides") flask_api.add_namespace(slide_ns, path="/api/competitions/<CID>/slides")
flask_api.add_namespace(team_ns, path="/api/competitions/<CID>/teams") flask_api.add_namespace(team_ns, path="/api/competitions/<CID>/teams")
flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/questions") flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/questions")
#flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/slides/<SID>/question") # flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/slides/<SID>/question")
###
# Admin stuff placed here for later use
# No need to implement this before the application is somewhat done
###
from flask import Blueprint
admin_blueprint = Blueprint("admin", __name__)
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import MediaDTO
from app.core.parsers import media_parser_search
from app.database.models import City, Media, MediaType, QuestionType, Role
from flask import request
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restx import Resource, reqparse
from flask_uploads import UploadNotAllowed
from PIL import Image
api = MediaDTO.api
image_set = MediaDTO.image_set
schema = MediaDTO.schema
list_schema = MediaDTO.list_schema
def generate_thumbnail(filename):
with Image.open(f"./static/images/{filename}") as im:
im.thumbnail((120, 120))
im.save(f"./static/images/thumbnail_{filename}")
@api.route("/images")
class ImageList(Resource):
@jwt_required
def get(self):
args = media_parser_search.parse_args(strict=True)
items, total = dbc.search.image(**args)
return list_response(list_schema.dump(items), total)
@jwt_required
def post(self):
if "image" not in request.files:
api.abort(codes.BAD_REQUEST, "Missing image in request.files")
try:
filename = image_set.save(request.files["image"])
generate_thumbnail(filename)
print(filename)
item = dbc.add.image(filename, get_jwt_identity())
return item_response(schema.dump(item))
except UploadNotAllowed:
api.abort(codes.BAD_REQUEST, "Could not save the image")
except:
api.abort(codes.INTERNAL_SERVER_ERROR, "Something went wrong when trying to save image")
...@@ -2,6 +2,14 @@ import app.core.rich_schemas as rich_schemas ...@@ -2,6 +2,14 @@ import app.core.rich_schemas as rich_schemas
import app.core.schemas as schemas import app.core.schemas as schemas
import marshmallow as ma import marshmallow as ma
from flask_restx import Namespace, fields from flask_restx import Namespace, fields
from flask_uploads import IMAGES, UploadSet
class MediaDTO:
api = Namespace("media")
image_set = UploadSet("photos", IMAGES)
schema = schemas.MediaSchema(many=False)
list_schema = schemas.MediaSchema(many=True)
class AuthDTO: class AuthDTO:
......
...@@ -64,3 +64,7 @@ question_parser.add_argument("type_id", type=int, default=None, location="json") ...@@ -64,3 +64,7 @@ question_parser.add_argument("type_id", type=int, default=None, location="json")
###TEAM#### ###TEAM####
team_parser = reqparse.RequestParser() team_parser = reqparse.RequestParser()
team_parser.add_argument("name", type=str, location="json") team_parser.add_argument("name", type=str, location="json")
###SEARCH_COMPETITION####
media_parser_search = search_parser.copy()
media_parser_search.add_argument("filename", type=str, default=None, location="args")
...@@ -4,6 +4,7 @@ from app.database.models import ( ...@@ -4,6 +4,7 @@ from app.database.models import (
Blacklist, Blacklist,
City, City,
Competition, Competition,
Media,
MediaType, MediaType,
Question, Question,
QuestionType, QuestionType,
...@@ -35,6 +36,11 @@ def blacklist(jti): ...@@ -35,6 +36,11 @@ def blacklist(jti):
return Blacklist(jti) return Blacklist(jti)
@db_add
def image(filename, user_id):
return Media(filename, 1, user_id)
@db_add @db_add
def slide(item_competition): def slide(item_competition):
order = Slide.query.filter(Slide.competition_id == item_competition.id).count() # first element has index 0 order = Slide.query.filter(Slide.competition_id == item_competition.id).count() # first element has index 0
......
from app.database.models import Competition, Question, Slide, Team, User from app.database.models import Competition, Media, Question, Slide, Team, User
def image(filename, page=0, page_size=15, order=1, order_by=None):
query = Media.query.filter(Media.type_id == 1)
if filename:
query = query.filter(Media.filename.like(f"%{filename}%"))
return query.pagination(page, page_size, None, None)
def user(email=None, name=None, city_id=None, role_id=None, page=0, page_size=15, order=1, order_by=None): def user(email=None, name=None, city_id=None, role_id=None, page=0, page_size=15, order=1, order_by=None):
...@@ -74,10 +82,7 @@ def questions( ...@@ -74,10 +82,7 @@ def questions(
if slide_id: if slide_id:
query = query.filter(Question.slide_id == slide_id) query = query.filter(Question.slide_id == slide_id)
if competition_id: if competition_id:
slide_ids = set( query = query.join(Slide, (Slide.competition_id == competition_id) & (Slide.id == Question.slide_id))
[x.id for x in Slide.query.filter(Slide.competition_id == competition_id).all()]
) # TODO: Filter using database instead of creating a set of slide_ids
query = query.filter(Question.slide_id.in_(slide_ids))
order_column = Question.id # Default order_by order_column = Question.id # Default order_by
if order_by: if order_by:
......
from app.core import bcrypt, db from app.core import bcrypt, db
from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
from sqlalchemy.orm import backref
STRING_SIZE = 254 STRING_SIZE = 254
...@@ -90,10 +91,13 @@ class Competition(db.Model): ...@@ -90,10 +91,13 @@ class Competition(db.Model):
year = db.Column(db.Integer, nullable=False, default=2020) year = db.Column(db.Integer, nullable=False, default=2020)
city_id = db.Column(db.Integer, db.ForeignKey("city.id"), nullable=False) city_id = db.Column(db.Integer, db.ForeignKey("city.id"), nullable=False)
background_image_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=True)
slides = db.relationship("Slide", backref="competition") slides = db.relationship("Slide", backref="competition")
teams = db.relationship("Team", backref="competition") teams = db.relationship("Team", backref="competition")
background_image = db.relationship("Media", uselist=False)
def __init__(self, name, year, city_id): def __init__(self, name, year, city_id):
self.name = name self.name = name
self.year = year self.year = year
...@@ -123,6 +127,9 @@ class Slide(db.Model): ...@@ -123,6 +127,9 @@ class Slide(db.Model):
settings = db.Column(db.Text, nullable=False, default="{}") settings = db.Column(db.Text, nullable=False, default="{}")
competition_id = db.Column(db.Integer, db.ForeignKey("competition.id"), nullable=False) competition_id = db.Column(db.Integer, db.ForeignKey("competition.id"), nullable=False)
background_image_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=True)
background_image = db.relationship("Media", uselist=False)
def __init__(self, order, competition_id): def __init__(self, order, competition_id):
self.order = order self.order = order
self.competition_id = competition_id self.competition_id = competition_id
......
import os
from datetime import timedelta from datetime import timedelta
class Config: class Config:
DEBUG = False DEBUG = False
TESTING = False TESTING = False
BUNDLE_ERRORS = True
SQLALCHEMY_DATABASE_URI = "sqlite:///database.db" SQLALCHEMY_DATABASE_URI = "sqlite:///database.db"
SQLALCHEMY_TRACK_MODIFICATIONS = False
JWT_SECRET_KEY = "super-secret" JWT_SECRET_KEY = "super-secret"
JWT_BLACKLIST_ENABLED = True JWT_BLACKLIST_ENABLED = True
JWT_BLACKLIST_TOKEN_CHECKS = ["access", "refresh"] JWT_BLACKLIST_TOKEN_CHECKS = ["access", "refresh"]
BUNDLE_ERRORS = True
JWT_ACCESS_TOKEN_EXPIRES = timedelta(days=2) JWT_ACCESS_TOKEN_EXPIRES = timedelta(days=2)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30) JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
SQLALCHEMY_TRACK_MODIFICATIONS = False UPLOADED_PHOTOS_DEST = "static/images" # os.getcwd()
SECRET_KEY = os.urandom(24)
class DevelopmentConfig(Config): class DevelopmentConfig(Config):
DEBUG = True DEBUG = True
SQLALCHEMY_DATABASE_URI = "sqlite:///database.db" SQLALCHEMY_DATABASE_URI = "sqlite:///database.db"
SQLALCHEMY_ECHO = True
class TestingConfig(Config): class TestingConfig(Config):
......
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment