Fix errors in metadata parsing

Remove useless extra checks in metadata parser
Add Flask-Caching, need to test how helpfull this is
This commit is contained in:
Michał 2023-03-08 09:01:20 +00:00
parent 91278e2d11
commit 99c1d81869
27 changed files with 504 additions and 125 deletions

View file

@ -15,6 +15,7 @@ import logging
# Flask
from flask_compress import Compress
from flask_caching import Cache
from flask import Flask, render_template
# Configuration
@ -73,6 +74,7 @@ def create_app(test_config=None):
"""
app = Flask(__name__,instance_path=INSTANCE_PATH)
compress = Compress()
cache = Cache(config={'CACHE_TYPE': 'SimpleCache', 'CACHE_DEFAULT_TIMEOUT': 69})
# App configuration
app.config.from_mapping(
@ -146,4 +148,5 @@ def create_app(test_config=None):
app.register_blueprint(api.blueprint)
compress.init_app(app)
cache.init_app(app)
return app

View file

@ -13,7 +13,7 @@ from flask import (
from werkzeug.utils import secure_filename
from colorthief import ColorThief
from PIL import Image, ImageOps # ImageFilter
from PIL import Image, ImageOps, ImageFilter
from sqlalchemy.orm import sessionmaker
from gallery.auth import login_required
@ -33,11 +33,13 @@ def uploads(file):
Returns a file from the uploads folder
w and h are the width and height of the image for resizing
f is whether to apply filters to the image, such as blurring NSFW images
b is whether to force blur the image, even if it's not NSFW
"""
# Get args
width = request.args.get('w', default=0, type=int) # Width of image
height = request.args.get('h', default=0, type=int) # Height of image
filtered = request.args.get('f', default=False, type=bool) # Whether to apply filters
filtered = request.args.get('f', default=False, type=bool) # Whether to apply filters
blur = request.args.get('b', default=False, type=bool) # Whether to force blur
# if no args are passed, return the raw file
if width == 0 and height == 0 and not filtered:
@ -78,6 +80,10 @@ def uploads(file):
if filtered:
#img = img.filter(ImageFilter.GaussianBlur(20))
pass
# If forced to blur, blur image
if blur:
img = img.filter(ImageFilter.GaussianBlur(20))
try:
img.save(buff, img_ext, icc_profile=img_icc)

View file

@ -69,6 +69,7 @@ class Metadata:
exif['Photographer'][PHOTOGRAHER_MAPPING[data][0]] = {
'raw': encoded_exif[data],
}
continue
elif data in CAMERA_MAPPING:
if len(CAMERA_MAPPING[data]) == 2:
# Camera - Exif Tag name
@ -81,6 +82,7 @@ class Metadata:
exif['Camera'][CAMERA_MAPPING[data][0]] = {
'raw': encoded_exif[data],
}
continue
elif data in SOFTWARE_MAPPING:
if len(SOFTWARE_MAPPING[data]) == 2:
exif['Software'][SOFTWARE_MAPPING[data][0]] = {
@ -92,6 +94,7 @@ class Metadata:
exif['Software'][SOFTWARE_MAPPING[data][0]] = {
'raw': encoded_exif[data],
}
continue
elif data in FILE_MAPPING:
if len(FILE_MAPPING[data]) == 2:
exif['File'][FILE_MAPPING[data][0]] = {

View file

@ -352,6 +352,7 @@ def orientation(value):
Maps the value of the orientation to a human readable format
"""
value_map = {
0: 'Undefined',
1: 'Horizontal (normal)',
2: 'Mirror horizontal',
3: 'Rotate 180',

View file

@ -31,7 +31,7 @@ CAMERA_MAPPING = {
'ISOSpeedRatings': ['ISO Speed Ratings', 'iso'],
'ISOSpeed': ['ISO Speed', 'iso'],
'SensitivityType': ['Sensitivity Type', 'sensitivity_type'],
'ExposureBiasValue': ['Exposure Bias', 'ev'],
'ExposureBiasValue': ['Exposure Bias', 'exposure'],
'ExposureTime': ['Exposure Time', 'shutter'],
'ExposureMode': ['Exposure Mode', 'exposure_mode'],
'ExposureProgram': ['Exposure Program', 'exposure_program'],

View file

@ -2,8 +2,9 @@
Onlylegs Gallery - Routing
"""
import os
from datetime import datetime as dt
from flask import Blueprint, render_template, current_app
from flask import Blueprint, render_template, current_app, request, g
from werkzeug.exceptions import abort
from sqlalchemy.orm import sessionmaker
@ -23,9 +24,10 @@ def index():
Home page of the website, shows the feed of latest images
"""
images = db_session.query(db.Posts.file_name,
db.Posts.id,
db.Posts.created_at
).order_by(db.Posts.id.desc()).all()
db.Posts.image_colours,
db.Posts.author_id,
db.Posts.created_at,
db.Posts.id).order_by(db.Posts.id.desc()).all()
return render_template('index.html',
images=images,
@ -47,12 +49,25 @@ def image(image_id):
return render_template('image.html', image=img, exif=img.image_exif)
@blueprint.route('/group')
@blueprint.route('/group', methods=['GET', 'POST'])
def groups():
"""
Group overview, shows all image groups
"""
return render_template('group.html', group_id='gwa gwa')
if request.method == 'GET':
groups = db_session.query(db.Groups.name, db.Groups.author_id).all()
return render_template('group.html', groups=groups)
elif request.method == 'POST':
group_name = request.form['name']
group_description = request.form['description']
group_author = g.user.id
new_group = db.Groups(name=group_name, description=group_description, author_id=group_author, created_at=dt.now())
db_session.add(new_group)
return ':3'
@blueprint.route('/group/<int:group_id>')
def group(group_id):
@ -73,4 +88,4 @@ def profile_id(user_id):
"""
Shows user ofa given id, displays their uploads and other info
"""
return render_template('profile.html', user_id=user_id)
return render_template('profile.html', user_id=user_id)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

View file

@ -1,20 +1,9 @@
function showUpload() {
popUpShow(
'Upload funny stuff',
'May the world see your stuff 👀',
'<button class="pop-up__btn pop-up__btn-primary-fill" form="uploadForm" type"submit">Upload</button>',
'<form id="uploadForm" onsubmit="return uploadFile(event)">\
<input class="pop-up__input" type="file" id="file"/>\
<input class="pop-up__input" type="text" placeholder="alt" id="alt"/>\
<input class="pop-up__input" type="text" placeholder="description" id="description"/>\
<input class="pop-up__input" type="text" placeholder="tags" id="tags"/>\
</form>'
);
};
function uploadFile(){
// AJAX takes control of subby form
event.preventDefault();
const jobList = document.querySelector(".upload-jobs");
// Check for empty upload
if ($("#file").val() === "") {
addNotification("Please select a file to upload", 2);
@ -35,32 +24,58 @@ function uploadFile(){
contentType: false,
processData: false,
beforeSend: function() {
console.log("Uploading...");
jobContainer = document.createElement("div");
jobContainer.classList.add("job");
jobStatus = document.createElement("span");
jobStatus.classList.add("job__status");
jobStatus.innerHTML = "Uploading...";
jobProgress = document.createElement("span");
jobProgress.classList.add("progress");
jobImg = document.createElement("img");
jobImg.src = URL.createObjectURL($("#file").prop("files")[0]);
jobImgFilter = document.createElement("span");
jobImgFilter.classList.add("img-filter");
jobContainer.appendChild(jobStatus);
jobContainer.appendChild(jobProgress);
jobContainer.appendChild(jobImg);
jobContainer.appendChild(jobImgFilter);
jobList.appendChild(jobContainer);
},
success: function (response) {
addNotification("File uploaded successfully!", 1);
console.log('File processed successfully');
jobContainer.classList.add("success");
jobStatus.innerHTML = "Uploaded!";
if (!document.querySelector(".upload-panel").classList.contains("open")) {
addNotification("Image uploaded successfully", 1);
}
},
error: function (response) {
jobContainer.classList.add("critical");
switch (response.status) {
case 500:
addNotification('Server exploded, F\'s in chat', 2);
jobStatus.innerHTML = "Server exploded, F's in chat";
break;
case 400:
case 404:
addNotification('Error uploading. Blame yourself', 2);
jobStatus.innerHTML = "Error uploading. Blame yourself";
break;
case 403:
addNotification('None but devils play past here...', 2);
jobStatus.innerHTML = "None but devils play past here...";
break;
case 413:
addNotification('File too large!!!!!!', 3);
jobStatus.innerHTML = "File too large!!!!!!";
break;
default:
addNotification('Error uploading file, blame someone', 2);
jobStatus.innerHTML = "Error uploading file, blame someone";
break;
}
console.log('Error uploading file');
if (!document.querySelector(".upload-panel").classList.contains("open")) {
addNotification("Error uploading file", 2);
}
},
});
@ -70,4 +85,23 @@ function uploadFile(){
$("#description").val("");
$("#tags").val("");
}
};
};
function openUploadTab() {
// Open upload tab
const uploadTab = document.querySelector(".upload-panel");
uploadTab.style.display = "block";
setTimeout( function() {
uploadTab.classList.add("open");
}, 10);
}
function closeUploadTab() {
// Close upload tab
const uploadTab = document.querySelector(".upload-panel");
uploadTab.classList.remove("open");
setTimeout( function() {
uploadTab.style.display = "none";
}, 250);
}

View file

@ -1,15 +1,45 @@
{% extends 'layout.html' %}
{% block header %}
<div class="background-decoration">
<img src="{{ url_for('static', filename='images/background.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
<div class="banner">
<img src="{{ url_for('static', filename='images/bg.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
<span></span>
<div class="banner__content">
{% block banner_subtitle%}{% endblock %}
<h1>Groups</h1>
<p>gwa gwa</p>
</div>
</div>
{% endblock %}
{% block nav_groups %}navigation-item__selected{% endblock %}
{% block content %}
<h1>Image Group</h1>
<p>{{group_id}}</p>
<div class="gallery">
{% for group in groups %}
<a id="group-{{ group['id'] }}" class="gallery__item" href="/group/{{ group['id'] }}">
<span class="gallery__item-info">
<p>{{ group['id'] }}</p>
<h2><span class="time">{{ group['created_at'] }}</span></h2>
</span>
<img
class="gallery__item-group"
data-src="{{ group['file_name'] }}"
onload="imgFade(this)"
style="opacity:0;"
/>
</a>
{% endfor %}
</div>
<form action="/group" method="post" enctype="multipart/form-data">
<input type="text" name="name" placeholder="name">
<input type="text" name="description" placeholder="description">
<button type="submit">Submit</button>
</form>
{% endblock %}
{% block script %}
<script>
</script>
{% endblock %}

View file

@ -3,7 +3,7 @@
{% block header %}
<div class="background-decoration">
<img src="/api/uploads/{{ image['file_name'] }}?w=1000&h=1000" onload="imgFade(this)" style="opacity:0;"/>
<span></span>
<span style="background-image: linear-gradient(to top, rgba({{ image['image_colours'][0][0] }}, {{ image['image_colours'][0][1] }}, {{ image['image_colours'][0][2] }}, 1), transparent);"></span>
</div>
{% endblock %}
{% block wrapper_class %}image-wrapper{% endblock %}

View file

@ -2,7 +2,7 @@
{% block header %}
<div class="banner">
<img src="{{ url_for('static', filename='images/leaves.jpg') }}" onload="imgFade(this)" style="opacity:0;"/>
<img src="{{ url_for('static', filename='images/bg.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
<span></span>
<div class="banner__content">
@ -19,9 +19,9 @@
{% block content %}
<div class="gallery">
{% for image in images %}
<a id="image-{{ image['id'] }}" class="gallery__item" href="/image/{{ image['id'] }}">
<a id="image-{{ image['id'] }}" class="gallery__item" href="/image/{{ image['id'] }}" style="background-color: rgb({{ image['image_colours'][0][0] }}, {{ image['image_colours'][0][1] }}, {{ image['image_colours'][0][2] }})">
<span class="gallery__item-info">
<p>{{ image['id'] }}</p>
<p></p>
<h2><span class="time">{{ image['created_at'] }}</span></h2>
</span>
<img
@ -59,15 +59,19 @@
};
if (document.referrer.includes('image')) {
var referrerId = document.referrer.split('/').pop();
var imgOffset = document.getElementById('image-' + referrerId).offsetTop;
var imgHeight = document.getElementById('image-' + referrerId).offsetHeight;
var windowHeight = window.innerHeight;
document.querySelector('html').style.scrollBehavior = 'auto';
window.scrollTo(0, imgOffset + (imgHeight / 2) - (windowHeight / 2));
document.querySelector('html').style.scrollBehavior = 'smooth';
try {
var referrerId = document.referrer.split('/').pop();
var imgOffset = document.getElementById('image-' + referrerId).offsetTop;
var imgHeight = document.getElementById('image-' + referrerId).offsetHeight;
var windowHeight = window.innerHeight;
document.querySelector('html').style.scrollBehavior = 'auto';
window.scrollTo(0, imgOffset + (imgHeight / 2) - (windowHeight / 2));
document.querySelector('html').style.scrollBehavior = 'smooth';
} catch (e) {
console.log(e);
}
}
</script>
{% endblock %}

View file

@ -18,8 +18,8 @@
<script src="{{ url_for('static', filename='js/login.js') }}" defer></script>
<!-- UI -->
<script src="{{ url_for('static', filename='js/ui/popup.js') }}" defer></script>
<script src="{{ url_for('static', filename='js/ui/notifications.js') }}" defer></script>
<script src="{{ url_for('static', filename='js/ui/popup.js') }}"></script>
<script src="{{ url_for('static', filename='js/ui/notifications.js') }}"></script>
</head>
<body>
<div class="wrapper">
@ -29,9 +29,9 @@
<path d="M11 8.414V14a1 1 0 0 1-2 0V8.414L6.464 10.95A1 1 0 1 1 5.05 9.536l4.243-4.243a.997.997 0 0 1 1.414 0l4.243 4.243a1 1 0 1 1-1.414 1.414L11 8.414zM10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"></path>
</svg>
{% block header %}{% endblock %}
<div class="navigation">
<img src="{{url_for('static', filename='images/icon.png')}}" alt="Logo" class="logo" onload="imgFade(this)" style="opacity:0;">
<a href="{{url_for('gallery.index')}}" class="navigation-item {% block nav_home %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 24 24" width="24" fill="currentColor">
<path d="M2 8v10h12V8H2zm2-2V2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-2v4a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2zm2 0h8a2 2 0 0 1 2 2v4h2V2H6v4zm0 9a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"></path><path d="M7 6a3 3 0 1 1 6 0h-2a1 1 0 0 0-2 0H7zm1.864 13.518l2.725-4.672a1 1 0 0 1 1.6-.174l1.087 1.184 1.473-1.354-1.088-1.183a3 3 0 0 0-4.8.52L7.136 18.51l1.728 1.007zm6.512-12.969a2.994 2.994 0 0 1 3.285.77l1.088 1.183-1.473 1.354-1.087-1.184A1 1 0 0 0 16 8.457V8c0-.571-.24-1.087-.624-1.451z"></path>
@ -47,7 +47,7 @@
</a>
{% if g.user %}
<button class="navigation-item {% block nav_upload %}{% endblock %}" onclick="showUpload()">
<button class="navigation-item {% block nav_upload %}{% endblock %}" onclick="openUploadTab()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 24 24" width="24" fill="currentColor">
<path d="M8 3.414v5.642a1 1 0 1 1-2 0V3.414L4.879 4.536A1 1 0 0 1 3.464 3.12L6.293.293a1 1 0 0 1 1.414 0l2.829 2.828A1 1 0 1 1 9.12 4.536L8 3.414zM1 12h12a1 1 0 0 1 0 2H1a1 1 0 0 1 0-2z"></path>
</svg>
@ -81,6 +81,8 @@
{% endif %}
</div>
{% block header %}{% endblock %}
<div class="content {% block wrapper_class %}{% endblock %}">
{% block content %}
{% endblock %}
@ -98,6 +100,22 @@
</div>
</div>
</div>
<div class="upload-panel">
<span class="click-off" onclick="closeUploadTab()"></span>
<div class="container">
<h3>Upload stuffs</h3>
<p>May the world see your stuff 👀</p>
<form id="uploadForm" onsubmit="return uploadFile(event)">
<input type="file" id="file"/>
<input type="text" placeholder="alt" id="alt"/>
<input type="text" placeholder="description" id="description"/>
<input type="text" placeholder="tags" id="tags"/>
<button type="submit">Upload</button>
</form>
<div class="upload-jobs"></div>
</div>
</div>
</div>
<script>

View file

@ -17,3 +17,9 @@
100%
left: 0%
height: 0
@keyframes uploadingLoop
0%
left: -100%
100%
left: 100%

View file

@ -6,6 +6,8 @@
@import "ui/notification"
@import "ui/pop-up"
@import "ui/upload-panel"
@import "ui/navigation"
@import "ui/content"
@import "ui/background"

View file

@ -42,6 +42,4 @@
width: 100%
height: 100%
background-image: linear-gradient(to bottom, rgba($white, 0), rgba($white, 1))
z-index: +1

View file

@ -9,13 +9,9 @@
top: 0
left: 3.5rem
background-color: $white
background-color: $black
color: $black
background-image: linear-gradient(to right, darken($white, 1%) 15%, darken($white, 10%) 35%, darken($white, 1%) 50%)
background-size: 1000px 640px
animation: imgLoading 1.8s linear infinite forwards
overflow: hidden
transition: opacity 0.3s ease-in-out
@ -27,7 +23,7 @@
width: 100%
height: 100%
background-color: $white
background-color: $black
object-fit: cover
object-position: center center

View file

@ -20,9 +20,9 @@
position: relative
background: linear-gradient(to right, darken($white, 1%) 15%, darken($white, 10%) 35%, darken($white, 1%) 50%)
background-size: 1000px 640px
animation: imgLoading 1.8s linear infinite forwards
// background: linear-gradient(to right, darken($white, 1%) 15%, darken($white, 10%) 35%, darken($white, 1%) 50%)
// background-size: 1000px 640px
// animation: imgLoading 1.8s linear infinite forwards
border-radius: $rad
box-sizing: border-box

View file

@ -22,6 +22,18 @@
> span
height: 100%
.logo
margin: 0
padding: 0
width: 3.5rem
height: 3.5rem
min-height: 3.5rem
display: flex
flex-direction: row
align-items: center
.navigation-item
margin: 0
padding: 1rem
@ -108,6 +120,9 @@
> span
display: none
.logo
display: none
.navigation-item
margin: 0.25rem
padding: 0

View file

@ -93,7 +93,7 @@
animation: notificationTimeout 5.1s linear
@each $name, $colour in (success: $succes, error: $critical, warning: $warning, info: $info)
@each $name, $colour in (success: $success, error: $critical, warning: $warning, info: $info)
.sniffle__notification--#{$name}
@include notification($colour)

View file

@ -25,7 +25,6 @@
left: 3.5rem
background-color: rgba($black, 0.8)
backdrop-filter: blur(1rem)
opacity: 0
z-index: 101

View file

@ -0,0 +1,219 @@
.upload-panel
position: fixed
top: 0
left: 0
display: none
width: 100%
height: 100vh
background-color: rgba($black, 0)
overflow: hidden
z-index: 68
transition: background-color 0.25s cubic-bezier(0.76, 0, 0.17, 1)
h3
margin: 0
padding: 0
font-size: 1.5rem
font-weight: 600
color: $white
p
margin: 0
padding: 0
font-size: 1rem
font-weight: 500
color: $white
form
margin: 0
padding: 0
width: 100%
display: flex
flex-direction: column
align-items: center
gap: 0.5rem
input
margin: 0
padding: 0.5rem 1rem
width: 100%
height: 2.5rem
font-size: 1rem
font-weight: 600
color: $white
background-color: $black2
border: none
border-radius: $rad-inner
&::placeholder
color: $white
button
margin: 0
padding: 0.5rem 1rem
width: 100%
height: 2.5rem
font-size: 1rem
font-weight: 600
color: $white
background-color: $primary
border: none
border-radius: $rad-inner
cursor: pointer
&:hover
background-color: $black2
color: $primary
.click-off
position: absolute
top: 0
left: 0
width: 100%
height: 100%
z-index: +1
.container
padding: 0.5rem
position: absolute
top: 0
left: calc(-400px + 3.5rem)
width: 400px
height: 100%
display: flex
flex-direction: column
gap: 1rem
background-color: $black
opacity: 0
z-index: +2
transition: left 0.25s cubic-bezier(0.76, 0, 0.17, 1), opacity 0.25s cubic-bezier(0.76, 0, 0.17, 1)
.upload-jobs
display: flex
flex-direction: column
gap: 0.5rem
border-radius: $rad
overflow-y: auto
.job
width: 100%
height: 5rem
min-height: 5rem
position: relative
display: flex
align-items: center
gap: 0.5rem
background-color: $black2
border-radius: $rad
overflow: hidden
img
position: absolute
top: 0
left: 0
width: 100%
height: 5rem
object-fit: cover
.img-filter
position: absolute
top: 0
left: 0
width: 100%
height: 100%
background-image: linear-gradient(to right, rgba($black, 0.8), rgba($black, 0))
.job__status
margin: 0
padding: 0
position: absolute
top: 0.5rem
left: 0.5rem
font-size: 1rem
font-weight: 600
color: $white
z-index: +3
transition: color 0.25s cubic-bezier(0.76, 0, 0.17, 1)
.progress
width: 100%
height: $rad-inner
position: absolute
bottom: 0
left: -100%
background-color: $primary
animation: uploadingLoop 1s cubic-bezier(0.76, 0, 0.17, 1) infinite
z-index: +5
transition: left 1s cubic-bezier(0.76, 0, 0.17, 1)
&.critical
.job__status, .progress
color: $critical
&.success
.job__status
color: $success
.progress
height: 0
animation: none
&.warning
.job__status, .progress
color: $warning
&.critical, &.success, &.warning
.progress
height: 0
&.open
background-color: rgba($black, 0.5)
.container
left: 3.5rem
opacity: 1

View file

@ -24,7 +24,6 @@
opacity: 0 // hide
background-color: rgba($black, 0.8)
backdrop-filter: blur(1rem)
z-index: 21
box-sizing: border-box
@ -86,6 +85,8 @@
object-fit: contain
object-position: center
// box-shadow: 0 0 0.5rem rgba($black, 0.5)
.image-info__container
margin: 0
padding: 0

View file

@ -12,7 +12,7 @@ $purple: #A988B0
$primary: $green
$warning: $orange
$critical: $red
$succes: $green
$success: $green
$info: $blue
$rad: 6px

134
poetry.lock generated
View file

@ -2,14 +2,14 @@
[[package]]
name = "astroid"
version = "2.14.2"
version = "2.15.0"
description = "An abstract syntax tree for Python with inference support."
category = "main"
optional = false
python-versions = ">=3.7.2"
files = [
{file = "astroid-2.14.2-py3-none-any.whl", hash = "sha256:0e0e3709d64fbffd3037e4ff403580550f14471fd3eaae9fa11cc9a5c7901153"},
{file = "astroid-2.14.2.tar.gz", hash = "sha256:a3cf9f02c53dd259144a7e8f3ccd75d67c9a8c716ef183e0c1f291bc5d7bb3cf"},
{file = "astroid-2.15.0-py3-none-any.whl", hash = "sha256:e3e4d0ffc2d15d954065579689c36aac57a339a4679a679579af6401db4d3fdb"},
{file = "astroid-2.15.0.tar.gz", hash = "sha256:525f126d5dc1b8b0b6ee398b33159105615d92dc4a17f2cd064125d57f6186fa"},
]
[package.dependencies]
@ -112,6 +112,18 @@ files = [
{file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
]
[[package]]
name = "cachelib"
version = "0.9.0"
description = "A collection of cache libraries in the same API interface."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"},
{file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"},
]
[[package]]
name = "click"
version = "8.1.3"
@ -192,6 +204,22 @@ Werkzeug = ">=2.2.2"
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]
[[package]]
name = "flask-caching"
version = "2.0.2"
description = "Adds caching support to Flask applications."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "Flask-Caching-2.0.2.tar.gz", hash = "sha256:24b60c552d59a9605cc1b6a42c56cdb39a82a28dab4532bbedb9222ae54ecb4e"},
{file = "Flask_Caching-2.0.2-py3-none-any.whl", hash = "sha256:19571f2570e9b8dd9dd9d2f49d7cbee69c14ebe8cc001100b1eb98c379dd80ad"},
]
[package.dependencies]
cachelib = ">=0.9.0,<0.10.0"
Flask = "<3"
[[package]]
name = "flask-compress"
version = "1.13"
@ -613,14 +641,14 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytes
[[package]]
name = "pylint"
version = "2.16.3"
version = "2.16.4"
description = "python code static checker"
category = "main"
optional = false
python-versions = ">=3.7.2"
files = [
{file = "pylint-2.16.3-py3-none-any.whl", hash = "sha256:3e803be66e3a34c76b0aa1a3cf4714b538335e79bd69718d34fcf36d8fff2a2b"},
{file = "pylint-2.16.3.tar.gz", hash = "sha256:0decdf8dfe30298cd9f8d82e9a1542da464db47da60e03641631086671a03621"},
{file = "pylint-2.16.4-py3-none-any.whl", hash = "sha256:4a770bb74fde0550fa0ab4248a2ad04e7887462f9f425baa0cd8d3c1d098eaee"},
{file = "pylint-2.16.4.tar.gz", hash = "sha256:8841f26a0dbc3503631b6a20ee368b3f5e0e5461a1d95cf15d103dab748a0db3"},
]
[package.dependencies]
@ -708,14 +736,14 @@ files = [
[[package]]
name = "setuptools"
version = "67.4.0"
version = "67.5.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "setuptools-67.4.0-py3-none-any.whl", hash = "sha256:f106dee1b506dee5102cc3f3e9e68137bbad6d47b616be7991714b0c62204251"},
{file = "setuptools-67.4.0.tar.gz", hash = "sha256:e5fd0a713141a4a105412233c63dc4e17ba0090c8e8334594ac790ec97792330"},
{file = "setuptools-67.5.1-py3-none-any.whl", hash = "sha256:1c39d42bda4cb89f7fdcad52b6762e3c309ec8f8715b27c684176b7d71283242"},
{file = "setuptools-67.5.1.tar.gz", hash = "sha256:15136a251127da2d2e77ac7a1bc231eb504654f7e3346d93613a13f2e2787535"},
]
[package.extras]
@ -725,53 +753,53 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (
[[package]]
name = "sqlalchemy"
version = "2.0.4"
version = "2.0.5.post1"
description = "Database Abstraction Library"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "SQLAlchemy-2.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b67d6e626caa571fb53accaac2fba003ef4f7317cb3481e9ab99dad6e89a70d6"},
{file = "SQLAlchemy-2.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b01dce097cf6f145da131a53d4cce7f42e0bfa9ae161dd171a423f7970d296d0"},
{file = "SQLAlchemy-2.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738c80705e11c1268827dbe22c01162a9cdc98fc6f7901b429a1459db2593060"},
{file = "SQLAlchemy-2.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6363697c938b9a13e07f1bc2cd433502a7aa07efd55b946b31d25b9449890621"},
{file = "SQLAlchemy-2.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a42e6831e82dfa6d16b45f0c98c69e7b0defc64d76213173456355034450c414"},
{file = "SQLAlchemy-2.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:011ef3c33f30bae5637c575f30647e0add98686642d237f0c3a1e3d9b35747fa"},
{file = "SQLAlchemy-2.0.4-cp310-cp310-win32.whl", hash = "sha256:c1e8edc49b32483cd5d2d015f343e16be7dfab89f4aaf66b0fa6827ab356880d"},
{file = "SQLAlchemy-2.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:77a380bf8721b416782c763e0ff66f80f3b05aee83db33ddfc0eac20bcb6791f"},
{file = "SQLAlchemy-2.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a2f9120eb32190bdba31d1022181ef08f257aed4f984f3368aa4e838de72bc0"},
{file = "SQLAlchemy-2.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:679b9bd10bb32b8d3befed4aad4356799b6ec1bdddc0f930a79e41ba5b084124"},
{file = "SQLAlchemy-2.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:582053571125895d008d4b8d9687d12d4bd209c076cdbab3504da307e2a0a2bd"},
{file = "SQLAlchemy-2.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c82395e2925639e6d320592943608070678e7157bd1db2672a63be9c7889434"},
{file = "SQLAlchemy-2.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:25e4e54575f9d2af1eab82d3a470fca27062191c48ee57b6386fe09a3c0a6a33"},
{file = "SQLAlchemy-2.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9946ee503962859f1a9e1ad17dff0859269b0cb453686747fe87f00b0e030b34"},
{file = "SQLAlchemy-2.0.4-cp311-cp311-win32.whl", hash = "sha256:c621f05859caed5c0aab032888a3d3bde2cae3988ca151113cbecf262adad976"},
{file = "SQLAlchemy-2.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:662a79e80f3e9fe33b7861c19fedf3d8389fab2413c04bba787e3f1139c22188"},
{file = "SQLAlchemy-2.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3f927340b37fe65ec42e19af7ce15260a73e11c6b456febb59009bfdfec29a35"},
{file = "SQLAlchemy-2.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67901b91bf5821482fcbe9da988cb16897809624ddf0fde339cd62365cc50032"},
{file = "SQLAlchemy-2.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1644c603558590f465b3fa16e4557d87d3962bc2c81fd7ea85b582ecf4676b31"},
{file = "SQLAlchemy-2.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9a7ecaf90fe9ec8e45c86828f4f183564b33c9514e08667ca59e526fea63893a"},
{file = "SQLAlchemy-2.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8a88b32ce5b69d18507ffc9f10401833934ebc353c7b30d1e056023c64f0a736"},
{file = "SQLAlchemy-2.0.4-cp37-cp37m-win32.whl", hash = "sha256:2267c004e78e291bba0dc766a9711c389649cf3e662cd46eec2bc2c238c637bd"},
{file = "SQLAlchemy-2.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:59cf0cdb29baec4e074c7520d7226646a8a8f856b87d8300f3e4494901d55235"},
{file = "SQLAlchemy-2.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dd801375f19a6e1f021dabd8b1714f2fdb91cbc835cd13b5dd0bd7e9860392d7"},
{file = "SQLAlchemy-2.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8efdda920988bcade542f53a2890751ff680474d548f32df919a35a21404e3f"},
{file = "SQLAlchemy-2.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:918c2b553e3c78268b187f70983c9bc6f91e451a4f934827e9c919e03d258bd7"},
{file = "SQLAlchemy-2.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d05773d5c79f2d3371d81697d54ee1b2c32085ad434ce9de4482e457ecb018"},
{file = "SQLAlchemy-2.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fdb2686eb01f670cdc6c43f092e333ff08c1cf0b646da5256c1237dc4ceef4ae"},
{file = "SQLAlchemy-2.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ff0a7c669ec7cdb899eae7e622211c2dd8725b82655db2b41740d39e3cda466"},
{file = "SQLAlchemy-2.0.4-cp38-cp38-win32.whl", hash = "sha256:57dcd9eed52413f7270b22797aa83c71b698db153d1541c1e83d45ecdf8e95e7"},
{file = "SQLAlchemy-2.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:54aa9f40d88728dd058e951eeb5ecc55241831ba4011e60c641738c1da0146b7"},
{file = "SQLAlchemy-2.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:817aab80f7e8fe581696dae7aaeb2ceb0b7ea70ad03c95483c9115970d2a9b00"},
{file = "SQLAlchemy-2.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc7b9f55c2f72c13b2328b8a870ff585c993ba1b5c155ece5c9d3216fa4b18f6"},
{file = "SQLAlchemy-2.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f696828784ab2c07b127bfd2f2d513f47ec58924c29cff5b19806ac37acee31c"},
{file = "SQLAlchemy-2.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce54965a94673a0ebda25e7c3a05bf1aa74fd78cc452a1a710b704bf73fb8402"},
{file = "SQLAlchemy-2.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f342057422d6bcfdd4996e34cd5c7f78f7e500112f64b113f334cdfc6a0c593d"},
{file = "SQLAlchemy-2.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b5deafb4901618b3f98e8df7099cd11edd0d1e6856912647e28968b803de0dae"},
{file = "SQLAlchemy-2.0.4-cp39-cp39-win32.whl", hash = "sha256:81f1ea264278fcbe113b9a5840f13a356cb0186e55b52168334124f1cd1bc495"},
{file = "SQLAlchemy-2.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:954f1ad73b78ea5ba5a35c89c4a5dfd0f3a06c17926503de19510eb9b3857bde"},
{file = "SQLAlchemy-2.0.4-py3-none-any.whl", hash = "sha256:0adca8a3ca77234a142c5afed29322fb501921f13d1d5e9fa4253450d786c160"},
{file = "SQLAlchemy-2.0.4.tar.gz", hash = "sha256:95a18e1a6af2114dbd9ee4f168ad33070d6317e11bafa28d983cc7b585fe900b"},
{file = "SQLAlchemy-2.0.5.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7fe933831e17f93947b4e3db4e4a7470dae24340f269baf06cdfcc0538c8d1cb"},
{file = "SQLAlchemy-2.0.5.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5c00d2b3607f9ae5c0827ebb2d01020c26cfce4064aa664db21d1fd6a47f8f60"},
{file = "SQLAlchemy-2.0.5.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c12f0455f30d637644f4d6df2bda1475c61398483edb58d55c670be31a31d549"},
{file = "SQLAlchemy-2.0.5.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7e55659740e768a1bf1257275b565137a3d28839789c85193dd6a1e642b3cc9"},
{file = "SQLAlchemy-2.0.5.post1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:15c92107437770087ad4fece6ed9553ab97474f3b92d15eb62cea9686228f252"},
{file = "SQLAlchemy-2.0.5.post1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:50b15fce441b8eb13bfd06df1088aa52c9a3d72d4894e3456040857d48a622da"},
{file = "SQLAlchemy-2.0.5.post1-cp310-cp310-win32.whl", hash = "sha256:ffda70373ddfe8ec733d518e4e41eb5599480d48e8496c44bb0cac0e37b281c0"},
{file = "SQLAlchemy-2.0.5.post1-cp310-cp310-win_amd64.whl", hash = "sha256:e40c39cfcbe416a7722a226ecd98fad0e08f8ae33e8f94b0858afe094583bfbc"},
{file = "SQLAlchemy-2.0.5.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:22f60d214899b573edc8aeb9ba84f7e832505511ce68974636e6da4a27c957a3"},
{file = "SQLAlchemy-2.0.5.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4756878d0ceb6e0e9c6cfcfaa9df81adbfcca8cc4b9ec37934918008c0f20507"},
{file = "SQLAlchemy-2.0.5.post1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6df48bb7af217fd086617aae1f9606ff91cfab9a29c3e77dd80e4bab8aaf29fc"},
{file = "SQLAlchemy-2.0.5.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1012c0440108c360b94f43667525365c43516e8c7f1f7de8dfb73471181055df"},
{file = "SQLAlchemy-2.0.5.post1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2bffc27ec0386ef4af7c8923f0f809f88671859b907c9e11f000c39b97195e99"},
{file = "SQLAlchemy-2.0.5.post1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8e7d5766fb99743eb70126daaff45f43bee3f4b79ba6047a0749912e8538f0ff"},
{file = "SQLAlchemy-2.0.5.post1-cp311-cp311-win32.whl", hash = "sha256:c2c41bf05b4cf4ffead35896affa3b457c17755d0fd83b1ba72f7f55abb3a3f1"},
{file = "SQLAlchemy-2.0.5.post1-cp311-cp311-win_amd64.whl", hash = "sha256:56d38c3638965df5257ac4648ba2887aaf1e3409397192dd85ddfe7b96dc7680"},
{file = "SQLAlchemy-2.0.5.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:68c4c5ab13997fa7af37d5780da11ddc184d4e88fb2d8a26525044c233f03bc7"},
{file = "SQLAlchemy-2.0.5.post1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:869ca02661f6d4cece5823997a364dfa97601de11151fca3ebc3429eb9ffa2e0"},
{file = "SQLAlchemy-2.0.5.post1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6cb241c5c1af422c0fa742bd09a8eaf158da1433617ded1ffcbb56de6ff8047"},
{file = "SQLAlchemy-2.0.5.post1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9bb63e22ddf01cbbb290e61f31471480d2e40283e558cdd924b94dc4fc2e186b"},
{file = "SQLAlchemy-2.0.5.post1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2c3869a7bdd5bb76fb50976abe339e30cce8e9f7c778a50811d310ec82cdf51a"},
{file = "SQLAlchemy-2.0.5.post1-cp37-cp37m-win32.whl", hash = "sha256:1edb6621782f9a3e80750ba1859580b778a424242d4e6b9bcd46fa6beca75c12"},
{file = "SQLAlchemy-2.0.5.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:3c4e673da09af37b7a5c13b947fdb387c3800d43dcd86c1d553e3c70369e4749"},
{file = "SQLAlchemy-2.0.5.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac12d8bef707d02ef179f0586c848db2954668dca2b72c69df950e08dc8cddb4"},
{file = "SQLAlchemy-2.0.5.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e13cea675937125417953fd011138f13cf979051567f48074fffb3bb0b64b917"},
{file = "SQLAlchemy-2.0.5.post1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:744b01fcdfef66b7373262bf75d714a4339f85c05b74b1732749f25ed65f33f6"},
{file = "SQLAlchemy-2.0.5.post1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03cd5709839fc376538f102c58f632d48bd0b92715bd290c3b2c066e0dd0f214"},
{file = "SQLAlchemy-2.0.5.post1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7fab3d4062472e1e6002bfcd53cc7446189941be083a5465760aa794092004ee"},
{file = "SQLAlchemy-2.0.5.post1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:059ddd4ddbfb8f1c872d601b168273dfaab0eae458736c7c754187b9a8e92ad5"},
{file = "SQLAlchemy-2.0.5.post1-cp38-cp38-win32.whl", hash = "sha256:9a4d9a7e9b344bf8ce2ed699baa8a43d9fbdad3aecff259f1d0daf6bb2e7e0c0"},
{file = "SQLAlchemy-2.0.5.post1-cp38-cp38-win_amd64.whl", hash = "sha256:b52be78c5e86ade646c82a10b2be4b6ed8f623052b4405b26681880df1a15d5a"},
{file = "SQLAlchemy-2.0.5.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e33867e09820c98630f7faec535a8cc4116fd362787404b41883d373437290b"},
{file = "SQLAlchemy-2.0.5.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0433abeb650c72c872e31010bff8536907fb05f6fa29a9b880046570c03383ca"},
{file = "SQLAlchemy-2.0.5.post1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d81b2fa605939c437f8b0b8522ec2c19508f3036d6043cb70a15dd56760ab710"},
{file = "SQLAlchemy-2.0.5.post1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d811b97f58d99e5948752087903cb414fd77a60a5e09293be16c924219178c3b"},
{file = "SQLAlchemy-2.0.5.post1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:adf597e756e27173be57f243cc17bea7af1ac74b35c0120aace2738f59c92a48"},
{file = "SQLAlchemy-2.0.5.post1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f53542654124d30a3c3ebff9f99e5749add75e4cf28895a2ca6cd1458039bd8f"},
{file = "SQLAlchemy-2.0.5.post1-cp39-cp39-win32.whl", hash = "sha256:b21694c8543becc2bc4f05f8d07970860e6ad005024712b7195abb1f4e0daf47"},
{file = "SQLAlchemy-2.0.5.post1-cp39-cp39-win_amd64.whl", hash = "sha256:72e8d65b20147df71297d863981d8e56e429a8ae2bb835bd5e89efd7ef849866"},
{file = "SQLAlchemy-2.0.5.post1-py3-none-any.whl", hash = "sha256:621e92ace804e19da2e472e349736d7ba5e2e4a14d41c4de9e2474e5f40a11ed"},
{file = "SQLAlchemy-2.0.5.post1.tar.gz", hash = "sha256:13eb2a5882cfd9f4eedaaec14a5603a096f0125f7c3cb48611b3bfa3c253f25d"},
]
[package.dependencies]
@ -958,5 +986,5 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "de131da70fd04213714611f747ff9102979dbc6855e68645ea93fa83a6d433d8"
python-versions = "^3.8"
content-hash = "0c2a7c41e8974f82e33d09ddcdf4a97f54fdbf1be71c0d7c7e4be1daa05fe661"

View file

@ -10,6 +10,7 @@ readme = ".github/README.md"
python = "^3.8"
Flask = "^2.2.2"
flask-compress = "^1.13"
flask-caching = "^2.0.2"
gunicorn = "^20.1.0"
python-dotenv = "^0.21.0"
pyyaml = "^6.0"