mirror of
https://github.com/ajurna/cbwebreader.git
synced 2025-12-06 06:17:17 +00:00
fixed tests.
removed os based file access. removed rarfile integration now using package. now using environment variable for comic directory.
This commit is contained in:
@@ -117,7 +117,7 @@ CBREADER_USE_RECAPTCHA = os.getenv("DJANGO_CBREADER_USE_RECAPTCHA", False)
|
|||||||
RECAPTCHA_PRIVATE_KEY = os.getenv("DJANGO_RECAPTCHA_PRIVATE_KEY", '')
|
RECAPTCHA_PRIVATE_KEY = os.getenv("DJANGO_RECAPTCHA_PRIVATE_KEY", '')
|
||||||
RECAPTCHA_PUBLIC_KEY = os.getenv("DJANGO_RECAPTCHA_PUBLIC_KEY", '')
|
RECAPTCHA_PUBLIC_KEY = os.getenv("DJANGO_RECAPTCHA_PUBLIC_KEY", '')
|
||||||
|
|
||||||
COMIC_DIR = "/media/comics"
|
COMIC_BOOK_VOLUME = Path(os.getenv("COMIC_BOOK_VOLUME"))
|
||||||
|
|
||||||
from .logger import LOGGING
|
from .logger import LOGGING
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,6 @@ class InitialSetupForm(forms.Form):
|
|||||||
password_confirm = forms.CharField(
|
password_confirm = forms.CharField(
|
||||||
widget=forms.PasswordInput(attrs={"class": "form-control"})
|
widget=forms.PasswordInput(attrs={"class": "form-control"})
|
||||||
)
|
)
|
||||||
base_dir = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))
|
|
||||||
|
|
||||||
def clean_base_dir(self):
|
|
||||||
data = self.cleaned_data["base_dir"]
|
|
||||||
if not path.isdir(data):
|
|
||||||
raise forms.ValidationError("This is not a valid Directory")
|
|
||||||
return data
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
form_data = self.cleaned_data
|
form_data = self.cleaned_data
|
||||||
|
|||||||
122
comic/models.py
122
comic/models.py
@@ -1,6 +1,10 @@
|
|||||||
import uuid
|
import uuid
|
||||||
import zipfile
|
import zipfile
|
||||||
from os import listdir, path
|
from dataclasses import dataclass
|
||||||
|
from functools import reduce
|
||||||
|
from os import listdir
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@@ -11,7 +15,7 @@ import PyPDF4
|
|||||||
import PyPDF4.utils
|
import PyPDF4.utils
|
||||||
|
|
||||||
|
|
||||||
from comic import rarfile
|
import rarfile
|
||||||
|
|
||||||
if settings.UNRAR_TOOL:
|
if settings.UNRAR_TOOL:
|
||||||
rarfile.UNRAR_TOOL = settings.UNRAR_TOOL
|
rarfile.UNRAR_TOOL = settings.UNRAR_TOOL
|
||||||
@@ -40,15 +44,18 @@ class Directory(models.Model):
|
|||||||
return "Directory: {0}; {1}".format(self.name, self.parent)
|
return "Directory: {0}; {1}".format(self.name, self.parent)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self) -> Path:
|
||||||
return self.get_path()
|
return self.get_path()
|
||||||
|
|
||||||
def get_path(self):
|
def get_path(self) -> Path:
|
||||||
path_items = self.get_path_items()
|
path_items = self.get_path_items()
|
||||||
path_items.reverse()
|
path_items.reverse()
|
||||||
return path.sep.join(path_items)
|
if len(path_items) >= 2:
|
||||||
|
return reduce(lambda x, y: Path(x, y), path_items)
|
||||||
|
else:
|
||||||
|
return Path(path_items[0])
|
||||||
|
|
||||||
def get_path_items(self, p=None):
|
def get_path_items(self, p: Optional[List] = None) -> List[str]:
|
||||||
if p is None:
|
if p is None:
|
||||||
p = []
|
p = []
|
||||||
p.append(self.name)
|
p.append(self.name)
|
||||||
@@ -64,14 +71,14 @@ class Directory(models.Model):
|
|||||||
self.parent.get_path_objects(p)
|
self.parent.get_path_objects(p)
|
||||||
return p
|
return p
|
||||||
|
|
||||||
@staticmethod
|
# @staticmethod
|
||||||
def get_dir_from_path(file_path):
|
# def get_dir_from_path(file_path):
|
||||||
file_path = file_path.split(path.sep)
|
# file_path = file_path.split(os_path.sep)
|
||||||
print(file_path)
|
# print(file_path)
|
||||||
for d in Directory.objects.filter(name=file_path[-1]):
|
# for d in Directory.objects.filter(name=file_path[-1]):
|
||||||
print(d)
|
# print(d)
|
||||||
if d.get_path_items() == file_path:
|
# if d.get_path_items() == file_path:
|
||||||
return d
|
# return d
|
||||||
|
|
||||||
|
|
||||||
class ComicBook(models.Model):
|
class ComicBook(models.Model):
|
||||||
@@ -89,15 +96,15 @@ class ComicBook(models.Model):
|
|||||||
return urlsafe_base64_encode(self.selector.bytes)
|
return urlsafe_base64_encode(self.selector.bytes)
|
||||||
|
|
||||||
def get_pdf(self):
|
def get_pdf(self):
|
||||||
base_dir = Setting.objects.get(name="BASE_DIR").value
|
base_dir = settings.COMIC_BOOK_VOLUME
|
||||||
return path.join(base_dir, self.directory.get_path(), self.file_name)
|
return Path(base_dir, self.directory.get_path(), self.file_name)
|
||||||
|
|
||||||
def get_image(self, page):
|
def get_image(self, page):
|
||||||
base_dir = Setting.objects.get(name="BASE_DIR").value
|
base_dir = settings.COMIC_BOOK_VOLUME
|
||||||
if self.directory:
|
if self.directory:
|
||||||
archive_path = path.join(base_dir, self.directory.path, self.file_name)
|
archive_path = Path(base_dir, self.directory.path, self.file_name)
|
||||||
else:
|
else:
|
||||||
archive_path = path.join(base_dir, self.file_name)
|
archive_path = Path(base_dir, self.file_name)
|
||||||
try:
|
try:
|
||||||
archive = rarfile.RarFile(archive_path)
|
archive = rarfile.RarFile(archive_path)
|
||||||
except rarfile.NotRarFile:
|
except rarfile.NotRarFile:
|
||||||
@@ -117,39 +124,23 @@ class ComicBook(models.Model):
|
|||||||
def page_count(self):
|
def page_count(self):
|
||||||
return ComicPage.objects.filter(Comic=self).count()
|
return ComicPage.objects.filter(Comic=self).count()
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class Navigation:
|
class Navigation:
|
||||||
next_index = 0
|
next_path: str
|
||||||
next_path = ""
|
prev_path: str
|
||||||
prev_index = 0
|
cur_path: str
|
||||||
prev_path = ""
|
|
||||||
cur_index = 0
|
|
||||||
cur_path = ""
|
|
||||||
q_prev_to_directory = False
|
|
||||||
q_next_to_directory = False
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def nav(self, user):
|
||||||
for arg in kwargs:
|
return self.Navigation(
|
||||||
setattr(self, arg, kwargs[arg])
|
next_path=self.nav_get_next_comic(user),
|
||||||
|
prev_path=self.nav_get_prev_comic(user),
|
||||||
|
cur_path=urlsafe_base64_encode(self.selector.bytes)
|
||||||
|
)
|
||||||
|
|
||||||
def nav(self, page, user):
|
def nav_get_prev_comic(self, user) -> str:
|
||||||
out = self.Navigation(cur_index=page, cur_path=urlsafe_base64_encode(self.selector.bytes))
|
base_dir = settings.COMIC_BOOK_VOLUME
|
||||||
if page == 0:
|
|
||||||
out.prev_path, out.prev_index = self.nav_get_prev_comic(user)
|
|
||||||
if out.prev_index == -1:
|
|
||||||
out.q_prev_to_directory = True
|
|
||||||
else:
|
|
||||||
out.prev_index = page - 1
|
|
||||||
out.prev_path = out.cur_path
|
|
||||||
|
|
||||||
out.next_path, out.next_index = self.nav_get_next_comic(user)
|
|
||||||
if out.next_index == -1:
|
|
||||||
out.q_next_to_directory = True
|
|
||||||
return out
|
|
||||||
|
|
||||||
def nav_get_prev_comic(self, user):
|
|
||||||
base_dir = Setting.objects.get(name="BASE_DIR").value
|
|
||||||
if self.directory:
|
if self.directory:
|
||||||
folder = path.join(base_dir, self.directory.path)
|
folder = Path(base_dir, self.directory.path)
|
||||||
else:
|
else:
|
||||||
folder = base_dir
|
folder = base_dir
|
||||||
dir_list = ComicBook.get_ordered_dir_list(folder)
|
dir_list = ComicBook.get_ordered_dir_list(folder)
|
||||||
@@ -159,11 +150,15 @@ class ComicBook(models.Model):
|
|||||||
comic_path = urlsafe_base64_encode(self.directory.selector.bytes)
|
comic_path = urlsafe_base64_encode(self.directory.selector.bytes)
|
||||||
else:
|
else:
|
||||||
comic_path = ""
|
comic_path = ""
|
||||||
index = -1
|
|
||||||
else:
|
else:
|
||||||
prev_comic = dir_list[comic_index - 1]
|
prev_comic = dir_list[comic_index - 1]
|
||||||
|
|
||||||
if not path.isdir(path.join(folder, prev_comic)):
|
if Path(folder, prev_comic).is_dir():
|
||||||
|
if self.directory:
|
||||||
|
comic_path = urlsafe_base64_encode(self.directory.selector.bytes)
|
||||||
|
else:
|
||||||
|
comic_path = ""
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
if self.directory:
|
if self.directory:
|
||||||
book = ComicBook.objects.get(file_name=prev_comic, directory=self.directory)
|
book = ComicBook.objects.get(file_name=prev_comic, directory=self.directory)
|
||||||
@@ -175,20 +170,14 @@ class ComicBook(models.Model):
|
|||||||
else:
|
else:
|
||||||
book = ComicBook.process_comic_book(prev_comic)
|
book = ComicBook.process_comic_book(prev_comic)
|
||||||
cs, _ = ComicStatus.objects.get_or_create(comic=book, user=user)
|
cs, _ = ComicStatus.objects.get_or_create(comic=book, user=user)
|
||||||
index = cs.last_read_page
|
|
||||||
comic_path = urlsafe_base64_encode(book.selector.bytes)
|
comic_path = urlsafe_base64_encode(book.selector.bytes)
|
||||||
else:
|
|
||||||
if self.directory:
|
return comic_path
|
||||||
comic_path = urlsafe_base64_encode(self.directory.selector.bytes)
|
|
||||||
else:
|
|
||||||
comic_path = ""
|
|
||||||
index = -1
|
|
||||||
return comic_path, index
|
|
||||||
|
|
||||||
def nav_get_next_comic(self, user):
|
def nav_get_next_comic(self, user):
|
||||||
base_dir = Setting.objects.get(name="BASE_DIR").value
|
base_dir = settings.COMIC_BOOK_VOLUME
|
||||||
if self.directory:
|
if self.directory:
|
||||||
folder = path.join(base_dir, self.directory.path)
|
folder = Path(base_dir, self.directory.path)
|
||||||
else:
|
else:
|
||||||
folder = base_dir
|
folder = base_dir
|
||||||
dir_list = ComicBook.get_ordered_dir_list(folder)
|
dir_list = ComicBook.get_ordered_dir_list(folder)
|
||||||
@@ -216,15 +205,12 @@ class ComicBook(models.Model):
|
|||||||
if type(book) is str:
|
if type(book) is str:
|
||||||
raise IndexError
|
raise IndexError
|
||||||
comic_path = urlsafe_base64_encode(book.selector.bytes)
|
comic_path = urlsafe_base64_encode(book.selector.bytes)
|
||||||
cs, _ = ComicStatus.objects.get_or_create(comic=book, user=user)
|
|
||||||
index = cs.last_read_page
|
|
||||||
except IndexError:
|
except IndexError:
|
||||||
if self.directory:
|
if self.directory:
|
||||||
comic_path = urlsafe_base64_encode(self.directory.selector.bytes)
|
comic_path = urlsafe_base64_encode(self.directory.selector.bytes)
|
||||||
else:
|
else:
|
||||||
comic_path = ""
|
comic_path = ""
|
||||||
index = -1
|
return comic_path
|
||||||
return comic_path, index
|
|
||||||
|
|
||||||
class DirFile:
|
class DirFile:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -261,11 +247,11 @@ class ComicBook(models.Model):
|
|||||||
return book
|
return book
|
||||||
except ComicBook.DoesNotExist:
|
except ComicBook.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
base_dir = Setting.objects.get(name="BASE_DIR").value
|
base_dir = settings.COMIC_BOOK_VOLUME
|
||||||
if directory:
|
if directory:
|
||||||
comic_full_path = path.join(base_dir, directory.get_path(), comic_file_name)
|
comic_full_path = Path(base_dir, directory.get_path(), comic_file_name)
|
||||||
else:
|
else:
|
||||||
comic_full_path = path.join(base_dir, comic_file_name)
|
comic_full_path = Path(base_dir, comic_file_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cbx = rarfile.RarFile(comic_full_path)
|
cbx = rarfile.RarFile(comic_full_path)
|
||||||
@@ -327,7 +313,7 @@ class ComicBook(models.Model):
|
|||||||
directories = []
|
directories = []
|
||||||
files = []
|
files = []
|
||||||
for item in listdir(folder):
|
for item in listdir(folder):
|
||||||
if path.isdir(path.join(folder, item)):
|
if Path(folder, item).is_dir():
|
||||||
directories.append(item)
|
directories.append(item)
|
||||||
else:
|
else:
|
||||||
files.append(item)
|
files.append(item)
|
||||||
|
|||||||
2087
comic/rarfile.py
2087
comic/rarfile.py
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,12 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from os import path
|
# from os import path
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.utils.http import urlsafe_base64_encode
|
from django.utils.http import urlsafe_base64_encode
|
||||||
|
from django.conf import settings
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from comic.models import ComicBook, ComicPage, ComicStatus, Directory, Setting
|
from comic.models import ComicBook, ComicPage, ComicStatus, Directory, Setting
|
||||||
from comic.util import generate_directory
|
from comic.util import generate_directory
|
||||||
@@ -12,7 +14,7 @@ from comic.util import generate_directory
|
|||||||
|
|
||||||
class ComicBookTests(TestCase):
|
class ComicBookTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
Setting.objects.create(name="BASE_DIR", value=path.join(os.getcwd(), "comic", "test"))
|
settings.COMIC_BOOK_VOLUME = Path(Path.cwd(), 'test_comics')
|
||||||
User.objects.create_user("test", "test@test.com", "test")
|
User.objects.create_user("test", "test@test.com", "test")
|
||||||
user = User.objects.first()
|
user = User.objects.first()
|
||||||
ComicBook.process_comic_book("test1.rar")
|
ComicBook.process_comic_book("test1.rar")
|
||||||
@@ -53,73 +55,48 @@ class ComicBookTests(TestCase):
|
|||||||
self.assertEqual(content_type, "image/jpeg")
|
self.assertEqual(content_type, "image/jpeg")
|
||||||
self.assertEqual(img.read(), b"img1.jpg")
|
self.assertEqual(img.read(), b"img1.jpg")
|
||||||
|
|
||||||
def test_nav_first_page_with_folder_above(self):
|
def test_nav_with_folder_above(self):
|
||||||
|
user = User.objects.get(username="test")
|
||||||
|
generate_directory(user)
|
||||||
book = ComicBook.objects.get(file_name="test1.rar")
|
book = ComicBook.objects.get(file_name="test1.rar")
|
||||||
user = User.objects.get(username="test")
|
|
||||||
nav = book.nav(0, user)
|
nav = book.nav(user)
|
||||||
self.assertEqual(nav.next_index, 1)
|
|
||||||
self.assertEqual(nav.next_path, urlsafe_base64_encode(book.selector.bytes))
|
|
||||||
self.assertEqual(nav.prev_index, -1)
|
|
||||||
self.assertEqual(nav.prev_path, "")
|
self.assertEqual(nav.prev_path, "")
|
||||||
self.assertEqual(nav.cur_index, 0)
|
|
||||||
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
||||||
self.assertEqual(nav.q_prev_to_directory, True)
|
|
||||||
self.assertEqual(nav.q_next_to_directory, False)
|
|
||||||
|
|
||||||
def test_nav_first_page_with_comic_above(self):
|
def test_nav_with_comic_above(self):
|
||||||
prev_book = ComicBook.objects.get(file_name="test1.rar")
|
user = User.objects.get(username="test")
|
||||||
|
generate_directory(user)
|
||||||
|
prev_book = ComicBook.objects.get(file_name="test1.rar", directory__isnull=True)
|
||||||
book = ComicBook.objects.get(file_name="test2.rar", directory__isnull=True)
|
book = ComicBook.objects.get(file_name="test2.rar", directory__isnull=True)
|
||||||
user = User.objects.get(username="test")
|
next_book = ComicBook.objects.get(file_name="test3.rar", directory__isnull=True)
|
||||||
|
|
||||||
|
|
||||||
|
nav = book.nav(user)
|
||||||
|
|
||||||
nav = book.nav(0, user)
|
|
||||||
self.assertEqual(nav.next_index, 1)
|
|
||||||
self.assertEqual(nav.next_path, urlsafe_base64_encode(book.selector.bytes))
|
|
||||||
self.assertEqual(nav.prev_index, 0)
|
|
||||||
self.assertEqual(nav.prev_path, urlsafe_base64_encode(prev_book.selector.bytes))
|
self.assertEqual(nav.prev_path, urlsafe_base64_encode(prev_book.selector.bytes))
|
||||||
self.assertEqual(nav.cur_index, 0)
|
|
||||||
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
||||||
self.assertEqual(nav.q_prev_to_directory, False)
|
self.assertEqual(nav.next_path, urlsafe_base64_encode(next_book.selector.bytes))
|
||||||
self.assertEqual(nav.q_next_to_directory, False)
|
|
||||||
|
|
||||||
def test_nav_last_page_with_comic_below(self):
|
def test_nav_with_comic_below(self):
|
||||||
user = User.objects.get(username="test")
|
user = User.objects.get(username="test")
|
||||||
|
generate_directory(user)
|
||||||
book = ComicBook.objects.get(file_name="test1.rar", directory__isnull=True)
|
book = ComicBook.objects.get(file_name="test1.rar", directory__isnull=True)
|
||||||
next_book = ComicBook.objects.get(file_name="test2.rar", directory__isnull=True)
|
next_book = ComicBook.objects.get(file_name="test2.rar", directory__isnull=True)
|
||||||
nav = book.nav(3, user)
|
nav = book.nav(user)
|
||||||
self.assertEqual(nav.next_index, 2)
|
|
||||||
|
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
||||||
self.assertEqual(nav.next_path, urlsafe_base64_encode(next_book.selector.bytes))
|
self.assertEqual(nav.next_path, urlsafe_base64_encode(next_book.selector.bytes))
|
||||||
self.assertEqual(nav.prev_index, 2)
|
|
||||||
self.assertEqual(nav.prev_path, urlsafe_base64_encode(book.selector.bytes))
|
|
||||||
self.assertEqual(nav.cur_index, 3)
|
|
||||||
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
|
||||||
self.assertEqual(nav.q_prev_to_directory, False)
|
|
||||||
self.assertEqual(nav.q_next_to_directory, False)
|
|
||||||
|
|
||||||
def test_nav_last_page_with_nothing_below(self):
|
def test_nav_with_nothing_below(self):
|
||||||
user = User.objects.get(username="test")
|
user = User.objects.get(username="test")
|
||||||
|
generate_directory(user)
|
||||||
book = ComicBook.objects.get(file_name="test4.rar")
|
book = ComicBook.objects.get(file_name="test4.rar")
|
||||||
nav = book.nav(3, user)
|
nav = book.nav(user)
|
||||||
self.assertEqual(nav.next_index, -1)
|
|
||||||
self.assertEqual(nav.next_path, "")
|
|
||||||
self.assertEqual(nav.prev_index, 2)
|
|
||||||
self.assertEqual(nav.prev_path, urlsafe_base64_encode(book.selector.bytes))
|
|
||||||
self.assertEqual(nav.cur_index, 3)
|
|
||||||
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
|
||||||
self.assertEqual(nav.q_prev_to_directory, False)
|
|
||||||
self.assertEqual(nav.q_next_to_directory, True)
|
|
||||||
|
|
||||||
def test_nav_in_comic(self):
|
|
||||||
user = User.objects.get(username="test")
|
|
||||||
book = ComicBook.objects.get(file_name="test1.rar", directory__isnull=True)
|
|
||||||
nav = book.nav(1, user)
|
|
||||||
self.assertEqual(nav.next_index, 2)
|
|
||||||
self.assertEqual(nav.next_path, urlsafe_base64_encode(book.selector.bytes))
|
|
||||||
self.assertEqual(nav.prev_index, 0)
|
|
||||||
self.assertEqual(nav.prev_path, urlsafe_base64_encode(book.selector.bytes))
|
|
||||||
self.assertEqual(nav.cur_index, 1)
|
|
||||||
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
self.assertEqual(nav.cur_path, urlsafe_base64_encode(book.selector.bytes))
|
||||||
self.assertEqual(nav.q_prev_to_directory, False)
|
self.assertEqual(nav.next_path, "")
|
||||||
self.assertEqual(nav.q_next_to_directory, False)
|
|
||||||
|
|
||||||
def test_generate_directory(self):
|
def test_generate_directory(self):
|
||||||
user = User.objects.get(username="test")
|
user = User.objects.get(username="test")
|
||||||
@@ -127,7 +104,7 @@ class ComicBookTests(TestCase):
|
|||||||
dir1 = folders[0]
|
dir1 = folders[0]
|
||||||
self.assertEqual(dir1.name, "test_folder")
|
self.assertEqual(dir1.name, "test_folder")
|
||||||
self.assertEqual(dir1.type, "directory")
|
self.assertEqual(dir1.type, "directory")
|
||||||
self.assertEqual(dir1.icon, "glyphicon-folder-open")
|
self.assertEqual("fa-folder-open", dir1.icon)
|
||||||
d = Directory.objects.get(name="test_folder", parent__isnull=True)
|
d = Directory.objects.get(name="test_folder", parent__isnull=True)
|
||||||
location = "/comic/{0}/".format(urlsafe_base64_encode(d.selector.bytes))
|
location = "/comic/{0}/".format(urlsafe_base64_encode(d.selector.bytes))
|
||||||
self.assertEqual(dir1.location, location)
|
self.assertEqual(dir1.location, location)
|
||||||
@@ -136,27 +113,27 @@ class ComicBookTests(TestCase):
|
|||||||
dir2 = folders[1]
|
dir2 = folders[1]
|
||||||
self.assertEqual(dir2.name, "test1.rar")
|
self.assertEqual(dir2.name, "test1.rar")
|
||||||
self.assertEqual(dir2.type, "book")
|
self.assertEqual(dir2.type, "book")
|
||||||
self.assertEqual(dir2.icon, "glyphicon-book")
|
self.assertEqual("fa-book", dir2.icon)
|
||||||
c = ComicBook.objects.get(file_name="test1.rar", directory__isnull=True)
|
c = ComicBook.objects.get(file_name="test1.rar", directory__isnull=True)
|
||||||
location = "/comic/read/{0}/{1}/".format(urlsafe_base64_encode(c.selector.bytes), "0")
|
location = "/comic/read/{0}/".format(urlsafe_base64_encode(c.selector.bytes))
|
||||||
self.assertEqual(dir2.location, location)
|
self.assertEqual(dir2.location, location)
|
||||||
self.assertEqual(dir2.label, '<center><span class="label label-default">Unread</span></center>')
|
self.assertEqual(dir2.label, '<center><span class="label label-default">Unread</span></center>')
|
||||||
|
|
||||||
dir3 = folders[2]
|
dir3 = folders[2]
|
||||||
self.assertEqual(dir3.name, "test2.rar")
|
self.assertEqual(dir3.name, "test2.rar")
|
||||||
self.assertEqual(dir3.type, "book")
|
self.assertEqual(dir3.type, "book")
|
||||||
self.assertEqual(dir3.icon, "glyphicon-book")
|
self.assertEqual("fa-book", dir3.icon)
|
||||||
c = ComicBook.objects.get(file_name="test2.rar", directory__isnull=True)
|
c = ComicBook.objects.get(file_name="test2.rar", directory__isnull=True)
|
||||||
location = "/comic/read/{0}/{1}/".format(urlsafe_base64_encode(c.selector.bytes), "2")
|
location = "/comic/read/{0}/".format(urlsafe_base64_encode(c.selector.bytes))
|
||||||
self.assertEqual(dir3.location, location)
|
self.assertEqual(dir3.location, location)
|
||||||
self.assertEqual(dir3.label, '<center><span class="label label-primary">3/4</span></center>')
|
self.assertEqual(dir3.label, '<center><span class="label label-primary">3/4</span></center>')
|
||||||
|
|
||||||
dir4 = folders[3]
|
dir4 = folders[3]
|
||||||
self.assertEqual(dir4.name, "test3.rar")
|
self.assertEqual(dir4.name, "test3.rar")
|
||||||
self.assertEqual(dir4.type, "book")
|
self.assertEqual(dir4.type, "book")
|
||||||
self.assertEqual(dir3.icon, "glyphicon-book")
|
self.assertEqual("fa-book", dir3.icon)
|
||||||
c = ComicBook.objects.get(file_name="test3.rar", directory__isnull=True)
|
c = ComicBook.objects.get(file_name="test3.rar", directory__isnull=True)
|
||||||
location = "/comic/read/{0}/{1}/".format(urlsafe_base64_encode(c.selector.bytes), "0")
|
location = "/comic/read/{0}/".format(urlsafe_base64_encode(c.selector.bytes))
|
||||||
self.assertEqual(dir4.location, location)
|
self.assertEqual(dir4.location, location)
|
||||||
self.assertEqual(dir4.label, '<center><span class="label label-default">Unread</span></center>')
|
self.assertEqual(dir4.label, '<center><span class="label label-default">Unread</span></center>')
|
||||||
|
|
||||||
@@ -217,8 +194,8 @@ class ComicBookTests(TestCase):
|
|||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
c.login(username="test", password="test")
|
c.login(username="test", password="test")
|
||||||
generate_directory(User.objects.first())
|
user = User.objects.get(username="test")
|
||||||
ComicStatus.objects.all().delete()
|
folders = generate_directory(user)
|
||||||
|
|
||||||
req_data = {"start": "0", "length": "10", "search[value]": "", "order[0][dir]": "desc"}
|
req_data = {"start": "0", "length": "10", "search[value]": "", "order[0][dir]": "desc"}
|
||||||
response = c.post("/comic/recent/json/", req_data)
|
response = c.post("/comic/recent/json/", req_data)
|
||||||
@@ -234,12 +211,12 @@ class ComicBookTests(TestCase):
|
|||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"date": book.date_added.strftime("%d/%m/%y-%H:%M"),
|
"date": book.date_added.strftime("%d/%m/%y-%H:%M"),
|
||||||
"icon": '<span class="glyphicon glyphicon-book"></span>',
|
"icon": '<span class="fa fa-book"></span>',
|
||||||
"label": '<center><span class="label ' 'label-default">Unread</span></center>',
|
"label": '<center><span class="label ' 'label-default">Unread</span></center>',
|
||||||
"name": "test1.rar",
|
"name": "test1.rar",
|
||||||
"selector": urlsafe_base64_encode(book.selector.bytes),
|
"selector": urlsafe_base64_encode(book.selector.bytes),
|
||||||
"type": "book",
|
"type": "book",
|
||||||
"url": f"/comic/read/" f"{urlsafe_base64_encode(book.selector.bytes)}/0/",
|
"url": f"/comic/read/" f"{urlsafe_base64_encode(book.selector.bytes)}/",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"recordsFiltered": 1,
|
"recordsFiltered": 1,
|
||||||
@@ -250,17 +227,6 @@ class ComicBookTests(TestCase):
|
|||||||
req_data["order[0][dir]"] = 3
|
req_data["order[0][dir]"] = 3
|
||||||
response = c.post("/comic/recent/json/", req_data)
|
response = c.post("/comic/recent/json/", req_data)
|
||||||
|
|
||||||
self.assertListEqual(
|
|
||||||
[x["name"] for x in json.loads(response.content)["data"]],
|
|
||||||
["test1.rar", "test2.rar", "test4.rar", "test3.rar"],
|
|
||||||
)
|
|
||||||
|
|
||||||
req_data["order[0][dir]"] = 2
|
|
||||||
response = c.post("/comic/recent/json/", req_data)
|
|
||||||
self.assertListEqual(
|
|
||||||
[x["name"] for x in json.loads(response.content)["data"]],
|
|
||||||
["test1.rar", "test2.rar", "test4.rar", "test3.rar"],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_comic_edit(self):
|
def test_comic_edit(self):
|
||||||
c = Client()
|
c = Client()
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from os import listdir, path
|
from os import listdir
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from django.db.models import Count, Q, F, Max, ExpressionWrapper, Prefetch
|
from django.conf import settings
|
||||||
|
from django.db.models import Count, Q, F
|
||||||
from django.utils.http import urlsafe_base64_encode
|
from django.utils.http import urlsafe_base64_encode
|
||||||
from collections import Counter
|
|
||||||
from .models import ComicBook, Directory, Setting, ComicStatus
|
from .models import ComicBook, Directory, ComicStatus
|
||||||
|
|
||||||
|
|
||||||
def generate_title_from_path(file_path):
|
def generate_title_from_path(file_path: Path):
|
||||||
if file_path == "":
|
if file_path == "Home":
|
||||||
return "CBWebReader"
|
return "CBWebReader"
|
||||||
return "CBWebReader - " + " - ".join(file_path.split(path.sep))
|
return f'CBWebReader - {" - ".join(p for p in file_path.parts)}'
|
||||||
|
|
||||||
|
|
||||||
class Menu:
|
class Menu:
|
||||||
@@ -122,21 +124,23 @@ def generate_directory(user, directory=False):
|
|||||||
:type user: User
|
:type user: User
|
||||||
:type directory: Directory
|
:type directory: Directory
|
||||||
"""
|
"""
|
||||||
base_dir = Setting.objects.get(name="BASE_DIR").value
|
base_dir = settings.COMIC_BOOK_VOLUME
|
||||||
files = []
|
files = []
|
||||||
if directory:
|
if directory:
|
||||||
ordered_dir_list = listdir(path.join(base_dir, directory.path))
|
dir_path = Path(base_dir, directory.path)
|
||||||
dir_list = [x for x in ordered_dir_list if path.isdir(path.join(base_dir, directory.path, x))]
|
# ordered_dir_list = sorted(dir_path.glob('*'))
|
||||||
|
dir_list = [x for x in sorted(dir_path.glob('*')) if Path(base_dir, directory.path, x).is_dir()]
|
||||||
else:
|
else:
|
||||||
ordered_dir_list = listdir(base_dir)
|
dir_path = base_dir
|
||||||
dir_list = [x for x in ordered_dir_list if path.isdir(path.join(base_dir, x))]
|
# ordered_dir_list = base_dir.glob('*')
|
||||||
file_list = [x for x in ordered_dir_list if x not in dir_list]
|
dir_list = [x for x in sorted(dir_path.glob('*')) if Path(base_dir, x).is_dir()]
|
||||||
|
file_list = [x for x in sorted(dir_path.glob('*')) if x.is_file()]
|
||||||
if directory:
|
if directory:
|
||||||
dir_list_obj = Directory.objects.filter(name__in=dir_list, parent=directory)
|
dir_list_obj = Directory.objects.filter(name__in=[x.name for x in dir_list], parent=directory)
|
||||||
file_list_obj = ComicBook.objects.filter(file_name__in=file_list, directory=directory)
|
file_list_obj = ComicBook.objects.filter(file_name__in=[x.name for x in file_list], directory=directory)
|
||||||
else:
|
else:
|
||||||
dir_list_obj = Directory.objects.filter(name__in=dir_list, parent__isnull=True)
|
dir_list_obj = Directory.objects.filter(name__in=[x.name for x in dir_list], parent__isnull=True)
|
||||||
file_list_obj = ComicBook.objects.filter(file_name__in=file_list, directory__isnull=True)
|
file_list_obj = ComicBook.objects.filter(file_name__in=[x.name for x in file_list], directory__isnull=True)
|
||||||
|
|
||||||
dir_list_obj = dir_list_obj.annotate(
|
dir_list_obj = dir_list_obj.annotate(
|
||||||
total=Count('comicbook', distinct=True),
|
total=Count('comicbook', distinct=True),
|
||||||
@@ -162,19 +166,19 @@ def generate_directory(user, directory=False):
|
|||||||
df = DirFile()
|
df = DirFile()
|
||||||
df.populate_directory(directory_obj, user)
|
df.populate_directory(directory_obj, user)
|
||||||
files.append(df)
|
files.append(df)
|
||||||
dir_list.remove(directory_obj.name)
|
dir_list.remove(Path(dir_path, directory_obj.name))
|
||||||
|
|
||||||
for file_obj in file_list_obj:
|
for file_obj in file_list_obj:
|
||||||
df = DirFile()
|
df = DirFile()
|
||||||
df.populate_comic(file_obj, user)
|
df.populate_comic(file_obj, user)
|
||||||
files.append(df)
|
files.append(df)
|
||||||
file_list.remove(file_obj.file_name)
|
file_list.remove(Path(dir_path, file_obj.file_name))
|
||||||
|
|
||||||
for directory_name in dir_list:
|
for directory_name in dir_list:
|
||||||
if directory:
|
if directory:
|
||||||
directory_obj = Directory(name=directory_name, parent=directory)
|
directory_obj = Directory(name=directory_name.name, parent=directory)
|
||||||
else:
|
else:
|
||||||
directory_obj = Directory(name=directory_name)
|
directory_obj = Directory(name=directory_name.name)
|
||||||
directory_obj.save()
|
directory_obj.save()
|
||||||
directory_obj.total = 0
|
directory_obj.total = 0
|
||||||
directory_obj.total_read = 0
|
directory_obj.total_read = 0
|
||||||
@@ -183,8 +187,8 @@ def generate_directory(user, directory=False):
|
|||||||
files.append(df)
|
files.append(df)
|
||||||
|
|
||||||
for file_name in file_list:
|
for file_name in file_list:
|
||||||
if file_name.lower()[-4:] in [".rar", ".zip", ".cbr", ".cbz", ".pdf"]:
|
if file_name.suffix.lower() in [".rar", ".zip", ".cbr", ".cbz", ".pdf"]:
|
||||||
book = ComicBook.process_comic_book(file_name, directory)
|
book = ComicBook.process_comic_book(file_name.name, directory)
|
||||||
df = DirFile()
|
df = DirFile()
|
||||||
df.populate_comic(book, user)
|
df.populate_comic(book, user)
|
||||||
files.append(df)
|
files.append(df)
|
||||||
@@ -226,14 +230,3 @@ def generate_dir_status(total, total_read):
|
|||||||
elif total_read == 0:
|
elif total_read == 0:
|
||||||
return '<center><span class="label label-default">Unread</span></center>'
|
return '<center><span class="label label-default">Unread</span></center>'
|
||||||
return f'<center><span class="label label-primary">{total_read}/{total}</span></center>'
|
return f'<center><span class="label label-primary">{total_read}/{total}</span></center>'
|
||||||
|
|
||||||
|
|
||||||
def get_ordered_dir_list(folder):
|
|
||||||
directories = []
|
|
||||||
files = []
|
|
||||||
for item in listdir(folder):
|
|
||||||
if path.isdir(path.join(folder, item)):
|
|
||||||
directories.append(item)
|
|
||||||
else:
|
|
||||||
files.append(item)
|
|
||||||
return sorted(directories) + sorted(files)
|
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
try:
|
import json
|
||||||
import ujson as json
|
|
||||||
except ImportError:
|
|
||||||
import json
|
|
||||||
import uuid
|
import uuid
|
||||||
from os import path
|
|
||||||
|
|
||||||
from django.contrib.auth import authenticate, login
|
from django.contrib.auth import authenticate, login
|
||||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
@@ -32,12 +28,14 @@ from .util import (
|
|||||||
@ensure_csrf_cookie
|
@ensure_csrf_cookie
|
||||||
@login_required
|
@login_required
|
||||||
def comic_list(request, directory_selector=False):
|
def comic_list(request, directory_selector=False):
|
||||||
try:
|
if User.objects.all().count() == 0:
|
||||||
base_dir = Setting.objects.get(name="BASE_DIR").value
|
|
||||||
except Setting.DoesNotExist:
|
|
||||||
return redirect("/comic/settings/")
|
|
||||||
if not path.isdir(base_dir):
|
|
||||||
return redirect("/comic/settings/")
|
return redirect("/comic/settings/")
|
||||||
|
# try:
|
||||||
|
# base_dir = Setting.objects.get(name="BASE_DIR").value
|
||||||
|
# except Setting.DoesNotExist:
|
||||||
|
# return redirect("/comic/settings/")
|
||||||
|
# if not path.isdir(base_dir):
|
||||||
|
# return redirect("/comic/settings/")
|
||||||
|
|
||||||
if directory_selector:
|
if directory_selector:
|
||||||
selector = uuid.UUID(bytes=urlsafe_base64_decode(directory_selector))
|
selector = uuid.UUID(bytes=urlsafe_base64_decode(directory_selector))
|
||||||
@@ -311,7 +309,7 @@ def read_comic(request, comic_selector):
|
|||||||
"book": book,
|
"book": book,
|
||||||
"pages": pages,
|
"pages": pages,
|
||||||
# "orig_file_name": book.page_name(page),
|
# "orig_file_name": book.page_name(page),
|
||||||
"nav": book.nav(0, request.user),
|
"nav": book.nav(request.user),
|
||||||
"status": status,
|
"status": status,
|
||||||
"breadcrumbs": generate_breadcrumbs_from_path(book.directory, book),
|
"breadcrumbs": generate_breadcrumbs_from_path(book.directory, book),
|
||||||
"menu": Menu(request.user),
|
"menu": Menu(request.user),
|
||||||
@@ -369,9 +367,6 @@ def initial_setup(request):
|
|||||||
)
|
)
|
||||||
user.set_password(form.cleaned_data["password"])
|
user.set_password(form.cleaned_data["password"])
|
||||||
user.save()
|
user.save()
|
||||||
base_dir, _ = Setting.objects.get_or_create(name="BASE_DIR")
|
|
||||||
base_dir.value = form.cleaned_data["base_dir"]
|
|
||||||
base_dir.save()
|
|
||||||
user = authenticate(username=form.cleaned_data["username"], password=form.cleaned_data["password"])
|
user = authenticate(username=form.cleaned_data["username"], password=form.cleaned_data["password"])
|
||||||
login(request, user)
|
login(request, user)
|
||||||
return redirect("/comic/")
|
return redirect("/comic/")
|
||||||
|
|||||||
79
poetry.lock
generated
79
poetry.lock
generated
@@ -60,6 +60,17 @@ category = "main"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "coverage"
|
||||||
|
version = "5.5"
|
||||||
|
description = "Code coverage measurement for Python"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
toml = ["toml"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dj-database-url"
|
name = "dj-database-url"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -267,6 +278,14 @@ category = "main"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rarfile"
|
||||||
|
version = "4.0"
|
||||||
|
description = "RAR archive reader for Python"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.25.1"
|
version = "2.25.1"
|
||||||
@@ -344,7 +363,7 @@ dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
content-hash = "cd143483d60bfe34643734cbc6bfcc7b50d3257f0a900fe34b70fd43b1cf0305"
|
content-hash = "2cf2bf8371f6fa6dcc2a9d8d889387667eed3a994e46797c5113f8ea7e7ffd95"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
asgiref = [
|
asgiref = [
|
||||||
@@ -372,6 +391,60 @@ colorama = [
|
|||||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||||
]
|
]
|
||||||
|
coverage = [
|
||||||
|
{file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"},
|
||||||
|
{file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"},
|
||||||
|
{file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"},
|
||||||
|
{file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"},
|
||||||
|
{file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"},
|
||||||
|
{file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"},
|
||||||
|
{file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"},
|
||||||
|
{file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"},
|
||||||
|
{file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"},
|
||||||
|
{file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"},
|
||||||
|
{file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"},
|
||||||
|
{file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"},
|
||||||
|
{file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"},
|
||||||
|
{file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"},
|
||||||
|
{file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"},
|
||||||
|
{file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"},
|
||||||
|
{file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"},
|
||||||
|
{file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"},
|
||||||
|
{file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"},
|
||||||
|
{file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"},
|
||||||
|
{file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"},
|
||||||
|
{file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"},
|
||||||
|
{file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"},
|
||||||
|
{file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"},
|
||||||
|
{file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"},
|
||||||
|
{file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"},
|
||||||
|
{file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"},
|
||||||
|
{file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"},
|
||||||
|
{file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"},
|
||||||
|
{file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"},
|
||||||
|
{file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"},
|
||||||
|
{file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"},
|
||||||
|
{file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"},
|
||||||
|
{file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"},
|
||||||
|
{file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"},
|
||||||
|
{file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"},
|
||||||
|
{file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"},
|
||||||
|
{file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"},
|
||||||
|
{file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"},
|
||||||
|
{file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"},
|
||||||
|
{file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"},
|
||||||
|
{file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"},
|
||||||
|
{file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
|
||||||
|
]
|
||||||
dj-database-url = [
|
dj-database-url = [
|
||||||
{file = "dj-database-url-0.5.0.tar.gz", hash = "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163"},
|
{file = "dj-database-url-0.5.0.tar.gz", hash = "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163"},
|
||||||
{file = "dj_database_url-0.5.0-py2.py3-none-any.whl", hash = "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9"},
|
{file = "dj_database_url-0.5.0-py2.py3-none-any.whl", hash = "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9"},
|
||||||
@@ -510,6 +583,10 @@ pytz = [
|
|||||||
{file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
|
{file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
|
||||||
{file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"},
|
{file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"},
|
||||||
]
|
]
|
||||||
|
rarfile = [
|
||||||
|
{file = "rarfile-4.0-py3-none-any.whl", hash = "sha256:1094869119012f95c31a6f22cc3a9edbdca61861b805241116adbe2d737b68f8"},
|
||||||
|
{file = "rarfile-4.0.tar.gz", hash = "sha256:67548769229c5bda0827c1663dce3f54644f9dbfba4ae86d4da2b2afd3e602a1"},
|
||||||
|
]
|
||||||
requests = [
|
requests = [
|
||||||
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
|
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
|
||||||
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
|
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ loguru = "^0.5.3"
|
|||||||
django-silk = "^4.1.0"
|
django-silk = "^4.1.0"
|
||||||
mysqlclient = "^2.0.1"
|
mysqlclient = "^2.0.1"
|
||||||
psycopg2 = "^2.8.6"
|
psycopg2 = "^2.8.6"
|
||||||
|
rarfile = "^4.0"
|
||||||
|
coverage = "^5.5"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
|
|
||||||
|
|||||||
0
test_comics/test_folder/blank.txt
Normal file
0
test_comics/test_folder/blank.txt
Normal file
Reference in New Issue
Block a user