18 Commits

Author SHA1 Message Date
dependabot[bot]
d6250faf5f Merge f083936aad into db23064929 2023-08-28 10:15:09 +00:00
Peter Dwyer
db23064929 library bumps
version bump.
added inital build action
2023-08-28 11:14:53 +01:00
dependabot[bot]
f083936aad Bump certifi from 2022.12.7 to 2023.7.22
Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2023.7.22.
- [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2023.07.22)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-25 21:11:56 +00:00
dependabot[bot]
bccbff8b33 Bump sqlparse from 0.4.3 to 0.4.4 (#93)
Bumps [sqlparse](https://github.com/andialbrecht/sqlparse) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/andialbrecht/sqlparse/releases)
- [Changelog](https://github.com/andialbrecht/sqlparse/blob/master/CHANGELOG)
- [Commits](https://github.com/andialbrecht/sqlparse/compare/0.4.3...0.4.4)

---
updated-dependencies:
- dependency-name: sqlparse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ajurna <ajurna@gmail.com>
2023-05-23 08:50:16 +01:00
dependabot[bot]
05e3db9e42 Bump django from 4.1.7 to 4.1.9 (#94)
Bumps [django](https://github.com/django/django) from 4.1.7 to 4.1.9.
- [Commits](https://github.com/django/django/compare/4.1.7...4.1.9)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ajurna <ajurna@gmail.com>
2023-05-23 08:48:02 +01:00
dependabot[bot]
a47153b917 Bump requests from 2.28.2 to 2.31.0 (#95)
Bumps [requests](https://github.com/psf/requests) from 2.28.2 to 2.31.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.28.2...v2.31.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 08:45:45 +01:00
dependabot[bot]
df01235325 Bump webpack from 5.75.0 to 5.76.0 in /frontend (#92)
Bumps [webpack](https://github.com/webpack/webpack) from 5.75.0 to 5.76.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.75.0...v5.76.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-17 15:54:34 +01:00
Peter Dwyer
7d20447526 library bumps
version bump.
2023-02-16 09:55:32 +00:00
Peter Dwyer
0dfe551b66 library bumps
version bump.
2023-02-16 09:36:50 +00:00
Peter Dwyer
94171ec548 library bumps
version bump.
2023-02-07 14:44:12 +00:00
Peter Dwyer
1fbe359448 library bumps
fix for LoginView.vue reloading instead of logging in.
improved errors for login failing
misc cleanup.
2023-02-07 14:39:26 +00:00
Peter Dwyer
84eec0c0bf library bumps 2022-11-16 10:02:20 +00:00
Peter Dwyer
ac83abaa97 library bumps 2022-11-16 09:58:37 +00:00
dependabot[bot]
ef03651ace Bump loader-utils from 1.4.0 to 1.4.1 in /frontend (#80)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.1/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-16 09:14:37 +00:00
Snyk bot
e39ae6847d fix: requirements.txt to reduce vulnerabilities (#81)
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-SETUPTOOLS-3113904
2022-11-16 09:14:25 +00:00
dependabot[bot]
970b56b126 Bump pillow from 9.2.0 to 9.3.0 (#82)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 9.2.0 to 9.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.2.0...9.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-16 09:14:10 +00:00
5ea87fe9b7 update pre-commit 2022-10-04 22:28:05 +01:00
a97abe4557 update pre-commit 2022-10-04 22:26:14 +01:00
17 changed files with 2826 additions and 11526 deletions

View File

@@ -0,0 +1,19 @@
name: Build and push image
on:
push:
branches: [main]
jobs:
deploy:
runs-on: self-hosted
defaults:
run:
working-directory: /repo
steps:
- name: Checkout repo
run: git pull
- name: Deploy
run: |
docker build . --no-cache -t ajurna/cbwebreader

View File

@@ -9,11 +9,11 @@ repos:
- id: check-yaml - id: check-yaml
- id: check-added-large-files - id: check-added-large-files
- repo: https://github.com/python-poetry/poetry - repo: https://github.com/python-poetry/poetry
rev: '1.2.0rc1' # add version here rev: '1.2.1' # add version here
hooks: hooks:
- id: poetry-check - id: poetry-check
- id: poetry-export - id: poetry-export
args: ["--without-hashes", "-f", "requirements.txt", "-o", "requirements.txt"] args: ["--without-hashes", "-o", "requirements.txt"]
- repo: https://github.com/pycqa/flake8 - repo: https://github.com/pycqa/flake8
rev: "5.0.4" rev: "5.0.4"
hooks: hooks:

View File

@@ -2,5 +2,5 @@
max-line-length=120 max-line-length=120
ignore-paths=.*/migrations ignore-paths=.*/migrations
load-plugins = pylint_django load-plugins = pylint_django
disable = missing-class-docstring,missing-function-docstring,abstract-method,missing-module-docstring,imported-auth-user disable = missing-class-docstring,missing-function-docstring,abstract-method,missing-module-docstring,imported-auth-user,missing-docstring
good-names=pk good-names=pk

View File

@@ -6,6 +6,7 @@ Django settings for cbreader project.
import os import os
from datetime import timedelta from datetime import timedelta
from pathlib import Path from pathlib import Path
from typing import Dict, List
import dj_database_url import dj_database_url
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -27,7 +28,7 @@ ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost").split(",")
# Application definition # Application definition
INSTALLED_APPS = ( INSTALLED_APPS = [
"django.contrib.admin", "django.contrib.admin",
"django.contrib.auth", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.contenttypes",
@@ -46,7 +47,7 @@ INSTALLED_APPS = (
'django_filters', 'django_filters',
'rest_framework', 'rest_framework',
# 'silk' # 'silk'
) ]
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
@@ -58,8 +59,7 @@ MIDDLEWARE = [
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
# 'silk.middleware.SilkyMiddleware', 'csp.middleware.CSPMiddleware',
# 'csp.middleware.CSPMiddleware',
] ]
ROOT_URLCONF = "cbreader.urls" ROOT_URLCONF = "cbreader.urls"
@@ -197,10 +197,10 @@ CSP_FONT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'",) CSP_SCRIPT_SRC = ("'self'",)
CSP_CONNECT_SRC = ("'self'",) CSP_CONNECT_SRC = ("'self'",)
CSP_INCLUDE_NONCE_IN = ['script-src'] CSP_INCLUDE_NONCE_IN = ['script-src']
CSP_SCRIPT_SRC_ATTR = ("'self'",)# "'unsafe-inline'") CSP_SCRIPT_SRC_ATTR = ("'self'",) # "'unsafe-inline'")
PERMISSIONS_POLICY = { PERMISSIONS_POLICY: Dict[str, List] = {
"accelerometer": [], "accelerometer": [],
"ambient-light-sensor": [], "ambient-light-sensor": [],
"autoplay": [], "autoplay": [],

View File

@@ -1,30 +1,11 @@
from .base import * from .base import INSTALLED_APPS, MIDDLEWARE, SILK_ENABLED
INSTALLED_APPS = ( INSTALLED_APPS += ["silk"]
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
'silk',
"snowpenguin.django.recaptcha2",
'bootstrap4',
"comic",
"comic_auth",
)
MIDDLEWARE = [ MIDDLEWARE += [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
'silk.middleware.SilkyMiddleware', 'silk.middleware.SilkyMiddleware',
] ]
SILK_ENABLED = True SILK_ENABLED = True # noqa: F811
SILKY_PYTHON_PROFILER = True SILKY_PYTHON_PROFILER = True

View File

@@ -1,6 +1,6 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.syndication.views import Feed from django.contrib.syndication.views import Feed
from django.db.models import Case, When, PositiveSmallIntegerField, F from django.db.models import Case, When, PositiveSmallIntegerField, F, QuerySet
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
@@ -18,7 +18,7 @@ class RecentComicsAPI(Feed):
self.user = user_misc.user self.user = user_misc.user
return user_misc.user return user_misc.user
def items(self) -> ComicBook: def items(self) -> QuerySet[ComicBook]:
comics = ComicBook.objects.order_by("-date_added") comics = ComicBook.objects.order_by("-date_added")
comics = comics.annotate( comics = comics.annotate(
classification=Case( classification=Case(

View File

@@ -1,11 +1,11 @@
from typing import Optional, Union from typing import Optional
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import User from django.contrib.auth.base_user import AbstractBaseUser
from django.core.management.base import BaseCommand, CommandParser from django.core.management.base import BaseCommand, CommandParser
from loguru import logger from loguru import logger
from comic.models import ComicBook, Directory from comic.models import Directory
from comic.processing import generate_directory from comic.processing import generate_directory
@@ -27,12 +27,11 @@ class Command(BaseCommand):
self.OUTPUT = options.get('out', False) self.OUTPUT = options.get('out', False)
self.scan_directory() self.scan_directory()
def scan_directory(self, user: Optional[User] = None, directory: Optional[Directory] = None) -> None: def scan_directory(self, user: Optional[AbstractBaseUser] = None, directory: Optional[Directory] = None) -> None:
if not user: if not user:
user_model = get_user_model() user_model = get_user_model()
user = user_model.objects.first() user: AbstractBaseUser = user_model.objects.first()
for item in generate_directory(user, directory): for item in generate_directory(user, directory):
item: Union[Directory, ComicBook] if item is Directory:
if item.type == 'Directory':
logger.info(item) logger.info(item)
self.scan_directory(user, item) self.scan_directory(user, item)

View File

@@ -6,7 +6,7 @@ from typing import NamedTuple, List, Optional, Union
import rarfile import rarfile
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.base_user import AbstractBaseUser
from django.db.models import Count, Q, F, Case, When, PositiveSmallIntegerField, QuerySet, ExpressionWrapper, \ from django.db.models import Count, Q, F, Case, When, PositiveSmallIntegerField, QuerySet, ExpressionWrapper, \
IntegerField IntegerField
@@ -14,7 +14,8 @@ from comic import models
from comic.errors import NotCompatibleArchive from comic.errors import NotCompatibleArchive
def generate_directory(user: User, directory: Optional[models.Directory] = None) -> List[QuerySet]: def generate_directory(user: AbstractBaseUser, directory: Optional[models.Directory] = None) \
-> List[Union[models.Directory, models.ComicBook]]:
dir_path = Path(settings.COMIC_BOOK_VOLUME, directory.path) if directory else settings.COMIC_BOOK_VOLUME dir_path = Path(settings.COMIC_BOOK_VOLUME, directory.path) if directory else settings.COMIC_BOOK_VOLUME
files = [] files = []
@@ -74,7 +75,8 @@ def clean_directories(directories: QuerySet, dir_path: Path, directory: Optional
models.Directory.objects.get(name=stale_directory.name, parent=directory).delete() models.Directory.objects.get(name=stale_directory.name, parent=directory).delete()
def clean_files(files: QuerySet, user: User, dir_path: Path, directory: Optional[models.Directory] = None) -> None: def clean_files(files: QuerySet, user: AbstractBaseUser, dir_path: Path, directory: Optional[models.Directory] = None) \
-> None:
file_list = set(x for x in sorted(dir_path.glob('*')) if x.is_file()) file_list = set(x for x in sorted(dir_path.glob('*')) if x.is_file())
files_db_set = set(Path(dir_path, x.file_name) for x in files) files_db_set = set(Path(dir_path, x.file_name) for x in files)

10686
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@
"vue-toast-notification": "3.0", "vue-toast-notification": "3.0",
"vuejs-paginate-next": "^1.0.2", "vuejs-paginate-next": "^1.0.2",
"vuex": "^4.0.0", "vuex": "^4.0.0",
"webpack": "^5.74.0" "webpack": "^5.76.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.16", "@babel/core": "^7.12.16",

View File

@@ -8,7 +8,7 @@ async function get_access_token() {
let refresh = jwtDecode(store.state.jwt.refresh) let refresh = jwtDecode(store.state.jwt.refresh)
if (access.exp - Date.now()/1000 < 5) { if (access.exp - Date.now()/1000 < 5) {
if (refresh.exp - Date.now()/1000 < 5) { if (refresh.exp - Date.now()/1000 < 5) {
await router.push({name: 'login', params: { username: 'eduardo' }}) await router.push({name: 'login'})
return null return null
} else { } else {
return store.dispatch('refreshToken').then(() => {return store.state.jwt.access}) return store.dispatch('refreshToken').then(() => {return store.state.jwt.access})
@@ -22,9 +22,9 @@ const axios_jwt = axios.create();
axios_jwt.interceptors.request.use(async function (config) { axios_jwt.interceptors.request.use(async function (config) {
let access_token = await get_access_token().catch(() => { let access_token = await get_access_token().catch(() => {
if (router.currentRoute.value.fullPath.includes('login')){ if (router.currentRoute.value.fullPath.includes('login')){
router.push({name: 'login'}) router.push({name: 'login'})
}else { }else {
router.push({name: 'login', query: { next: router.currentRoute.value.fullPath }}) router.push({name: 'login', query: { next: router.currentRoute.value.fullPath }})
} }
}) })
@@ -32,9 +32,9 @@ axios_jwt.interceptors.request.use(async function (config) {
Authorization: "Bearer " + access_token Authorization: "Bearer " + access_token
} }
return config return config
}, function (error) { }, function (error) {
// Do something with request error // Do something with request error
return Promise.reject(error); return Promise.reject(error);
}); });
export default axios_jwt export default axios_jwt

View File

@@ -73,14 +73,22 @@ export default createStore({
if ('next' in router.currentRoute.value.query) { if ('next' in router.currentRoute.value.query) {
router.push(router.currentRoute.value.query.next) router.push(router.currentRoute.value.query.next)
} else { } else {
router.push('/') router.push('browse')
} }
}) })
.catch((error)=>{ .catch((error)=>{
// console.log(error);
const $toast = useToast(); const $toast = useToast();
$toast.error(error.response.data.detail, {position:'top'}); if (error.response.data.detail) {
$toast.error(error.response.data.detail, {position:'top'});
}
if (error.response.data.username) {
$toast.error("Username: " + error.response.data.username, {position:'top'});
}
if (error.response.data.password) {
$toast.error("Password: " + error.response.data.password, {position:'top'});
}
}) })
}, },
refreshToken(){ refreshToken(){

View File

@@ -3,7 +3,7 @@
<div class="row" v-if="!initialSetupRequired"> <div class="row" v-if="!initialSetupRequired">
<div class="col col-lg-4" /> <div class="col col-lg-4" />
<div class="col col-lg-4" id="login-col"> <div class="col col-lg-4" id="login-col">
<form @submit="login"> <form @submit="login" v-on:submit.prevent="onSubmit">
<label class="form-label" for="username">Username</label> <label class="form-label" for="username">Username</label>
<input id="username" placeholder="username" aria-describedby="loginFormControlInputHelpInline" class="form-control" type="text" v-model="username" /> <input id="username" placeholder="username" aria-describedby="loginFormControlInputHelpInline" class="form-control" type="text" v-model="username" />
<div class="form-text" id="loginFormControlInputHelpInline">Please enter your username</div> <div class="form-text" id="loginFormControlInputHelpInline">Please enter your username</div>

0
mypy.ini Normal file
View File

3436
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,52 +3,53 @@ line_length = 119
[tool.poetry] [tool.poetry]
name = "cbwebreader" name = "cbwebreader"
version = "1.1.1" version = "1.1.5"
description = "CBR/Z Web Reader" description = "CBR/Z Web Reader"
authors = ["ajurna <ajurna@gmail.com>"] authors = ["ajurna <ajurna@gmail.com>"]
license = "Creative Commons Attribution-ShareAlike 4.0 International License" license = "Creative Commons Attribution-ShareAlike 4.0 International License"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.10" python = "^3.10"
Django = "4.1.1" Django = "^4.1"
gunicorn = "^20.0.4" gunicorn = "^20.0.4"
dj-database-url = "^1.0.0" dj-database-url = "^1.3.0"
python-dotenv = "^0.21.0" python-dotenv = "^1.0.0"
loguru = "^0.6.0" loguru = "^0.7.0"
django-silk = "^5.0.0" django-silk = "^5.0.0"
mysqlclient = "^2.0.1" mysqlclient = "^2.0.1"
psycopg2 = "^2.8.6" psycopg2 = "^2.9.6"
rarfile = "^4.0" rarfile = "^4.0"
django-extensions = "^3.2.1" django-extensions = "^3.2.1"
Pillow = "^9.1.1" Pillow = "^9.3.0"
django-imagekit = "^4.0.2" django-imagekit = "^4.0.2"
PyMuPDF = "~1.20.2" PyMuPDF = "~1.20.2"
django-bootstrap4 = "^22.1" django-bootstrap4 = "^23.1"
django-csp = "^3.7" django-csp = "^3.7"
django-boost = "^2.1" django-boost = "^2.1"
django-sri = "^0.5.0" django-sri = "^0.5.0"
django-permissions-policy = "^4.9.0" django-permissions-policy = "^4.15.0"
djangorestframework = "^3.13.1" djangorestframework = "^3.13.1"
django-filter = "^22.1" django-filter = "^23.1"
django-cors-headers = "^3.13.0" django-cors-headers = "^3.14.0"
djangorestframework-simplejwt = "^5.2.0" djangorestframework-simplejwt = "^5.2.0"
django-webpack-loader = "^1.6.0" django-webpack-loader = "^1.6.0"
drf-yasg = "^1.20.0" drf-yasg = "^1.20.0"
drf-extensions = "^0.7.1" drf-extensions = "^0.7.1"
flake8 = "^5.0.4"
flake8-annotations = "^2.9.1"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
mypy = "^0.971" mypy = "^1.2.0"
Werkzeug = "^2.2" Werkzeug = "^2.2"
pyOpenSSL = "^22.0.0" pyOpenSSL = "^22.0.0"
ipython = "^8.4.0" ipython = "^8.12.0"
coverage = "^6.2" coverage = "^7.2.3"
pre-commit = "^2.20.0" pre-commit = "^3.2.2"
flake8 = "^6.0.0"
flake8-annotations = "^3.0.0"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
pylint = "^2.15.0" pylint = "^2.15.0"
pylint-django = "^2.5.3" pylint-django = "^2.5.3"
mypy = "^1.2.0"
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=1.0.0"]

View File

@@ -1,66 +1,60 @@
asgiref==3.5.2 ; python_version >= "3.10" and python_version < "4.0" asgiref==3.6.0 ; python_version >= "3.10" and python_version < "4.0"
attrs==22.1.0 ; python_version >= "3.10" and python_version < "4.0" autopep8==2.0.2 ; python_version >= "3.10" and python_version < "4.0"
autopep8==1.7.0 ; python_version >= "3.10" and python_version < "4.0" beautifulsoup4==4.12.2 ; python_version >= "3.10" and python_version < "4.0"
beautifulsoup4==4.11.1 ; python_version >= "3.10" and python_version < "4.0" certifi==2022.12.7 ; python_version >= "3.10" and python_version < "4"
certifi==2022.9.14 ; python_version >= "3.10" and python_version < "4" charset-normalizer==3.1.0 ; python_version >= "3.10" and python_version < "4"
charset-normalizer==2.1.1 ; python_version >= "3.10" and python_version < "4" colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32"
colorama==0.4.5 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32"
coreapi==2.3.3 ; python_version >= "3.10" and python_version < "4.0" coreapi==2.3.3 ; python_version >= "3.10" and python_version < "4.0"
coreschema==0.0.4 ; python_version >= "3.10" and python_version < "4.0" coreschema==0.0.4 ; python_version >= "3.10" and python_version < "4.0"
dj-database-url==1.0.0 ; python_version >= "3.10" and python_version < "4.0" dj-database-url==1.3.0 ; python_version >= "3.10" and python_version < "4.0"
django-appconf==1.0.5 ; python_version >= "3.10" and python_version < "4.0" django-appconf==1.0.5 ; python_version >= "3.10" and python_version < "4.0"
django-boost==2.1 ; python_version >= "3.10" and python_version < "4.0" django-boost==2.1 ; python_version >= "3.10" and python_version < "4.0"
django-bootstrap4==22.2 ; python_version >= "3.10" and python_version < "4.0" django-bootstrap4==23.1 ; python_version >= "3.10" and python_version < "4.0"
django-cors-headers==3.13.0 ; python_version >= "3.10" and python_version < "4.0" django-cors-headers==3.14.0 ; python_version >= "3.10" and python_version < "4.0"
django-csp==3.7 ; python_version >= "3.10" and python_version < "4.0" django-csp==3.7 ; python_version >= "3.10" and python_version < "4.0"
django-extensions==3.2.1 ; python_version >= "3.10" and python_version < "4.0" django-extensions==3.2.1 ; python_version >= "3.10" and python_version < "4.0"
django-filter==22.1 ; python_version >= "3.10" and python_version < "4.0" django-filter==23.1 ; python_version >= "3.10" and python_version < "4.0"
django-imagekit==4.1.0 ; python_version >= "3.10" and python_version < "4.0" django-imagekit==4.1.0 ; python_version >= "3.10" and python_version < "4.0"
django-permissions-policy==4.13.0 ; python_version >= "3.10" and python_version < "4.0" django-permissions-policy==4.15.0 ; python_version >= "3.10" and python_version < "4.0"
django-silk==5.0.1 ; python_version >= "3.10" and python_version < "4.0" django-silk==5.0.3 ; python_version >= "3.10" and python_version < "4.0"
django-sri==0.5.0 ; python_version >= "3.10" and python_version < "4.0" django-sri==0.5.0 ; python_version >= "3.10" and python_version < "4.0"
django-webpack-loader==1.6.0 ; python_version >= "3.10" and python_version < "4.0" django-webpack-loader==1.8.1 ; python_version >= "3.10" and python_version < "4.0"
django==4.1.1 ; python_version >= "3.10" and python_version < "4.0" django==4.2 ; python_version >= "3.10" and python_version < "4.0"
djangorestframework-simplejwt==5.2.0 ; python_version >= "3.10" and python_version < "4.0" djangorestframework-simplejwt==5.2.2 ; python_version >= "3.10" and python_version < "4.0"
djangorestframework==3.13.1 ; python_version >= "3.10" and python_version < "4.0" djangorestframework==3.14.0 ; python_version >= "3.10" and python_version < "4.0"
drf-extensions==0.7.1 ; python_version >= "3.10" and python_version < "4.0" drf-extensions==0.7.1 ; python_version >= "3.10" and python_version < "4.0"
drf-yasg==1.21.3 ; python_version >= "3.10" and python_version < "4.0" drf-yasg==1.21.5 ; python_version >= "3.10" and python_version < "4.0"
flake8-annotations==2.9.1 ; python_version >= "3.10" and python_version < "4.0"
flake8==5.0.4 ; python_version >= "3.10" and python_version < "4.0"
gprof2dot==2022.7.29 ; python_version >= "3.10" and python_version < "4.0" gprof2dot==2022.7.29 ; python_version >= "3.10" and python_version < "4.0"
gunicorn==20.1.0 ; python_version >= "3.10" and python_version < "4.0" gunicorn==20.1.0 ; python_version >= "3.10" and python_version < "4.0"
idna==3.4 ; python_version >= "3.10" and python_version < "4" idna==3.4 ; python_version >= "3.10" and python_version < "4"
inflection==0.5.1 ; python_version >= "3.10" and python_version < "4.0" inflection==0.5.1 ; python_version >= "3.10" and python_version < "4.0"
itypes==1.2.0 ; python_version >= "3.10" and python_version < "4.0" itypes==1.2.0 ; python_version >= "3.10" and python_version < "4.0"
jinja2==3.1.2 ; python_version >= "3.10" and python_version < "4.0" jinja2==3.1.2 ; python_version >= "3.10" and python_version < "4.0"
loguru==0.6.0 ; python_version >= "3.10" and python_version < "4.0" loguru==0.7.0 ; python_version >= "3.10" and python_version < "4.0"
markupsafe==2.1.1 ; python_version >= "3.10" and python_version < "4.0" markupsafe==2.1.2 ; python_version >= "3.10" and python_version < "4.0"
mccabe==0.7.0 ; python_version >= "3.10" and python_version < "4.0"
mysqlclient==2.1.1 ; python_version >= "3.10" and python_version < "4.0" mysqlclient==2.1.1 ; python_version >= "3.10" and python_version < "4.0"
packaging==21.3 ; python_version >= "3.10" and python_version < "4.0" packaging==23.1 ; python_version >= "3.10" and python_version < "4.0"
pilkit==2.0 ; python_version >= "3.10" and python_version < "4.0" pilkit==2.0 ; python_version >= "3.10" and python_version < "4.0"
pillow==9.2.0 ; python_version >= "3.10" and python_version < "4.0" pillow==9.5.0 ; python_version >= "3.10" and python_version < "4.0"
psycopg2==2.9.3 ; python_version >= "3.10" and python_version < "4.0" psycopg2==2.9.6 ; python_version >= "3.10" and python_version < "4.0"
pycodestyle==2.9.1 ; python_version >= "3.10" and python_version < "4.0" pycodestyle==2.10.0 ; python_version >= "3.10" and python_version < "4.0"
pyflakes==2.5.0 ; python_version >= "3.10" and python_version < "4.0" pyjwt==2.6.0 ; python_version >= "3.10" and python_version < "4.0"
pyjwt==2.4.0 ; python_version >= "3.10" and python_version < "4.0"
pymupdf==1.20.2 ; python_version >= "3.10" and python_version < "4.0" pymupdf==1.20.2 ; python_version >= "3.10" and python_version < "4.0"
pyparsing==3.0.9 ; python_version >= "3.10" and python_version < "4.0" python-dotenv==1.0.0 ; python_version >= "3.10" and python_version < "4.0"
python-dateutil==2.8.2 ; python_version >= "3.10" and python_version < "4.0" pytz==2023.3 ; python_version >= "3.10" and python_version < "4.0"
python-dotenv==0.21.0 ; python_version >= "3.10" and python_version < "4.0"
pytz==2022.2.1 ; python_version >= "3.10" and python_version < "4.0"
rarfile==4.0 ; python_version >= "3.10" and python_version < "4.0" rarfile==4.0 ; python_version >= "3.10" and python_version < "4.0"
requests==2.28.1 ; python_version >= "3.10" and python_version < "4" requests==2.28.2 ; python_version >= "3.10" and python_version < "4"
ruamel-yaml-clib==0.2.6 ; platform_python_implementation == "CPython" and python_version < "3.11" and python_version >= "3.10" ruamel-yaml-clib==0.2.7 ; platform_python_implementation == "CPython" and python_version < "3.11" and python_version >= "3.10"
ruamel-yaml==0.17.21 ; python_version >= "3.10" and python_version < "4.0" ruamel-yaml==0.17.21 ; python_version >= "3.10" and python_version < "4.0"
setuptools==65.3.0 ; python_version >= "3.10" and python_version < "4.0" setuptools==67.6.1 ; python_version >= "3.10" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.10" and python_version < "4.0" six==1.16.0 ; python_version >= "3.10" and python_version < "4.0"
soupsieve==2.3.2.post1 ; python_version >= "3.10" and python_version < "4.0" soupsieve==2.4.1 ; python_version >= "3.10" and python_version < "4.0"
sqlparse==0.4.2 ; python_version >= "3.10" and python_version < "4.0" sqlparse==0.4.3 ; python_version >= "3.10" and python_version < "4.0"
toml==0.10.2 ; python_version >= "3.10" and python_version < "4.0" tomli==2.0.1 ; python_version >= "3.10" and python_version < "3.11"
tzdata==2022.2 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32" typing-extensions==4.5.0 ; python_version >= "3.10" and python_version < "4.0"
tzdata==2023.3 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32"
ua-parser==0.16.1 ; python_version >= "3.10" and python_version < "4.0" ua-parser==0.16.1 ; python_version >= "3.10" and python_version < "4.0"
uritemplate==4.1.1 ; python_version >= "3.10" and python_version < "4.0" uritemplate==4.1.1 ; python_version >= "3.10" and python_version < "4.0"
urllib3==1.26.12 ; python_version >= "3.10" and python_version < "4" urllib3==1.26.15 ; python_version >= "3.10" and python_version < "4"
user-agents==2.2.0 ; python_version >= "3.10" and python_version < "4.0" user-agents==2.2.0 ; python_version >= "3.10" and python_version < "4.0"
win32-setctime==1.1.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32" win32-setctime==1.1.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32"