64 Commits

Author SHA1 Message Date
3f947a934c Merge branch 'fix/communicate-file-too-large' into 'marietje-zuid'
fix(songs): handle HTTP 413 and remove redundant .json() calls

Closes #76

See merge request technicie/MarietjeDjango!90
2024-04-23 19:09:55 +02:00
b5aa079254 Merge branch 'wkuijltjes/playsinat' into 'marietje-zuid'
Fix toggling of Plays In/Plays At in mobile view

Closes #86

See merge request technicie/MarietjeDjango!93
2024-04-23 18:19:39 +02:00
7ab6bc6c78 fix(queues): remove redundant and faulty toggle_details call
song is not even defined in this scope.
2024-04-23 18:11:59 +02:00
3c2df2adfd styling(queues): move no underline to css 2024-04-23 17:56:58 +02:00
82dc7c0193 Merge branch 'wkuijltjes/fixtimer' into 'marietje-zuid'
Fix dynamic times that should be static and vice versa if logged in user has queued

Closes #66

See merge request technicie/MarietjeDjango!94
2024-04-23 17:44:22 +02:00
6901d913c0 Merge branch 'wkuijltjes/aesthetics' into 'marietje-zuid'
Brush up some details: reduce breakpoints on small screens, Requested by --> Requested By

See merge request technicie/MarietjeDjango!95
2024-04-23 17:31:35 +02:00
0f85727c5d Brush up some details: reduce breakpoints on small screens, Requested by --> Requested By 2024-03-18 18:02:25 +01:00
2b2e0a0275 Fix dynamic times that should be static and vice versa if logged in user has queued 2024-03-18 16:51:34 +01:00
311e13f696 Fix toggling of Plays In/Plays At in mobile view 2024-03-18 12:45:15 +01:00
cf76861961 Merge branch 'wkuijltjes/alignrequest' into 'marietje-zuid'
Left-align long song titles in the request tab with a lot of tender loving care from Kees xx

Closes #81

See merge request technicie/MarietjeDjango!92
2024-03-05 14:27:46 +01:00
841e5daf8f Merge branch 'fix/now-really-fix-error-message-reporting' into 'marietje-zuid'
fix(queues): now really fix the request notifications

Closes #82

See merge request technicie/MarietjeDjango!91
2024-03-05 13:25:13 +01:00
beda2a4c63 Merge branch 'kvkempen/assorted-fixes-20240227' into 'marietje-zuid'
Assorted fixes on the night of 2024-02-27

Closes #84, #66, and #77

See merge request technicie/MarietjeDjango!89
2024-03-05 12:57:51 +01:00
d004ff81ac Left-align long song titles in the request tab with a lot of tender loving care from Kees xx 2024-03-05 11:59:28 +01:00
669e1e3b18 fix(queues): now really fix the request notifications
Instead of checking for success, now the correct HTTP response code is
sent by the request API. My previous fix broke the other responses, oops.

Closes #82

Relates #44

The refresh as introduced in my fix for #82 (!89) is reverted, as this
was already done on successful requests.
2024-03-05 10:48:21 +01:00
8218803ca8 feature(queue): refresh queue after requesting
Closes #84
2024-03-05 10:21:33 +01:00
5c32fcb1d1 fix(songs): handle HTTP 413 and remove redundant .json() calls
Closes #76
2024-02-28 00:20:57 +01:00
0e5fb7acd7 fix(queues): remove faulty update, add null-coalescing on song user
Removal of update call might resolve #66, but I hope I do not negate
@tjibbegottmer's work.
2024-02-27 23:20:23 +01:00
ff3171022a chore: update dependencies
Was a chore, but also fixes a FontAwesome warning in Firefox.

Closes #77
2024-02-27 23:20:23 +01:00
749546867a fix: set SameSite=Lax on cookies
Prevents Firefox warning.
2024-02-27 23:20:23 +01:00
2e65d7fd89 Merge branch 'wkuijltjes/searchfield' into 'marietje-zuid'
Auto-select search field in request tab

Closes #42

See merge request technicie/MarietjeDjango!88
2024-02-27 22:37:35 +01:00
521c047225 Auto-select search field in request tab 2024-02-27 22:37:35 +01:00
0a3b2d2a8c Merge branch 'wkuijltjes/streepje' into 'marietje-zuid'
Fix streepje from issue #48

Closes #48

See merge request technicie/MarietjeDjango!84
2024-02-27 21:08:48 +01:00
13448a2eec Merge branch 'fix/remember-song-details-unfold' into 'marietje-zuid'
Keep song details unfolded between refreshes

Closes #79

See merge request technicie/MarietjeDjango!86
2024-02-27 20:56:24 +01:00
fe186ffa99 Merge branch 'feature/remember-plays-at-in' into 'marietje-zuid'
Store plays at/in in cookie

Closes #73

See merge request technicie/MarietjeDjango!87
2024-02-27 20:28:31 +01:00
e30cc33ffc Merge branch 'oslomp/remove_pipeline' into 'marietje-zuid'
disabled deployment pipeline

See merge request technicie/MarietjeDjango!85
2024-02-27 20:20:18 +01:00
4d797691b1 Disable deployment pipeline
It is broken since the migration to the new server.
2024-02-27 20:20:18 +01:00
e45940772f Fix streepje from issue #48 2024-02-27 13:04:01 +01:00
77728c9a82 Replace shift shit with array filter
Co-Authored-By: Olaf Slomp <git@oslomp.nl>
2024-02-27 12:48:52 +01:00
08cd59ce09 Store plays at/in in cookie
Closes #73
2024-02-27 12:16:38 +01:00
ad94fe7930 Keep song details unfolded between refreshes
Closes #79
2024-02-27 12:11:43 +01:00
5acf553269 feat(queue): re-add song details on mobile view
Closes #62
2024-02-21 15:50:20 +01:00
ec66f09a79 Merge branch 'feature/marietje-4.1-in-header' into 'marietje-zuid'
Marietje in header and fix load times for upload page

Adds the Marietje header back into the mobile view. Also fixes an issue
with the upload page loading taking a long time due to a large id3
library.
Finally, this fixes errors running the pipeline: the project name is
changed to the folder name (marietje) and a typo was removed from one
of the maintainer names.

Closes #39, #37, and #44

See merge request technicie/MarietjeDjango!65
2024-02-21 15:42:01 +01:00
4768271aee Marietje in header and fix load times for upload page 2024-02-21 15:40:41 +01:00
2dd4dd3381 Merge branch 'fix/request-table' into 'marietje-zuid'
Other container layout and responsive queue container

Closes #47

See merge request technicie/MarietjeDjango!67
2024-02-21 12:28:25 +01:00
219af8fa1d Merge branch 'wkuijltjes/aesthetic-changes' into 'marietje-zuid'
Some aesthetic changes to the dark mode colors and the table borders

See merge request technicie/MarietjeDjango!81
2023-11-25 07:45:46 +01:00
98e43aa688 Some aesthetic changes to the dark mode colors and the table borders 2023-11-25 07:45:46 +01:00
831f479eec Merge branch 'wkuijltjes/minor-edits-stats-pages' into 'marietje-zuid'
Slightly improve stats pages

Closes #69

See merge request technicie/MarietjeDjango!80
2023-11-25 07:45:01 +01:00
6a9c22b7f8 Slightly improve stats pages 2023-11-25 07:45:01 +01:00
2d36ace60f Merge branch 'wkuijltjes/reduce-control-column-size' into 'marietje-zuid'
Reduce the width of buttons in the control column and recombine them into one row

Closes #68

See merge request technicie/MarietjeDjango!79
2023-11-25 07:44:45 +01:00
fbafcf1b06 Reduce the width of buttons in the control column and recombine them into one row 2023-11-25 07:44:45 +01:00
66ac1076d3 Merge branch 'fix/scrolling-in-requests-page' into 'marietje-zuid'
Replace a with button

Closes #41

See merge request technicie/MarietjeDjango!77
2023-10-14 15:20:14 +02:00
222ff7598a Replace a with button 2023-10-14 08:51:27 +02:00
4f134ab62a Merge branch 'wkuijltjes/fix-stats-linebreaking' into 'marietje-zuid'
Preventing the line breaking of some of the "# Requests" columns in the stats pages

Closes #43

See merge request technicie/MarietjeDjango!76
2023-10-13 13:08:29 +02:00
f4f6cd6eed Preventing the line breaking of some of the "# Requests" columns in the stats pages 2023-10-13 13:08:29 +02:00
f516a31476 Merge branch 'fix/personal-queue-stats' into 'marietje-zuid'
Reimplement personal queue length overview

Closes #52

See merge request technicie/MarietjeDjango!75
2023-10-13 08:58:37 +02:00
72a6e65e98 Update settings layout for uWSGI 2023-10-13 08:47:45 +02:00
16e99ea320 Merge branch 'fix/manage-page-shows-all-songs' into 'marietje-zuid'
Show only songs that match the user on manage page

Closes #45

See merge request technicie/MarietjeDjango!66
2023-10-12 18:14:03 +02:00
107d5053d3 Show only songs that match the user on manage page 2023-10-12 18:14:03 +02:00
0672555d75 Reimplement personal queue length overview
Closes #52

The functionality was partially removed in !62.
2023-10-12 15:43:01 +02:00
3786368810 Merge branch 'wkuijltjes/capitalising-a-single-lowercase' into 'marietje-zuid'
Changing a single lowercase letter to uppercase

See merge request technicie/MarietjeDjango!74
2023-10-11 17:28:48 +02:00
41b128e2d8 Changing a single lowercase letter to uppercase 2023-10-11 17:28:48 +02:00
f69d0f1377 Merge branch 'fix/beeldscherm-marietje-null' into 'marietje-zuid'
Fix beeldscherm by resolving null to marietje

See merge request technicie/MarietjeDjango!73
2023-10-11 17:22:04 +02:00
04bf9532e4 Fix beeldscherm by resolving null to marietje 2023-10-11 17:22:04 +02:00
36f2830d48 Merge branch 'feature/announcements' into 'marietje-zuid'
Add announcements app

See merge request technicie/MarietjeDjango!71
2023-10-11 16:49:37 +02:00
a78098198f Add announcements app 2023-10-11 16:49:37 +02:00
5c0480853a Merge branch 'fix/forgot-password-page' into 'marietje-zuid'
Fix bug for Forgot Password page

Closes #54

See merge request technicie/MarietjeDjango!70
2023-10-11 16:17:47 +02:00
41f33f5d20 Merge branch 'feature/save-amount-shown-per-page' into 'marietje-zuid'
Store page size on manage and request pages

Closes #51

See merge request technicie/MarietjeDjango!68
2023-10-11 16:09:30 +02:00
a9e0614b2c Merge branch 'feature/development-settings' into 'marietje-zuid'
Split production and development settings

Closes #34

See merge request technicie/MarietjeDjango!64
2023-10-11 16:02:25 +02:00
4c10d2308c Split production and development settings 2023-10-11 16:02:25 +02:00
d19a115bbe Merge branch 'fix/sort-artist-title' into 'marietje-zuid'
Sort tracks based on artist, then on title

Closes #57 and #55

See merge request technicie/MarietjeDjango!72
2023-10-11 15:26:00 +02:00
63cc1a8fe0 Sort tracks based on artist, then on title 2023-10-11 15:26:00 +02:00
d8fa14e377 Fix bug for Forgot Password page 2023-10-06 20:49:18 +02:00
7a1b0583a6 Store page size on manage and request pages 2023-10-04 20:41:00 +02:00
8a926f3924 Other container layout and responsive queue container 2023-10-04 20:31:49 +02:00
38 changed files with 1274 additions and 627 deletions

5
.gitignore vendored
View File

@ -140,4 +140,7 @@ dmypy.json
cython_debug/
# Jetbrains
.idea/
.idea/
# macOS
.DS_Store

View File

@ -16,7 +16,8 @@ black:
script:
- poetry run black --quiet --check marietje
deploy:
# TODO: Fix the deploy stage, as it has not been adapted to the new server Marietje runs on. The . disables the stage.
.deploy:
stage: deploy
only: ['marietje-zuid']
before_script:

View File

View File

@ -0,0 +1,16 @@
from django.contrib import admin
from announcements.models import Announcement
@admin.register(Announcement)
class AnnouncementAdmin(admin.ModelAdmin):
"""Manage the admin pages for the announcements."""
list_display = ("title", "since", "until", "visible")
def visible(self, obj):
"""Is the object visible."""
return obj.is_visible
visible.boolean = True

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
class AnnouncementsConfig(AppConfig):
"""Announcements AppConfig."""
default_auto_field = "django.db.models.BigAutoField"
name = "announcements"

View File

@ -0,0 +1,43 @@
from announcements.services import (
validate_closed_announcements,
sanitize_closed_announcements,
encode_closed_announcements,
)
from django.apps import apps
class ClosedAnnouncementsMiddleware:
"""Closed Announcements Middleware."""
def __init__(self, get_response):
"""Initialize."""
self.get_response = get_response
def __call__(self, request):
"""Update the closed announcements' cookie."""
response = self.get_response(request)
closed_announcements = validate_closed_announcements(
sanitize_closed_announcements(request.COOKIES.get("closed-announcements", None))
)
response.set_cookie("closed-announcements", encode_closed_announcements(closed_announcements))
return response
class AppAnnouncementMiddleware:
"""Announcement Middleware."""
def __init__(self, get_response):
"""Initialize Announcement Middleware."""
self.get_response = get_response
def __call__(self, request):
"""Call app announcement function if they exist for gathering all announcements."""
announcements = []
for app in apps.get_app_configs():
if hasattr(app, "announcements") and hasattr(app.announcements, "__call__"):
announcements += app.announcements(request)
setattr(request, "_app_announcements", announcements)
return self.get_response(request)

View File

@ -0,0 +1,30 @@
# Generated by Django 3.2.16 on 2022-12-09 19:50
from django.db import migrations, models
import django.utils.timezone
import tinymce.models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Announcement',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(help_text='This is not shown on the announcement but can be used as an identifier in the admin area.', max_length=100)),
('content', tinymce.models.HTMLField(max_length=500)),
('since', models.DateTimeField(default=django.utils.timezone.now)),
('until', models.DateTimeField(blank=True, null=True)),
('icon', models.CharField(default='bullhorn', help_text='Font Awesome 6 abbreviation for icon to use.', max_length=150, verbose_name='Font Awesome 6 icon')),
],
options={
'ordering': ('-since',),
},
),
]

View File

@ -0,0 +1,47 @@
from django.db import models
from django.db.models import Q
from django.utils import timezone
from tinymce.models import HTMLField
class AnnouncementManager(models.Manager):
"""Announcement Manager."""
def visible(self):
"""Get only visible announcements."""
return self.get_queryset().filter((Q(until__gt=timezone.now()) | Q(until=None)) & Q(since__lte=timezone.now()))
class Announcement(models.Model):
"""Announcement model."""
title = models.CharField(
max_length=100,
help_text="This is not shown on the announcement but can be used as an identifier in the admin area.",
)
content = HTMLField(blank=False, max_length=500)
since = models.DateTimeField(default=timezone.now)
until = models.DateTimeField(blank=True, null=True)
icon = models.CharField(
verbose_name="Font Awesome 6 icon",
help_text="Font Awesome 6 abbreviation for icon to use.",
max_length=150,
default="bullhorn",
)
objects = AnnouncementManager()
class Meta:
"""Meta class."""
ordering = ("-since",)
def __str__(self):
"""Cast this object to string."""
return self.title
@property
def is_visible(self):
"""Is this announcement currently visible."""
return (self.until is None or self.until > timezone.now()) and self.since <= timezone.now()

View File

@ -0,0 +1,33 @@
import json
import urllib.parse
from announcements.models import Announcement
def sanitize_closed_announcements(closed_announcements) -> list:
"""Convert a cookie (closed_announcements) to a list of id's of closed announcements."""
if closed_announcements is None or not isinstance(closed_announcements, str):
return []
try:
closed_announcements_list = json.loads(urllib.parse.unquote(closed_announcements))
except json.JSONDecodeError:
return []
if not isinstance(closed_announcements_list, list):
return []
closed_announcements_list_ints = []
for closed_announcement in closed_announcements_list:
if isinstance(closed_announcement, int):
closed_announcements_list_ints.append(closed_announcement)
return closed_announcements_list_ints
def validate_closed_announcements(closed_announcements) -> list:
"""Verify the integers in the list such that the ID's that in the database exist only remain."""
return list(Announcement.objects.filter(id__in=closed_announcements).values_list("id", flat=True))
def encode_closed_announcements(closed_announcements: list) -> str:
"""Encode the announcement list in URL encoding."""
return urllib.parse.quote(json.dumps(closed_announcements))

View File

@ -0,0 +1,34 @@
.announcement {
display: flex;
justify-content: center;
align-items: center;
background: var(--primary-shade);
color: var(--primary-contrast);
text-align: center;
padding: 0.5rem 1rem;
}
.announcement .btn-close {
color: var(--primary-contrast);
}
.announcement p {
display: inline;
margin: 0;
color: var(--primary-contrast);
font-size: 0.9rem;
font-weight: bold;
}
.announcement a {
color: var(--primary-contrast);
text-decoration: underline;
}
.announcement a:hover {
color: var(--primary-contrast-hover);
}
.announcement button {
background-size: .6rem;
}

View File

@ -0,0 +1,53 @@
const ANNOUNCEMENT_COOKIE = "closed-announcements";
function safeGetCookie() {
try {
return getCookie(ANNOUNCEMENT_COOKIE);
} catch {
return null;
}
}
function sanitizeAnnouncementsArray(closedAnnouncements) {
if (closedAnnouncements === null || typeof closedAnnouncements !== 'string') {
return [];
}
else {
try {
closedAnnouncements = JSON.parse(closedAnnouncements);
} catch {
return [];
}
if (! Array.isArray(closedAnnouncements)) {
closedAnnouncements = [];
}
for (let i = 0; i < closedAnnouncements.length; i++) {
if (!Number.isInteger(closedAnnouncements[i])) {
closedAnnouncements.splice(i, 1);
}
}
return closedAnnouncements;
}
}
function close_announcement(announcementId) {
let announcementElement = document.getElementById("announcement-" + announcementId);
if (announcementElement !== null) {
announcementElement.remove();
}
let closedAnnouncements = safeGetCookie();
if (closedAnnouncements === null) {
closedAnnouncements = [];
} else {
closedAnnouncements = sanitizeAnnouncementsArray(closedAnnouncements);
}
if (!closedAnnouncements.includes(announcementId)) {
closedAnnouncements.push(announcementId);
}
setListCookie(ANNOUNCEMENT_COOKIE, closedAnnouncements, 31);
}

View File

@ -0,0 +1,15 @@
{% load bleach_tags %}
{% for announcement in announcements %}
<div class="announcement" id="announcement-{{ announcement.id }}">
<i class="fas fa-{{ announcement.icon }} me-3"></i> {{ announcement.content|bleach }}
<button type="button" class="btn-close ms-3" aria-label="Close"
data-announcement-id="{{ announcement.pk }}" onclick="close_announcement({{ announcement.id }})"></button>
</div>
{% endfor %}
{% for announcement in app_announcements %}
<div class="announcement">
{{ announcement|bleach }}
</div>
{% endfor %}

View File

@ -0,0 +1,20 @@
from django import template
from django.db.models import Q
from announcements.models import Announcement
from announcements.services import sanitize_closed_announcements
register = template.Library()
@register.inclusion_tag("announcements/announcements.html", takes_context=True)
def render_announcements(context):
"""Render all active announcements."""
request = context.get("request")
closed_announcements = sanitize_closed_announcements(request.COOKIES.get("closed-announcements", None))
return {
"announcements": Announcement.objects.visible().filter(~Q(id__in=closed_announcements)),
"app_announcements": getattr(request, "_app_announcements", []),
"closed_announcements": closed_announcements,
}

View File

@ -0,0 +1,41 @@
from bleach.css_sanitizer import CSSSanitizer
from django import template
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe
from bleach import clean
register = template.Library()
@register.filter(is_safe=True)
@stringfilter
def bleach(value):
"""Bleach dangerous html from the input."""
css_sanitizer = CSSSanitizer(allowed_css_properties=["text-decoration"])
return mark_safe(
clean(
value,
tags=[
"h2",
"h3",
"p",
"a",
"div",
"strong",
"em",
"i",
"b",
"ul",
"li",
"br",
"ol",
"span",
],
attributes={
"*": ["class", "style"],
"a": ["href", "rel", "target", "title"],
},
css_sanitizer=css_sanitizer,
strip=True,
)
)

View File

View File

@ -0,0 +1,43 @@
from django.test import TestCase
from announcements import models
from announcements.services import sanitize_closed_announcements, validate_closed_announcements
class OrderServicesTests(TestCase):
def test_sanitize_closed_announcements_none(self):
self.assertEquals([], sanitize_closed_announcements(None))
def test_sanitize_closed_announcements_non_string(self):
self.assertEquals([], sanitize_closed_announcements(5))
def test_sanitize_closed_announcements_non_json(self):
self.assertEquals([], sanitize_closed_announcements("this,is,not,json"))
def test_sanitize_closed_announcements_non_list(self):
self.assertEquals([], sanitize_closed_announcements("{}"))
def test_sanitize_closed_announcements_list_of_ints(self):
self.assertEquals([1, 8, 4], sanitize_closed_announcements("[1, 8, 4]"))
def test_sanitize_closed_announcements_list_of_different_types(self):
self.assertEquals(
[1, 8, 4], sanitize_closed_announcements('[1, "bla", 8, 4, 123.4, {"a": "b"}, ["This is also a list"]]')
)
def test_validate_closed_announcements_all_exist(self):
announcement_1 = models.Announcement.objects.create(title="Announcement 1", content="blablabla")
announcement_2 = models.Announcement.objects.create(title="Announcement 2", content="blablabla")
announcement_3 = models.Announcement.objects.create(title="Announcement 3", content="blablabla")
self.assertEqual(
{announcement_1.id, announcement_2.id, announcement_3.id}, set(validate_closed_announcements([1, 2, 3]))
)
def test_validate_closed_announcements_some_do_not_exist(self):
announcement_1 = models.Announcement.objects.create(title="Announcement 1", content="blablabla")
announcement_2 = models.Announcement.objects.create(title="Announcement 2", content="blablabla")
announcement_3 = models.Announcement.objects.create(title="Announcement 3", content="blablabla")
self.assertEqual(
{announcement_1.id, announcement_2.id, announcement_3.id},
set(validate_closed_announcements([1, 2, 3, 4, 5, 6])),
)

View File

@ -3,7 +3,13 @@ import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "marietje.settings.settings")
try:
import marietje.settings.production # noqa
except ModuleNotFoundError:
# Use the development settings if the production settings are not available (so we're on a dev machine)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "marietje.settings.development")
else:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "marietje.settings.production")
try:
from django.core.management import execute_from_command_line
except ImportError:

View File

@ -1,14 +1,7 @@
"""
Django settings for marietje project.
"""
import os
from pathlib import Path
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
SECRET_KEY = 'sae2hahHao1soo0Ocoz5Ieh1Ushae6feJe4mooliooj0Ula8'
DEBUG = False
ALLOWED_HOSTS = ['*']
BASE_DIR = Path(__file__).resolve().parent.parent.parent
INSTALLED_APPS = [
'django.contrib.admin',
@ -20,6 +13,8 @@ INSTALLED_APPS = [
'django_bootstrap5',
'fontawesomefree',
'rest_framework',
'tinymce',
'announcements',
'marietje',
'queues',
'songs',
@ -64,18 +59,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'marietje.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'marietje',
'USER': 'marietje',
'PASSWORD': 'v8TzZwdAdSi7Tk5I',
'HOST': 'localhost',
'PORT': '3306',
'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
}
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
@ -147,12 +130,6 @@ OAUTH2_PROVIDER = {
},
}
# zc files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
BASE_URL = 'https://marietje-zuid.science.ru.nl'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = '/static/'
LOGIN_URL = '/login/'
@ -164,10 +141,8 @@ LOGOUT_REDIRECT_URL = '/'
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# App specific settings
BERTHA_HOST = ('bach.science.ru.nl', 1234)
MAIL_FROM = 'marietje@marietje.science.ru.nl'
MAX_MINUTES_IN_A_ROW = 45
# Time range (dependent on timezone specified) when MAX_MINUTES_IN_A_ROW is in effect.

View File

@ -0,0 +1,20 @@
import os
from .base import *
SECRET_KEY = 'sae2hahHao1soo0Ocoz5Ieh1Ushae6feJe4mooliooj0Ula8'
DEBUG = True
ALLOWED_HOSTS = ['*']
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
BERTHA_HOST = ('localhost', 1234)
BASE_URL = 'http://localhost:8000'

View File

@ -0,0 +1,23 @@
from .base import *
SECRET_KEY = '******'
DEBUG = False
ALLOWED_HOSTS = ['marietje-zuid.nl']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'marietje',
'USER': 'marietje',
'PASSWORD': '******',
'HOST': 'localhost',
'PORT': '3306',
'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
}
}
BASE_URL = 'https://marietje-zuid.science.ru.nl'
BERTHA_HOST = ('bach.science.ru.nl', 1234)

View File

@ -31,19 +31,6 @@ a {
color: var(--text-color);
}
.table {
color: var(--text-color);
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: var(--background-shade);
color: var(--text-color);
}
.table-striped > tbody > tr:nth-of-type(odd) > * {
color: var(--text-color);
}
input[type="text"], input[type="password"] {
background-color: var(--background-shade-light);
border: 1px solid var(--background-shade);
@ -66,22 +53,23 @@ button[type="button"] i {
min-width: 90px;
}
.song-info {
position: absolute;
padding: 8px;
background: silver;
white-space: nowrap;
z-index: 1;
}
#queue-time-header {
cursor: pointer;
}
footer {
text-align: center;
}
.table {
color: var(--text-color);
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: var(--background-shade);
color: var(--text-color);
}
.table-striped > tbody > tr:nth-of-type(odd) > * {
color: var(--text-color);
}
.marietjequeue {
color: #777777;
}
@ -91,7 +79,24 @@ footer {
}
.marietjequeue-pre-start td {
border-bottom: 3px double #777777;
border-bottom: 3px double var(--text-color);
}
.marietjequeue-post-start td {
border-top: 3px double var(--text-color);
}
.ownsong {
border-left: 1px solid var(--text-color);
}
.currentsong {
border-bottom: 1px solid var(--text-color);
font-weight: bold;
}
.underline_cell {
border-bottom: 1px solid var(--text-color);
}
.block-button {
@ -100,8 +105,16 @@ footer {
transition: 1s transform ease-in-out;
}
.currentsong{
border-bottom: 1px solid #DDDDDD;
.btn-link {
text-decoration: none;
}
.navbar-text {
color: var(--text-color);
}
.danger {
color: red !important;
}
/* Bootstrap 3 doesn't support equal height columns, hack via <https://medium.com/wdstack/bootstrap-equal-height-columns-d07bc934eb27#892f> */

View File

@ -1,4 +1,9 @@
:root {
--pimary: #0d6efd;
--primary-shade: #0d54bd;
--primary-contrast: #ffffff;
--primary-contrast-hover: rgba(255, 255, 255, 0.7);
--background-color: #ffffff;
--background-shade: #dddddd;
--background-shade-light: #f7f7f7;
@ -15,17 +20,19 @@
@media (prefers-color-scheme: dark) {
:root {
--background-color: #202020;
--background-shade: #404040;
--background-shade-light: #696969;
--background-color: #181818;
--background-shade: #282828;
--background-shade-light: #404040;
--card-background: #696969;
--card-background-shade: #404040;
--card-background-contrast: #ffffff;
--card-background: #404040;
--card-background-shade: #282828;
--card-background-contrast: #dddddd;
--title-color: #000000;
--sub-title-color: #dddddd;
--link-color: #007bff;
--text-color: #ffffff;
--text-color: #dddddd;
--bs-border-color: #282828;
}
}

View File

@ -36,4 +36,37 @@ Number.prototype.timestampToHHMMSS = function () {
seconds = '0' + seconds;
}
return hours + ':' + minutes + ':' + seconds;
};
};
function setCookie(name,value,days) {
let expires = "";
value = encodeURI(value);
if (days) {
let date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/; SameSite=Lax";
}
function getCookie(name) {
let nameEQ = name + "=";
let ca = document.cookie.split(';');
for(let i=0;i < ca.length;i++) {
let c = ca[i];
while (c.charAt(0)===' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length,c.length));
}
return null;
}
function setListCookie(name, list, days) {
try {
let string = JSON.stringify(list);
setCookie(name, string, days);
return true;
}
catch(error) {
return false;
}
}

View File

@ -1,4 +1,4 @@
{% load static django_bootstrap5 %}
{% load static django_bootstrap5 announcements %}
<!DOCTYPE html>
<html lang="en">
<head>
@ -15,17 +15,32 @@
<link href="{% static 'fontawesomefree/css/all.min.css' %}" rel="stylesheet" type="text/css">
<!-- Vue JS -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script>
const { createApp } = Vue;
</script>
<!-- TaTa.js notifications -->
<script src="{% static 'marietje/js/tata.js' %}"></script>
<!-- Base JavaScript -->
<script type="text/javascript" src="{% static "marietje/js/base.js" %}"></script>
<!-- Announcements static files -->
<link rel="stylesheet" href="{% static 'announcements/css/announcements.css' %}" type="text/css">
<script src="{% static 'announcements/js/announcements.js' %}"></script>
<script>
const CSRF_TOKEN = "{{ csrf_token }}";
</script>
</head>
<body>
<section id="announcements-alerts">
{% render_announcements %}
</section>
<nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand d-block d-lg-none" href="{% url "index" %}">Marietje 4.1</a>
<button class="navbar-toggler ms-auto" type="button" data-bs-toggle="offcanvas"
data-bs-target="#offcanvasNavbar" aria-controls="offcanvasNavbar">
<span class="navbar-toggler-icon"></span>
@ -109,9 +124,6 @@
</footer>
{% endif %}
{% bootstrap_javascript %}
<script>
const CSRF_TOKEN = "{{ csrf_token }}";
</script>
{% block js %}{% endblock %}
</body>
</html>

View File

@ -59,7 +59,13 @@
return response.json();
})
.then(json => {
updateScreen(json.current_song.user.name, json.current_song.song.artist, json.current_song.song.title);
let requestor;
if(json.current_song.user === null) {
requestor = "Marietje";
} else {
requestor = json.current_song.user.name;
}
updateScreen(requestor, json.current_song.song.artist, json.current_song.song.title);
setTimeout(fetchCurrentQueue, 1000);
}).catch(err => {
if(err.name == "NotLoggedIn") {

View File

@ -31,7 +31,7 @@ urlpatterns = [
path("register/", RegisterView.as_view(), name="register"),
path("activate/<int:user_id>/<str:token>/", ActivateView.as_view(), name="activate"),
path("forgotpassword/", ForgotPasswordView.as_view(), name="forgotpassword"),
path("resetpassword/<int:user_id>/<str:token>/", ResetPasswordView.as_view, name="resetpassword"),
path("resetpassword/<int:user_id>/<str:token>/", ResetPasswordView.as_view(), name="resetpassword"),
path("admin/", admin.site.urls),
path("privacy/", PrivacyView.as_view(), name="privacy"),
path("metrics/", metrics, name="metrics"),

View File

@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "marietje.settings.settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "marietje.settings.production")
application = get_wsgi_application()

View File

@ -163,7 +163,7 @@ class QueueRequestAPIView(CreateAPIView):
try:
playlist_song = queue.request(song, request.user)
except RequestException as e:
return Response(data={"success": False, "errorMessage": str(e)})
return Response(status=403, data={"success": False, "errorMessage": str(e)})
request_counter.labels(queue=queue.name).inc()
return Response(status=200, data=self.serializer_class(playlist_song).data)

View File

@ -5,7 +5,7 @@
{% block content %}
<nav class="navbar navbar-expand navbar-default navbar-light border-bottom">
<div class="container">
<div class="container-lg">
<ul class="nav nav-pills" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="queue-tab" data-bs-toggle="tab" data-bs-target="#queue"
@ -13,7 +13,7 @@
</button>
</li>
<li class="nav-item me-3" role="presentation">
<button class="nav-link" id="request-tab" data-bs-toggle="tab" data-bs-target="#request"
<button onclick="request_vue.select_textinput()" class="nav-link" id="request-tab" data-bs-toggle="tab" data-bs-target="#request"
type="button" role="tab" aria-controls="request" aria-selected="false">Request
</button>
</li>
@ -38,21 +38,36 @@
{% endif %}
</li>
</ul>
<ul class="navbar-nav navbar-right hidden-xs">
<li class="nav-item me-3">
<p class="navbar-text mb-0 start-queue hidden-sm hidden-xs"></p>
</li>
<li class="nav-item me-3">
<p class="navbar-text mb-0 end-queue"></p>
</li>
<li class="nav-item">
<p class="navbar-text mb-0 duration-queue"></p>
</li>
<ul id="personal-queue-container" class="navbar-nav navbar-right hidden-xs">
<template v-if="infobar !== null && 'start_personal_queue' in infobar && infobar.start_personal_queue !== null">
<li v-if="infobar.start_personal_queue !== 0" class="nav-item me-3">
<p v-if="infobar.plays_in" class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
First song starts in ${ infobar.start_personal_queue.secondsToMMSS() }$
</p>
<p v-else class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
First song starts at ${ (infobar.now_in_seconds + infobar.start_personal_queue).timestampToHHMMSS() }$
</p>
</li>
<li class="nav-item me-3">
<p v-if="infobar.plays_in" class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
Last song ends in ${ infobar.end_personal_queue.secondsToMMSS() }$
</p>
<p v-else class="navbar-text mb-0 start-queue hidden-sm hidden-xs">
Last song ends at ${ (infobar.now_in_seconds + infobar.end_personal_queue).timestampToHHMMSS() }$
</p>
</li>
<li class="nav-item">
<p class="navbar-text mb-0 duration-queue" v-bind:class="{danger: infobar.length_personal_queue > infobar.max_length * 60}">
(${ infobar.length_personal_queue.secondsToMMSS() }$)
</p>
</li>
</template>
</ul>
</div>
</nav>
<div class="container">
<br><br>
<div class="container-lg">
<div class="d-none d-lg-block"><br></div>
<div class="alert-location">
</div>
<div class="tab-content">
@ -60,53 +75,85 @@
<div id="queue-container">
<table class="table table-striped">
<thead>
<tr class="table-header-style">
<tr class="table-header-style underline_cell">
<td class="col-md-4">Artist</td>
<td class="col-md-4">Title</td>
<td class="col-md-2 d-sm-table-cell d-none">Requested By</td>
<td class="col-md-1 text-info d-sm-table-cell d-none" style="cursor: pointer;">
<span v-if="playsIn" id="timeswitch" class="btn btn-link p-0" v-on:click="playsIn = false">Plays In</span>
<span v-else class="btn btn-link p-0" v-on:click="playsIn = true">Plays at</span>
<span v-if="playsIn" class="btn btn-link p-0" v-on:click="playsIn = false">Plays In</span>
<span v-else class="btn btn-link p-0" v-on:click="playsIn = true">Plays At</span>
</td>
<td class="col-md-1">
<span class="control-icons">Control</span>
<span v-if="playsIn" class="btn btn-link p-0 d-sm-none" v-on:click="playsIn = false" >(Plays In)</span>
<span v-else class="btn btn-link p-0 d-sm-none" v-on:click="playsIn = true">(Plays At)</span>
</td>
<td class="col-md-1 control-icons">Control</td>
</tr>
</thead>
<tbody class="queuebody">
<template v-for="(song, index) in queue">
<tr :class="{ marietjequeue: (song.user === null), currentsong: (index === 0), 'fw-bold': (index === 0) }">
<td class="artist"><% song.song.artist %></td>
<td class="title"><% song.song.title %></td>
<tr :class="{ marietjequeue: (song.user === null),
underline_cell: (index === queue[-1]),
currentsong: (index === 0),
ownsong: (this.user_data.id === song.user?.id && index !== 0),
}"
v-on:click="toggle_details(song)">
<td>
<span class="artist">${ song.song.artist }$</span>
<span v-if="show_details(song)" class="requested-by d-sm-none d-block small mt-3 fw-normal">
Requested By:<br>
<template v-if="song.user === null">
Marietje
</template>
<template v-else>
${ song.user.name }$
</template>
</span>
</td>
<td>
<span class="title">${ song.song.title }$</span>
<span v-if="show_details(song) && song.time_until_song_seconds > 0" class="plays-at d-sm-none d-block small mt-3 fw-normal" style="text-align: right">
<span v-if="playsIn">Plays In:</span>
<span v-else>Plays At:</span>
<br>
<template v-if="song.time_until_song_seconds !== null && song.time_until_song_seconds > 0 && playsIn === true">
${ song.time_until_song_seconds.secondsToMMSS() }$
</template>
<template v-else-if="playsIn === false && song.plays_at !== null && song.played === false">
${ song.plays_at.timestampToHHMMSS() }$
</template>
</span>
</td>
<td class="d-sm-table-cell d-none requested-by">
<template v-if="song.user === null">
Marietje
</template>
<template v-else>
<% song.user.name %>
${ song.user.name }$
</template>
</td>
<td class="d-sm-table-cell d-none plays-at" style="text-align: right">
<template v-if="song.time_until_song_seconds !== null && song.time_until_song_seconds > 0 && playsIn === true">
<% song.time_until_song_seconds.secondsToMMSS() %>
${ song.time_until_song_seconds.secondsToMMSS() }$
</template>
<template v-else-if="playsIn === false && song.plays_at !== null && song.played === false">
<% song.plays_at.timestampToHHMMSS() %>
${ song.plays_at.timestampToHHMMSS() }$
</template>
</td>
<td>
<div class="d-flex flex-column">
<div class="d-flex flex-row">
<button v-if="song.can_move_up" v-on:click="move_down(queue[index-1].id)" class="btn btn-link"><i
class="fa-solid fa-arrow-up"></i></button>
<button v-else class="btn btn-link invisible"><i class="fa-solid fa-arrow-up"></i></button>
<button v-if="song.can_move_up" v-on:click="move_down(queue[index-1].id)"
class="btn btn-link p-1 p-md-2"><i class="fa-solid fa-arrow-up"></i></button>
<button v-else class="btn btn-link invisible p-1 p-md-2"><i class="fa-solid fa-arrow-up"></i></button>
<button v-if="song.can_move_down" v-on:click="move_down(song.id)" class="btn btn-link"><i
class="fa-solid fa-arrow-down"></i></button>
<button v-else class="btn btn-link invisible"><i class="fa-solid fa-arrow-down"></i></button>
</div>
<div class="d-flex flex-row">
<button v-if="song.can_delete" v-on:click="cancel_song(song.id)" class="btn btn-link"><i
class="fa-solid fa-trash-can"></i></button>
<button v-else class="btn btn-link invisible"><i class="fa-solid fa-trash-can"></i></button>
<button v-if="song.can_move_down" v-on:click="move_down(song.id)"
class="btn btn-link p-1 p-md-2"><i class="fa-solid fa-arrow-down"></i></button>
<button v-else class="btn btn-link invisible p-1 p-md-2"><i class="fa-solid fa-arrow-down"></i></button>
<button v-if="song.can_delete" v-on:click="cancel_song(song.id)"
class="btn btn-link p-1 p-md-2"><i class="fa-solid fa-trash-can"></i></button>
<button v-else class="btn btn-link invisible p-1 p-md-2"><i class="fa-solid fa-trash-can"></i></button>
</div>
</div>
</td>
@ -117,7 +164,7 @@
</div>
</div>
<div class="tab-pane fade" id="request" role="tabpanel" aria-labelledby="request-tab">
<div id="request-container">
<div id="request-container" class="table-responsive">
<table id="request-table" class="table table-striped">
<thead>
<tr>
@ -128,7 +175,7 @@
<th>Report</th>
</tr>
<tr>
<th colspan="5"><input id="search-all" class="search-input" type="text"
<th colspan="5"><input id="search-all" class="search-input" type="text" ref="search_textinput"
v-model="search_input"/></th>
</tr>
</thead>
@ -165,7 +212,7 @@
</select>
<select class="pagenum input-mini" title="Select page number" v-model="page_number">
<template v-for="(i, index) in number_of_pages">
<option :value="i"><% i %></option>
<option :value="i">${ i }$</option>
</template>
</select>
</th>
@ -176,26 +223,26 @@
<template v-for="(song, index) in songs">
<tr>
<td>
<% song.artist %>
${ song.artist }$
</td>
<td>
<a href="#" v-on:click="request_song(song.id);"><% song.title %></a>
<button v-on:click="request_song(song.id);" class="btn btn-link p-0" style="text-align: left">${ song.title }$</button>
</td>
<td>
<template v-if="song.user === null">
Marietje
</template>
<template v-else>
<% song.user.name %>
${ song.user.name }$
</template>
</td>
<td>
<% song.duration.secondsToMMSS() %>
${ song.duration.secondsToMMSS() }$
</td>
<td>
<a href="#" v-on:click="report_song(song.id);">
<button v-on:click="report_song(song.id);" class="btn btn-link p-0">
</a>
</button>
</td>
</tr>
</template>
@ -214,21 +261,41 @@
const CAN_MOVE = {{ perms.queues.can_move|yesno:"1,0" }};
</script>
<script>
const queue_vue = new Vue({
el: '#queue-container',
delimiters: ['<%', '%>'],
data: {
current_song: null,
queue: [],
user_data: null,
refreshing: true,
refreshTimer: null,
clockInterval: null,
started_at: null,
playsIn: true,
const personal_queue_vue = createApp({
delimiters: ['${', '}$'],
data() {
return {
infobar: null,
}
},
}).mount('#personal-queue-container');
const queue_vue = createApp({
delimiters: ['${', '}$'],
data() {
return {
current_song: null,
queue: [],
user_data: null,
refreshing: true,
refreshTimer: null,
clockInterval: null,
started_at: null,
playsIn: true,
songs_show_details_on_mobile: [],
}
},
watch: {
playsIn: {
handler(val, oldVal) {
setCookie("PLAYS_IN", this.playsIn, 14);
}
},
},
mounted() {
this.clockInterval = setInterval(this.update_song_times, 1000);
const stored_playsIn = getCookie("PLAYS_IN");
this.playsIn = (stored_playsIn !== "false");
},
unmounted() {
clearInterval(this.clockInterval);
@ -252,27 +319,6 @@
});
},
computed: {
infoBar() {
let infoBar = {
start_personal_queue: 0,
length_personal_queue: 0,
length_total_queue: 0,
end_personal_queue: 0,
max_length: 45,
}
for (let i = 0; i < this.queue.length; i++) {
const current_song = this.queue[i];
infoBar['length_total_queue'] = infoBar['length_total_queue'] + current_song.song.duration;
if (current_song.user !== null && current_song.user.id === this.user_data.id) {
infoBar['length_personal_queue'] = infoBar['length_personal_queue'] + current_song.song.duration;
infoBar['end_personal_queue'] = infoBar['length_total_queue'];
if (infoBar['start_personal_queue'] === 0) {
infoBar['start_personal_queue'] = infoBar['length_total_queue'] - current_song.song.duration;
}
}
}
return infoBar;
},
play_next_song_at() {
if (this.started_at !== null && this.current_song !== null) {
return this.started_at + this.current_song.song.duration;
@ -300,6 +346,42 @@
}
}
}
this.update_infobar();
},
update_infobar() {
let infoBar = {
start_personal_queue: null,
length_personal_queue: 0,
length_total_queue: 0,
end_personal_queue: 0,
max_length: 45,
plays_in: this.playsIn,
now_in_seconds: 0,
}
infoBar.now_in_seconds = Math.round((new Date()).getTime() / 1000);
// If the current song is the current user's, their queue has started.
if (this.queue[0].user?.id === this.user_data.id) {
infoBar.start_personal_queue = 0;
}
for (let i = 0; i < this.queue.length; i++) {
const current_song = this.queue[i];
if (i === 0) {
const current_song_remaining_seconds = current_song.song.duration - this.queue[1].time_until_song_seconds;
if (current_song.user !== null && current_song.user.id === this.user_data.id) {
infoBar['length_personal_queue'] -= current_song_remaining_seconds;
}
infoBar['length_total_queue'] -= current_song_remaining_seconds;
}
infoBar['length_total_queue'] += current_song.song.duration;
if (current_song.user !== null && current_song.user.id === this.user_data.id) {
infoBar['length_personal_queue'] += current_song.song.duration;
infoBar['end_personal_queue'] = infoBar['length_total_queue'];
if (infoBar['start_personal_queue'] === null) {
infoBar['start_personal_queue'] = infoBar['length_total_queue'] - current_song.song.duration;
}
}
}
personal_queue_vue.infobar = infoBar;
},
refresh() {
if (!this.refreshing) {
@ -383,21 +465,35 @@
}).finally(() => {
this.refresh();
});
}
},
show_details(song) {
return this.songs_show_details_on_mobile.includes(song.id);
},
toggle_details(song) {
if (!this.show_details(song)) {
this.songs_show_details_on_mobile.push(song.id);
} else {
// Deze filter is gehaat door Kees, gemaakt door Olaf. Bedankt, Olaf. Duurde wel even.
this.songs_show_details_on_mobile = this.songs_show_details_on_mobile.filter(
value => value !== song.id
);
}
},
}
});
}).mount("#queue-container");
</script>
<script>
const request_vue = new Vue({
el: '#request-container',
delimiters: ['<%', '%>'],
data: {
songs: [],
total_songs: 0,
search_input: "",
typing_timer: null,
page_size: 10,
page_number: 1,
const request_vue = createApp({
delimiters: ['${', '}$'],
data() {
return {
songs: [],
total_songs: 0,
search_input: "",
typing_timer: null,
page_size: 10,
page_number: 1,
}
},
watch: {
search_input: {
@ -425,6 +521,7 @@
this.page_size = 10;
}
this.page_number = 1;
setCookie("REQUEST_PAGE_SIZE", this.page_size, 14);
this.search();
}
}
@ -436,7 +533,7 @@
},
created() {
fetch(
`/api/v1/songs/?ordering=title&limit=${this.page_size}&offset=${this.page_size * (this.page_number - 1)}`
`/api/v1/songs/?ordering=artist,title&limit=${this.page_size}&offset=${this.page_size * (this.page_number - 1)}`
).then(response => {
if (response.status === 200) {
return response.json();
@ -455,11 +552,15 @@
tata.error("", "An unknown error occurred, please try again.")
}
});
const stored_page_size = parseInt(getCookie("REQUEST_PAGE_SIZE"));
if (stored_page_size !== Number.NaN && stored_page_size > 0) {
this.page_size = stored_page_size;
}
},
methods: {
search() {
fetch(
`/api/v1/songs/?ordering=title&limit=${this.page_size}&offset=${this.page_size * (this.page_number - 1)}&search=${this.search_input}`,
`/api/v1/songs/?ordering=artist,title&limit=${this.page_size}&offset=${this.page_size * (this.page_number - 1)}&search=${this.search_input}`,
{
headers: {
"X-CSRFToken": CSRF_TOKEN,
@ -484,6 +585,7 @@
}
});
},
request_song(song_id) {
fetch('/api/v1/queues/current/request/', {
method: 'POST',
@ -514,6 +616,7 @@
}
});
},
report_song(song_id) {
let message = prompt("What is wrong with the song?");
if (message === null) {
@ -551,11 +654,16 @@
}
});
},
update_page(page_number) {
this.page_number = page_number;
}
},
select_textinput() {
this.$refs.search_textinput.select();
},
}
});
}).mount('#request-container');
</script>
<script>
function volume_down() {

View File

@ -1,3 +1,4 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.generics import ListAPIView, RetrieveAPIView, CreateAPIView
from rest_framework import filters
from rest_framework.views import APIView
@ -16,7 +17,8 @@ class SongsListAPIView(ListAPIView):
queryset = Song.objects.all()
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {"GET": ["read"]}
filter_backends = (filters.SearchFilter, filters.OrderingFilter)
filter_backends = (filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend)
filterset_fields = ["user__username", "artist"]
search_fields = ["artist", "title", "user__name", "user__username"]
ordering_fields = [
"artist",
@ -84,7 +86,7 @@ class SongUploadAPIView(APIView):
song = upload_file(file, artist, title, request.user)
upload_counter.inc()
return Response(status=200, data=self.serializer_class(song).data)
except UploadException:
except (UploadException, ConnectionRefusedError):
return Response(
status=500,
data={

View File

@ -4,7 +4,7 @@
{% block title %}Manage{% endblock %}
{% block content %}
<div class="container">
<div class="container-lg">
<div class="table-responsive mt-5">
<table id="request-table" class="table table-striped">
<thead>
@ -41,7 +41,7 @@
</select>
<select class="pagenum input-mini" title="Select page number" v-model="page_number">
<template v-for="(i, index) in number_of_pages">
<option :value="i"><% i %></option>
<option :value="i">${ i }$</option>
</template>
</select>
</th>
@ -52,10 +52,10 @@
<template v-for="(song, index) in songs">
<tr>
<td>
<% song.artist %>
${ song.artist }$
</td>
<td>
<a :href="'/songs/edit/' + song.id + '/'" v-on:click="request_song(song.id);"><% song.title %></a>
<a :href="'/songs/edit/' + song.id + '/'" v-on:click="request_song(song.id);">${ song.title }$</a>
</td>
</tr>
</template>
@ -64,16 +64,18 @@
</div>
</div>
<script>
let manage_vue = new Vue({
el: '#request-table',
delimiters: ['<%', '%>'],
data: {
songs: [],
total_songs: 0,
search_input: "",
typing_timer: null,
page_size: 10,
page_number: 1,
let manage_vue = createApp({
delimiters: ['${', '}$'],
data() {
return {
songs: [],
total_songs: 0,
search_input: "",
typing_timer: null,
page_size: 10,
page_number: 1,
user_data: null,
}
},
watch: {
search_input: {
@ -101,6 +103,7 @@
this.page_size = 10;
}
this.page_number = 1;
setCookie("MANAGE_PAGE_SIZE", this.page_size, 14);
this.refresh();
}
}
@ -111,26 +114,23 @@
}
},
created() {
fetch(
`/api/v1/songs/?ordering=title&limit=${this.page_size}&offset=${this.page_size * (this.page_number - 1)}`
).then(response => {
fetch('/api/v1/users/me/').then(response => {
if (response.status === 200) {
return response.json();
} else {
throw response;
}
}).then(data => {
this.songs = data.results;
this.total_songs = data.count;
}).catch((e) => {
if (e instanceof Response) {
e.json().then(data => {
tata.error("", data.errorMessage);
});
} else {
tata.error("", "An unknown error occurred, please try again.")
}
this.user_data = data;
}).then(() => {
this.refresh();
}).catch(() => {
tata.error('', 'User details failed to fetch, please reload this page to try again.');
});
const stored_page_size = parseInt(getCookie("MANAGE_PAGE_SIZE"));
if (stored_page_size !== Number.NaN && stored_page_size > 0) {
this.page_size = stored_page_size;
}
},
methods: {
search() {
@ -139,7 +139,7 @@
},
refresh() {
fetch(
`/api/v1/songs/?ordering=title&limit=${this.page_size}&offset=${this.page_size * (this.page_number - 1)}&search=${this.search_input}`,
`/api/v1/songs/?ordering=artist,title&user__username=${this.user_data.username}&limit=${this.page_size}&offset=${this.page_size * (this.page_number - 1)}&search=${this.search_input}`,
{
headers: {
"X-CSRFToken": CSRF_TOKEN,
@ -168,6 +168,6 @@
this.page_number = page_number;
}
}
});
}).mount('#request-table');
</script>
{% endblock %}

View File

@ -16,9 +16,12 @@
{% csrf_token %}
<div class="fileupload fileupload-new" data-provides="fileupload">
<span class="btn btn-primary btn-file">
<span v-if="fileObjects.length === 0">
<span v-if="fileObjects.length === 0 && !files_loading">
Select files
</span>
<span v-else-if="files_loading">
Loading new files...
</span>
<span v-else>
Change
</span>
@ -29,26 +32,35 @@
<div class="songs">
<div v-for="fileObject in fileObjects" class="song-container card mb-3">
<div class="card-header">
<h3><% fileObject.name %></h3>
<h3>${ fileObject.name }$</h3>
</div>
<div class="card-body">
<div class="form-group mb-3">
<div v-if="fileObject.artist === '' || fileObject.artist === null" class="alert alert-danger">Please enter an artist for this song.</div>
<input v-if="upload_in_progress || uploaded" type="text" name="artist[]" class="form-control input-sm artist" disabled
<div v-if="fileObject.artist === '' || fileObject.artist === null"
class="alert alert-danger">Please enter an artist for this song.
</div>
<input v-if="upload_in_progress || uploaded" type="text" name="artist[]"
class="form-control input-sm artist" disabled
placeholder="Artist" v-model="fileObject.artist"/>
<input v-else type="text" name="artist[]" class="form-control input-sm artist"
<input v-else type="text" name="artist[]"
class="form-control input-sm artist"
placeholder="Artist" v-model="fileObject.artist"/>
</div>
<div class="form-group mb-3">
<div v-if="fileObject.title === '' || fileObject.title === null" class="alert alert-danger">Please enter a title for this song.</div>
<input v-if="upload_in_progress || uploaded" type="text" name="title[]" class="form-control input-sm title" disabled
<div v-if="fileObject.title === '' || fileObject.title === null"
class="alert alert-danger">Please enter a title for this song.
</div>
<input v-if="upload_in_progress || uploaded" type="text" name="title[]"
class="form-control input-sm title" disabled
placeholder="Title" v-model="fileObject.title"/>
<input v-else type="text" name="title[]" class="form-control input-sm title"
placeholder="Title" v-model="fileObject.title"/>
</div>
<template v-if="fileObject.upload_finished === true">
<div v-if="fileObject.success === true" class="alert alert-success">Upload finished successfully.</div>
<div v-else class="alert alert-danger"><% fileObject.error_message %></div>
<div v-if="fileObject.success === true" class="alert alert-success">Upload
finished successfully.
</div>
<div v-else class="alert alert-danger">${ fileObject.error_message }$</div>
</template>
</div>
</div>
@ -56,14 +68,20 @@
</div>
<div class="card-footer">
<div class="progress mt-2 mb-3">
<div :class="{ 'progress-bar-animated': (upload_in_progress), 'bg-success': (uploaded && everything_successfully_uploaded), 'bg-danger': (uploaded && !everything_successfully_uploaded) }" class="progress-bar progress-bar-striped" role="progressbar" :style="{ width: (progress_bar_width + '%') }" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"></div>
<div :class="{ 'progress-bar-animated': (upload_in_progress), 'bg-success': (uploaded && everything_successfully_uploaded), 'bg-danger': (uploaded && !everything_successfully_uploaded) }"
class="progress-bar progress-bar-striped" role="progressbar"
:style="{ width: (progress_bar_width + '%') }" aria-valuenow="50" aria-valuemin="0"
aria-valuemax="100"></div>
</div>
<template v-if="upload_in_progress || uploaded">
<button v-if="uploaded" class="btn btn-primary btn-block w-100" v-on:click="clear">Clear</button>
<button v-if="uploaded" class="btn btn-primary btn-block w-100" v-on:click="clear">
Clear
</button>
<button v-else class="btn btn-primary btn-block w-100 disabled">Clear</button>
</template>
<template v-else>
<input v-if="ready_for_upload" id="upload" class="btn btn-primary btn-block w-100" type="submit" value="Upload" v-on:click="upload"/>
<input v-if="ready_for_upload" id="upload" class="btn btn-primary btn-block w-100"
type="submit" value="Upload" v-on:click="upload"/>
<button v-else class="btn btn-primary btn-block w-100 disabled">Upload</button>
</template>
</div>
@ -73,20 +91,21 @@
</div>
</div>
<link rel="stylesheet" href="{% static 'songs/css/upload.css' %}"/>
<script type="module">
import * as id3 from '//unpkg.com/id3js@^2/lib/id3.js';
let upload_vue = new Vue({
el: '#uploadform',
delimiters: ['<%', '%>'],
data: {
files: [],
fileObjects: [],
uploaded: false,
upload_in_progress: false,
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsmediatags/3.9.5/jsmediatags.min.js"></script>
<script>
let upload_vue = createApp({
delimiters: ['${', '}$'],
data() {
return {
files: [],
fileObjects: [],
uploaded: false,
upload_in_progress: false,
files_loading: false,
}
},
computed: {
ready_for_upload: function() {
ready_for_upload: function () {
if (this.uploaded !== false || this.upload_in_progress !== false || this.fileObjects.length === 0) {
return false;
} else {
@ -98,14 +117,14 @@
return true;
}
},
everything_successfully_uploaded: function() {
everything_successfully_uploaded: function () {
return this.fileObjects.map((fileObject) => {
return fileObject.upload_finished === true && fileObject.success === true;
}).reduce((previousValue, currentValue) => {
return previousValue && currentValue;
}, true);
},
progress_bar_width: function() {
progress_bar_width: function () {
if (this.fileObjects.length === 0) {
return 0;
}
@ -158,14 +177,21 @@
}).then(() => {
this.fileObjects[i].success = true;
}).catch(e => {
console.log(e);
if (e instanceof Response) {
e.json().then(data => {
this.fileObjects.error_message = data.errorMessage;
this.fileObjects.success = false;
});
// Intercept an HTTP 413 error (entity too large), as this might
// be thrown by a (reverse) proxy server and its payload is not
// nicely formatted as the code below expects.
if (e.status === 413) {
this.fileObjects[i].error_message = "The song you tried to upload is too large in size. (HTTP 413 error)";
this.fileObjects[i].success = false;
} else {
this.fileObjects[i].error_message = `${e.statusText} (${e.status})`;
this.fileObjects[i].success = false;
}
} else {
this.fileObjects.error_message = "An exception occurred while uploading this file, please try again.";
this.fileObjects.success = false;
this.fileObjects[i].error_message = "An exception occurred while uploading this file, please try again.";
this.fileObjects[i].success = false;
}
}).finally(() => {
this.fileObjects[i].upload_finished = true;
@ -177,23 +203,25 @@
});
},
async set_new_files(event) {
this.files_loading = true;
this.uploaded = false;
this.upload_in_progress = false;
this.files = event.target.files;
let newFileObjects = [];
for (let i = 0; i < this.files.length; i++) {
try {
const tags = await this.parseSong(this.files[i]);
await this.parseSong(this.files[i]).then((song) => {
newFileObjects.push(
{
"file": this.files[i],
"name": this.files[i].name,
"artist": tags.artist,
"title": tags.title,
"artist": song.artist,
"title": song.title,
"success": null,
"error_message": null,
"upload_finished": false,
}
);
} catch {
}).catch(() => {
newFileObjects.push(
{
"file": this.files[i],
@ -205,14 +233,28 @@
"upload_finished": false,
}
)
}
});
}
this.fileObjects = newFileObjects;
this.files_loading = false;
},
parseSong(file) {
return id3.fromFile(file);
async parseSong(file) {
let jsMediaTags = window.jsmediatags;
const tags = await new Promise((resolve, reject) => {
jsMediaTags.read(file, {
onSuccess: function (tag) {
resolve(tag);
},
onError: function (error) {
reject(error);
}
});
});
return tags.tags;
}
}
});
}).mount('#uploadform');
</script>
{% endblock %}

View File

@ -28,6 +28,7 @@
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Songs</th>
<th></th>
</tr>
</thead>
<tbody>
@ -54,6 +55,7 @@
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Requests</th>
<th></th>
</tr>
</thead>
<tbody>
@ -109,6 +111,7 @@
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Unique</th>
<th></th>
</tr>
</thead>
<tbody>
@ -134,7 +137,7 @@
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
<th style="white-space: nowrap; text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
@ -143,7 +146,7 @@
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td>{{ stat.total }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
@ -151,7 +154,7 @@
</div>
</div>
<div class="col-md-6">
<h2>Most played Artists</h2>
<h2>Most played artists</h2>
<p>These are the {{ stats.stats_top_count }} most played artists ever.</p>
<div class="table-responsive">
<table class="table table-striped">
@ -176,7 +179,9 @@
</div>
<div class="col-md-6">
<h2>Most played uploaders</h2>
<p>These are the {{ stats.stats_top_count }} people whose songs are requested most often by other people, as shown in the left column. The right column shows how many times that person has queued his own songs.</p>
<p>These are the {{ stats.stats_top_count }} people whose songs are requested most often by other
people, as shown in the left column. The right column shows how many times that person has queued
their own songs.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@ -202,7 +207,7 @@
</div>
<div class="col-md-6">
<h2>Most played songs last 14 days</h2>
<p>These songs are played the {{ stats.stats_top_count }} most in the last two weeks.</p>
<p>These {{ stats.stats_top_count }} songs have been requested the most in the last two weeks.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@ -210,7 +215,7 @@
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
<th style="white-space: nowrap; text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
@ -219,7 +224,7 @@
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td>{{ stat.total }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>

View File

@ -21,9 +21,10 @@
{% endif %}
<div class="col-md-6">
<h2>Most played songs</h2>
<p>You have requested <strong> {{ stats.unique_requests }} </strong> different
songs a total of <strong> {{ stats.total_requests }} </strong> times. This
means <strong> {% widthratio stats.unique_requests stats.total_requests 100 %}% </strong> of your requests have been unique. </p>
<p>You have requested <strong> {{ stats.unique_requests }} </strong> different songs a total of
<strong> {{ stats.total_requests }} </strong> times. This means
<strong> {% widthratio stats.unique_requests stats.total_requests 100 %}% </strong> of your requests
have been unique. These are the song you have requested the most.</p>
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
@ -32,7 +33,7 @@
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
<th style="white-space: nowrap; text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
@ -41,7 +42,7 @@
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td style="text-align: middle;">{{ stat.total }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
@ -49,7 +50,8 @@
</div>
</div>
<div class="col-md-6">
<h2>Most played Artists</h2>
<h2>Most played artists</h2>
<p>These are the artists you have requested the most.</p>
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
@ -57,7 +59,7 @@
<tr>
<th>#</th>
<th>Artist</th>
<th style="text-align: right;"># Requests</th>
<th style="white-space:nowrap; text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
@ -74,11 +76,11 @@
</div>
<div class="col-md-6">
<h2>Uploads requested</h2>
<p> You have uploaded a total of <strong> {{stats.total_uploads }} </strong> songs. The left column
shows how many times these have been requested by other people. The right column shows
how many times you requested your own songs. In total your songs
have been queued <strong> {{stats.total_played_uploads }} </strong> times by others and
<strong> {{stats.total_played_user_uploads }} </strong> by yourself.
<p>You have uploaded a total of <strong> {{stats.total_uploads }} </strong> songs. The left column
shows how many times these have been requested by other people. The right column shows how many times
you requested your own songs. In total your songs have been queued
<strong> {{stats.total_played_uploads }} </strong> times by others and
<strong> {{stats.total_played_user_uploads }} </strong> times by yourself.</p>
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
@ -87,8 +89,8 @@
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th style="text-align: right;">Others</th>
<th>You</th>
<th style="white-space:nowrap; text-align: right;"># Others</th>
<th style="white-space:nowrap;"># You</th>
</tr>
</thead>
<tbody>
@ -107,8 +109,8 @@
</div>
<div class="col-md-6">
<h2>Upload artists requested</h2>
<p> The left column shows how many times songs from artists uploaded by you have been requested by
other people. The right column shows how many times you requested those songs.
<p>The left column shows how many times songs from artists uploaded by you have been requested by
other people. The right column shows how many times you requested those songs.</p>
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
@ -116,8 +118,8 @@
<tr>
<th>#</th>
<th>Artist</th>
<th style="text-align: right;">Others</th>
<th>You</th>
<th style="white-space:nowrap; text-align: right;"># Others</th>
<th style="white-space:nowrap;"># You</th>
</tr>
</thead>
<tbody>
@ -135,14 +137,15 @@
</div>
<div class="col-md-6">
<h2>Most played uploaders</h2>
<p> The people whose songs you have queued the most are:</p>
<p>These are the people whose songs you have requested the most.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Uploader</th>
<th style="text-align: right;"># Requests</th>
<th style="white-space:nowrap; text-align: right;"># Requests</th>
<th></th>
</tr>
</thead>
<tbody>
@ -160,14 +163,14 @@
</div>
<div class="col-md-6">
<h2>Biggest fans</h2>
<p> The people that queued your songs the most are:</p>
<p>These are the people that have requested your songs the most.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Requests</th>
<th style="white-space:nowrap; text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>

704
poetry.lock generated
View File

@ -76,33 +76,33 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
[[package]]
name = "black"
version = "23.9.1"
version = "23.12.1"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.8"
files = [
{file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"},
{file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"},
{file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"},
{file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"},
{file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"},
{file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"},
{file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"},
{file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"},
{file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"},
{file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"},
{file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"},
{file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"},
{file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"},
{file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"},
{file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"},
{file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"},
{file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"},
{file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"},
{file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"},
{file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"},
{file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"},
{file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"},
{file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"},
{file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"},
{file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"},
{file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"},
{file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"},
{file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"},
{file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"},
{file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"},
{file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"},
{file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"},
{file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"},
{file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"},
{file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"},
{file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"},
{file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"},
{file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"},
{file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"},
{file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"},
{file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"},
{file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"},
{file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"},
{file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"},
]
[package.dependencies]
@ -116,92 +116,100 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "bleach"
version = "5.0.1"
description = "An easy safelist-based HTML-sanitizing tool."
optional = false
python-versions = ">=3.7"
files = [
{file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"},
{file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"},
]
[package.dependencies]
six = ">=1.9.0"
tinycss2 = {version = ">=1.1.0,<1.2", optional = true, markers = "extra == \"css\""}
webencodings = "*"
[package.extras]
css = ["tinycss2 (>=1.1.0,<1.2)"]
dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"]
[[package]]
name = "certifi"
version = "2023.7.22"
version = "2024.2.2"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
]
[[package]]
name = "cffi"
version = "1.15.1"
version = "1.16.0"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = "*"
python-versions = ">=3.8"
files = [
{file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
{file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
{file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
{file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
{file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
{file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
{file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
{file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
{file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
{file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
{file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
{file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
{file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
{file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
{file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
{file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
{file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
{file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
{file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
{file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
{file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
{file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
{file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
{file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
{file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
{file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
{file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
{file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
{file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
{file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
{file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
{file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
{file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
{file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
{file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
{file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
{file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
{file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
{file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
{file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
{file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
{file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
{file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
{file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
{file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
{file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
{file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
{file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
{file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
{file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
{file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
{file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
{file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
{file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
{file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
{file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
{file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
{file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
{file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
{file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
{file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
{file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
{file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
{file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
]
[package.dependencies]
@ -209,86 +217,101 @@ pycparser = "*"
[[package]]
name = "charset-normalizer"
version = "3.2.0"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"},
{file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"},
{file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"},
{file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"},
{file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"},
{file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"},
{file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"},
{file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"},
{file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"},
{file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"},
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
@ -318,75 +341,67 @@ files = [
[[package]]
name = "cryptography"
version = "41.0.3"
version = "42.0.5"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = ">=3.7"
files = [
{file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507"},
{file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922"},
{file = "cryptography-41.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81"},
{file = "cryptography-41.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd"},
{file = "cryptography-41.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47"},
{file = "cryptography-41.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116"},
{file = "cryptography-41.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c"},
{file = "cryptography-41.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae"},
{file = "cryptography-41.0.3-cp37-abi3-win32.whl", hash = "sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306"},
{file = "cryptography-41.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574"},
{file = "cryptography-41.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087"},
{file = "cryptography-41.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858"},
{file = "cryptography-41.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906"},
{file = "cryptography-41.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e"},
{file = "cryptography-41.0.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd"},
{file = "cryptography-41.0.3-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207"},
{file = "cryptography-41.0.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84"},
{file = "cryptography-41.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7"},
{file = "cryptography-41.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d"},
{file = "cryptography-41.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de"},
{file = "cryptography-41.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1"},
{file = "cryptography-41.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4"},
{file = "cryptography-41.0.3.tar.gz", hash = "sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34"},
{file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"},
{file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"},
{file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"},
{file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"},
{file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"},
{file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"},
{file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"},
{file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"},
{file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"},
{file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"},
{file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"},
{file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"},
{file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"},
{file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"},
{file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"},
{file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"},
{file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"},
{file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"},
{file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"},
{file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"},
{file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"},
{file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"},
{file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"},
{file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"},
{file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"},
{file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"},
{file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"},
{file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"},
{file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"},
{file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"},
{file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"},
{file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"},
]
[package.dependencies]
cffi = ">=1.12"
cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
[package.extras]
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
nox = ["nox"]
pep8test = ["black", "check-sdist", "mypy", "ruff"]
pep8test = ["check-sdist", "click", "mypy", "ruff"]
sdist = ["build"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"]
[[package]]
name = "deprecated"
version = "1.2.14"
description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"},
{file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"},
]
[package.dependencies]
wrapt = ">=1.10,<2"
[package.extras]
dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
[[package]]
name = "django"
version = "4.2.5"
version = "4.2.10"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.8"
files = [
{file = "Django-4.2.5-py3-none-any.whl", hash = "sha256:b6b2b5cae821077f137dc4dade696a1c2aa292f892eca28fa8d7bfdf2608ddd4"},
{file = "Django-4.2.5.tar.gz", hash = "sha256:5e5c1c9548ffb7796b4a8a4782e9a2e5a3df3615259fc1bfd3ebc73b646146c1"},
{file = "Django-4.2.10-py3-none-any.whl", hash = "sha256:a2d4c4d4ea0b6f0895acde632071aff6400bfc331228fc978b05452a0ff3e9f1"},
{file = "Django-4.2.10.tar.gz", hash = "sha256:b1260ed381b10a11753c73444408e19869f3241fc45c985cd55a30177c789d13"},
]
[package.dependencies]
@ -400,17 +415,31 @@ bcrypt = ["bcrypt"]
[[package]]
name = "django-bootstrap5"
version = "23.3"
version = "23.4"
description = "Bootstrap 5 for Django"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "django_bootstrap5-23.3-py3-none-any.whl", hash = "sha256:ca1bb2f40175ed1c1725f52f249a574bf629b33b5a0af3bec0eab1de6d72836c"},
{file = "django_bootstrap5-23.3.tar.gz", hash = "sha256:21e1956a8a819370decc5d365ce4f4207761cb065afec5a7df8c4c8c49507f2e"},
{file = "django-bootstrap5-23.4.tar.gz", hash = "sha256:fbf9942a17e1f48b4142e78df9e85afb65e4066a20e38ec7c497d36ae5ef7256"},
{file = "django_bootstrap5-23.4-py3-none-any.whl", hash = "sha256:5181bf1e97afae6211e963f28f48d4a90c937a3b036b3f752f52260f0029f3bc"},
]
[package.dependencies]
django = ">=3.2"
Django = ">=3.2"
[[package]]
name = "django-filter"
version = "23.5"
description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
optional = false
python-versions = ">=3.7"
files = [
{file = "django-filter-23.5.tar.gz", hash = "sha256:67583aa43b91fe8c49f74a832d95f4d8442be628fd4c6d65e9f811f5153a4e5c"},
{file = "django_filter-23.5-py3-none-any.whl", hash = "sha256:99122a201d83860aef4fe77758b69dda913e874cc5e0eaa50a86b0b18d708400"},
]
[package.dependencies]
Django = ">=3.2"
[[package]]
name = "django-oauth-toolkit"
@ -429,6 +458,20 @@ jwcrypto = ">=0.8.0"
oauthlib = ">=3.1.0"
requests = ">=2.13.0"
[[package]]
name = "django-tinymce"
version = "3.7.1"
description = "A Django application that contains a widget to render a"
optional = false
python-versions = ">=3.8"
files = [
{file = "django-tinymce-3.7.1.tar.gz", hash = "sha256:29086daffb337bdd2178413e600693dff846aa4efd557c3924b8c3cba9a37e8c"},
{file = "django_tinymce-3.7.1-py3-none-any.whl", hash = "sha256:beb4d27cdacd4f8b00c90378f02898cb448e9f01a1a8a65eff4c38ca3c8edbc9"},
]
[package.dependencies]
django = ">=3.2"
[[package]]
name = "djangorestframework"
version = "3.14.0"
@ -462,38 +505,38 @@ pyflakes = ">=3.1.0,<3.2.0"
[[package]]
name = "fontawesomefree"
version = "6.4.2"
version = "6.5.1"
description = "Font Awesome Free"
optional = false
python-versions = "*"
files = [
{file = "fontawesomefree-6.4.2-py3-none-any.whl", hash = "sha256:ceaff7efc4fc39dadff3c3ff7298a7a1095403110d373f22456bb1c34ab6db02"},
{file = "fontawesomefree-6.5.1-py3-none-any.whl", hash = "sha256:8dec4a2ee37bf8e53379ebbf0d38cefec4e06c7c9227f59af39dbb7b1632817b"},
]
[[package]]
name = "idna"
version = "3.4"
version = "3.6"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
{file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
]
[[package]]
name = "jwcrypto"
version = "1.5.0"
version = "1.5.4"
description = "Implementation of JOSE Web standards"
optional = false
python-versions = ">= 3.6"
python-versions = ">= 3.8"
files = [
{file = "jwcrypto-1.5.0.tar.gz", hash = "sha256:2c1dc51cf8e38ddf324795dfe9426dee9dd46caf47f535ccbc18781fba810b8d"},
{file = "jwcrypto-1.5.4.tar.gz", hash = "sha256:0815fbab613db99bad85691da5f136f8860423396667728a264bcfa6e1db36b0"},
]
[package.dependencies]
cryptography = ">=3.4"
deprecated = "*"
typing_extensions = ">=4.5.0"
[[package]]
name = "mccabe"
@ -530,18 +573,20 @@ files = [
[[package]]
name = "mysqlclient"
version = "2.2.0"
version = "2.2.4"
description = "Python interface to MySQL"
optional = false
python-versions = ">=3.8"
files = [
{file = "mysqlclient-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:68837b6bb23170acffb43ae411e47533a560b6360c06dac39aa55700972c93b2"},
{file = "mysqlclient-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5670679ff1be1cc3fef0fa81bf39f0cd70605ba121141050f02743eb878ac114"},
{file = "mysqlclient-2.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:004fe1d30d2c2ff8072f8ea513bcec235fd9b896f70dad369461d0ad7e570e98"},
{file = "mysqlclient-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9c6b142836c7dba4f723bf9c93cc46b6e5081d65b2af807f400dda9eb85a16d0"},
{file = "mysqlclient-2.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:955dba905a7443ce4788c63fdb9f8d688316260cf60b20ff51ac3b1c77616ede"},
{file = "mysqlclient-2.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:530ece9995a36cadb6211b9787f0c9e05cdab6702549bdb4236af5e9b535ed6a"},
{file = "mysqlclient-2.2.0.tar.gz", hash = "sha256:04368445f9c487d8abb7a878e3d23e923e6072c04a6c320f9e0dc8a82efba14e"},
{file = "mysqlclient-2.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:ac44777eab0a66c14cb0d38965572f762e193ec2e5c0723bcd11319cc5b693c5"},
{file = "mysqlclient-2.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:329e4eec086a2336fe3541f1ce095d87a6f169d1cc8ba7b04ac68bcb234c9711"},
{file = "mysqlclient-2.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:e1ebe3f41d152d7cb7c265349fdb7f1eca86ccb0ca24a90036cde48e00ceb2ab"},
{file = "mysqlclient-2.2.4-cp38-cp38-win_amd64.whl", hash = "sha256:3c318755e06df599338dad7625f884b8a71fcf322a9939ef78c9b3db93e1de7a"},
{file = "mysqlclient-2.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:9d4c015480c4a6b2b1602eccd9846103fc70606244788d04aa14b31c4bd1f0e2"},
{file = "mysqlclient-2.2.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d43987bb9626096a302ca6ddcdd81feaeca65ced1d5fe892a6a66b808326aa54"},
{file = "mysqlclient-2.2.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4e80dcad884dd6e14949ac6daf769123223a52a6805345608bf49cdaf7bc8b3a"},
{file = "mysqlclient-2.2.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9d3310295cb682232cadc28abd172f406c718b9ada41d2371259098ae37779d3"},
{file = "mysqlclient-2.2.4.tar.gz", hash = "sha256:33bc9fb3464e7d7c10b1eaf7336c5ff8f2a3d3b88bab432116ad2490beb3bf41"},
]
[[package]]
@ -562,40 +607,40 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
[[package]]
name = "packaging"
version = "23.1"
version = "23.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
{file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
]
[[package]]
name = "pathspec"
version = "0.11.2"
version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
{file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
]
[[package]]
name = "platformdirs"
version = "3.10.0"
version = "4.2.0"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"},
{file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"},
{file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
{file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
]
[package.extras]
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
[[package]]
name = "prometheus-client"
@ -613,13 +658,13 @@ twisted = ["twisted"]
[[package]]
name = "pycodestyle"
version = "2.11.0"
version = "2.11.1"
description = "Python style guide checker"
optional = false
python-versions = ">=3.8"
files = [
{file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"},
{file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"},
{file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"},
{file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"},
]
[[package]]
@ -646,13 +691,13 @@ files = [
[[package]]
name = "pytz"
version = "2023.3.post1"
version = "2024.1"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
files = [
{file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
{file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
{file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
{file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
]
[[package]]
@ -680,6 +725,7 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
@ -735,6 +781,17 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]]
name = "sqlparse"
version = "0.4.4"
@ -751,6 +808,24 @@ dev = ["build", "flake8"]
doc = ["sphinx"]
test = ["pytest", "pytest-cov"]
[[package]]
name = "tinycss2"
version = "1.1.1"
description = "A tiny CSS parser"
optional = false
python-versions = ">=3.6"
files = [
{file = "tinycss2-1.1.1-py3-none-any.whl", hash = "sha256:fe794ceaadfe3cf3e686b22155d0da5780dd0e273471a51846d0a02bc204fec8"},
{file = "tinycss2-1.1.1.tar.gz", hash = "sha256:b2e44dd8883c360c35dd0d1b5aad0b610e5156c2cb3b33434634e539ead9d8bf"},
]
[package.dependencies]
webencodings = ">=0.4"
[package.extras]
doc = ["sphinx", "sphinx_rtd_theme"]
test = ["coverage[toml]", "pytest", "pytest-cov", "pytest-flake8", "pytest-isort"]
[[package]]
name = "tomli"
version = "2.0.1"
@ -764,24 +839,24 @@ files = [
[[package]]
name = "typing-extensions"
version = "4.8.0"
version = "4.10.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
{file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
{file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
{file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
]
[[package]]
name = "tzdata"
version = "2023.3"
version = "2024.1"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
files = [
{file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"},
{file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"},
{file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
{file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
]
[[package]]
@ -797,116 +872,43 @@ files = [
[[package]]
name = "urllib3"
version = "2.0.4"
version = "2.2.1"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"},
{file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"},
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uwsgi"
version = "2.0.22"
version = "2.0.24"
description = "The uWSGI server"
optional = false
python-versions = "*"
files = [
{file = "uwsgi-2.0.22.tar.gz", hash = "sha256:4cc4727258671ac5fa17ab422155e9aaef8a2008ebb86e4404b66deaae965db2"},
{file = "uwsgi-2.0.24.tar.gz", hash = "sha256:77b6dd5cd633f4ae87ee393f7701f617736815499407376e78f3d16467523afe"},
]
[[package]]
name = "wrapt"
version = "1.15.0"
description = "Module for decorators, wrappers and monkey patching."
name = "webencodings"
version = "0.5.1"
description = "Character encoding aliases for legacy web content"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
python-versions = "*"
files = [
{file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"},
{file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"},
{file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"},
{file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"},
{file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"},
{file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"},
{file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"},
{file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"},
{file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"},
{file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"},
{file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"},
{file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"},
{file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"},
{file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"},
{file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"},
{file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"},
{file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"},
{file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"},
{file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"},
{file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"},
{file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"},
{file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"},
{file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"},
{file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"},
{file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"},
{file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"},
{file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"},
{file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"},
{file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"},
{file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"},
{file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"},
{file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"},
{file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"},
{file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"},
{file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"},
{file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"},
{file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"},
{file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"},
{file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"},
{file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"},
{file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"},
{file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"},
{file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"},
{file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"},
{file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"},
{file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"},
{file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"},
{file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"},
{file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"},
{file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"},
{file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"},
{file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"},
{file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"},
{file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"},
{file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"},
{file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"},
{file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"},
{file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"},
{file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"},
{file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"},
{file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"},
{file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"},
{file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"},
{file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"},
{file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"},
{file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"},
{file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"},
{file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"},
{file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"},
{file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"},
{file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"},
{file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"},
{file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"},
{file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"},
{file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"},
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
{file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "06f67dca27bc15e59c5195d91a0817565d8ab6c9d2eb5bd7898b7a3310df91ac"
content-hash = "10299202bc4f1f9735feefcb1d140a38aa51dad5d4fd03fe5debd1a84f1cc680"

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "MarietjeDjango"
version = "4.1.0"
name = "marietje"
version = "4.1.1"
description = "A music player for the south canteen of the Huygens building"
authors = [
"Jim Driessen <jim@driessen.li>",
@ -11,7 +11,7 @@ authors = [
"Lars van Rhijn <l.vanrhijn@student.science.ru.nl>",
]
maintainers = [
"Kees van Kempen <ru@keesvankempen.nl",
"Kees van Kempen <ru@keesvankempen.nl>",
"Lars van Rhijn <l.vanrhijn@student.science.ru.nl>",
]
readme = "README.md"
@ -21,7 +21,7 @@ python = "^3.9"
Django = "^4.2.5"
django-oauth-toolkit = "^2.3.0"
django-bootstrap5 = "^23.3"
fontawesomefree = "^6.4.2"
fontawesomefree = "^6.5.1"
djangorestframework = "^3.14.0"
mysqlclient = "^2.2.0"
prometheus-client = "^0.17.1"
@ -30,6 +30,9 @@ argon2-cffi = "^23.1.0"
uritemplate = "^4.1.1"
pyyaml = "^6.0.1"
uwsgi = "^2.0.22"
django-filter = "^23.3"
django-tinymce = "^3.6.1"
bleach = { extras = ["css"], version = "^5.0.1" }
[tool.poetry.group.dev.dependencies]
black = "^23.7.0"