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:
2021-04-07 16:30:46 +01:00
parent 9900467413
commit c32f12cce7
15 changed files with 210 additions and 2285 deletions

View File

@@ -13,13 +13,6 @@ class InitialSetupForm(forms.Form):
password_confirm = forms.CharField(
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):
form_data = self.cleaned_data

View File

@@ -1,6 +1,10 @@
import uuid
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.contrib.auth.models import User
@@ -11,7 +15,7 @@ import PyPDF4
import PyPDF4.utils
from comic import rarfile
import rarfile
if 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)
@property
def path(self):
def path(self) -> Path:
return self.get_path()
def get_path(self):
def get_path(self) -> Path:
path_items = self.get_path_items()
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:
p = []
p.append(self.name)
@@ -64,14 +71,14 @@ class Directory(models.Model):
self.parent.get_path_objects(p)
return p
@staticmethod
def get_dir_from_path(file_path):
file_path = file_path.split(path.sep)
print(file_path)
for d in Directory.objects.filter(name=file_path[-1]):
print(d)
if d.get_path_items() == file_path:
return d
# @staticmethod
# def get_dir_from_path(file_path):
# file_path = file_path.split(os_path.sep)
# print(file_path)
# for d in Directory.objects.filter(name=file_path[-1]):
# print(d)
# if d.get_path_items() == file_path:
# return d
class ComicBook(models.Model):
@@ -89,15 +96,15 @@ class ComicBook(models.Model):
return urlsafe_base64_encode(self.selector.bytes)
def get_pdf(self):
base_dir = Setting.objects.get(name="BASE_DIR").value
return path.join(base_dir, self.directory.get_path(), self.file_name)
base_dir = settings.COMIC_BOOK_VOLUME
return Path(base_dir, self.directory.get_path(), self.file_name)
def get_image(self, page):
base_dir = Setting.objects.get(name="BASE_DIR").value
base_dir = settings.COMIC_BOOK_VOLUME
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:
archive_path = path.join(base_dir, self.file_name)
archive_path = Path(base_dir, self.file_name)
try:
archive = rarfile.RarFile(archive_path)
except rarfile.NotRarFile:
@@ -117,39 +124,23 @@ class ComicBook(models.Model):
def page_count(self):
return ComicPage.objects.filter(Comic=self).count()
@dataclass
class Navigation:
next_index = 0
next_path = ""
prev_index = 0
prev_path = ""
cur_index = 0
cur_path = ""
q_prev_to_directory = False
q_next_to_directory = False
next_path: str
prev_path: str
cur_path: str
def __init__(self, **kwargs):
for arg in kwargs:
setattr(self, arg, kwargs[arg])
def nav(self, user):
return self.Navigation(
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):
out = self.Navigation(cur_index=page, cur_path=urlsafe_base64_encode(self.selector.bytes))
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
def nav_get_prev_comic(self, user) -> str:
base_dir = settings.COMIC_BOOK_VOLUME
if self.directory:
folder = path.join(base_dir, self.directory.path)
folder = Path(base_dir, self.directory.path)
else:
folder = base_dir
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)
else:
comic_path = ""
index = -1
else:
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:
if self.directory:
book = ComicBook.objects.get(file_name=prev_comic, directory=self.directory)
@@ -175,20 +170,14 @@ class ComicBook(models.Model):
else:
book = ComicBook.process_comic_book(prev_comic)
cs, _ = ComicStatus.objects.get_or_create(comic=book, user=user)
index = cs.last_read_page
comic_path = urlsafe_base64_encode(book.selector.bytes)
else:
if self.directory:
comic_path = urlsafe_base64_encode(self.directory.selector.bytes)
else:
comic_path = ""
index = -1
return comic_path, index
return comic_path
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:
folder = path.join(base_dir, self.directory.path)
folder = Path(base_dir, self.directory.path)
else:
folder = base_dir
dir_list = ComicBook.get_ordered_dir_list(folder)
@@ -216,15 +205,12 @@ class ComicBook(models.Model):
if type(book) is str:
raise IndexError
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:
if self.directory:
comic_path = urlsafe_base64_encode(self.directory.selector.bytes)
else:
comic_path = ""
index = -1
return comic_path, index
return comic_path
class DirFile:
def __init__(self):
@@ -261,11 +247,11 @@ class ComicBook(models.Model):
return book
except ComicBook.DoesNotExist:
pass
base_dir = Setting.objects.get(name="BASE_DIR").value
base_dir = settings.COMIC_BOOK_VOLUME
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:
comic_full_path = path.join(base_dir, comic_file_name)
comic_full_path = Path(base_dir, comic_file_name)
try:
cbx = rarfile.RarFile(comic_full_path)
@@ -327,7 +313,7 @@ class ComicBook(models.Model):
directories = []
files = []
for item in listdir(folder):
if path.isdir(path.join(folder, item)):
if Path(folder, item).is_dir():
directories.append(item)
else:
files.append(item)

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,10 +1,12 @@
import json
import os
from os import path
# from os import path
from django.contrib.auth.models import User
from django.test import Client, TestCase
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.util import generate_directory
@@ -12,7 +14,7 @@ from comic.util import generate_directory
class ComicBookTests(TestCase):
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 = User.objects.first()
ComicBook.process_comic_book("test1.rar")
@@ -53,73 +55,48 @@ class ComicBookTests(TestCase):
self.assertEqual(content_type, "image/jpeg")
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")
user = User.objects.get(username="test")
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, -1)
nav = book.nav(user)
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.q_prev_to_directory, True)
self.assertEqual(nav.q_next_to_directory, False)
def test_nav_first_page_with_comic_above(self):
prev_book = ComicBook.objects.get(file_name="test1.rar")
def test_nav_with_comic_above(self):
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)
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.cur_index, 0)
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)
self.assertEqual(nav.next_path, urlsafe_base64_encode(next_book.selector.bytes))
def test_nav_last_page_with_comic_below(self):
def test_nav_with_comic_below(self):
user = User.objects.get(username="test")
generate_directory(user)
book = ComicBook.objects.get(file_name="test1.rar", directory__isnull=True)
next_book = ComicBook.objects.get(file_name="test2.rar", directory__isnull=True)
nav = book.nav(3, user)
self.assertEqual(nav.next_index, 2)
nav = book.nav(user)
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.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")
generate_directory(user)
book = ComicBook.objects.get(file_name="test4.rar")
nav = book.nav(3, 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)
nav = book.nav(user)
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.q_prev_to_directory, False)
self.assertEqual(nav.q_next_to_directory, False)
self.assertEqual(nav.next_path, "")
def test_generate_directory(self):
user = User.objects.get(username="test")
@@ -127,7 +104,7 @@ class ComicBookTests(TestCase):
dir1 = folders[0]
self.assertEqual(dir1.name, "test_folder")
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)
location = "/comic/{0}/".format(urlsafe_base64_encode(d.selector.bytes))
self.assertEqual(dir1.location, location)
@@ -136,27 +113,27 @@ class ComicBookTests(TestCase):
dir2 = folders[1]
self.assertEqual(dir2.name, "test1.rar")
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)
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.label, '<center><span class="label label-default">Unread</span></center>')
dir3 = folders[2]
self.assertEqual(dir3.name, "test2.rar")
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)
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.label, '<center><span class="label label-primary">3/4</span></center>')
dir4 = folders[3]
self.assertEqual(dir4.name, "test3.rar")
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)
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.label, '<center><span class="label label-default">Unread</span></center>')
@@ -217,8 +194,8 @@ class ComicBookTests(TestCase):
self.assertEqual(response.status_code, 302)
c.login(username="test", password="test")
generate_directory(User.objects.first())
ComicStatus.objects.all().delete()
user = User.objects.get(username="test")
folders = generate_directory(user)
req_data = {"start": "0", "length": "10", "search[value]": "", "order[0][dir]": "desc"}
response = c.post("/comic/recent/json/", req_data)
@@ -234,12 +211,12 @@ class ComicBookTests(TestCase):
"data": [
{
"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>',
"name": "test1.rar",
"selector": urlsafe_base64_encode(book.selector.bytes),
"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,
@@ -250,17 +227,6 @@ class ComicBookTests(TestCase):
req_data["order[0][dir]"] = 3
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):
c = Client()

View File

@@ -1,16 +1,18 @@
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 collections import Counter
from .models import ComicBook, Directory, Setting, ComicStatus
from .models import ComicBook, Directory, ComicStatus
def generate_title_from_path(file_path):
if file_path == "":
def generate_title_from_path(file_path: Path):
if file_path == "Home":
return "CBWebReader"
return "CBWebReader - " + " - ".join(file_path.split(path.sep))
return f'CBWebReader - {" - ".join(p for p in file_path.parts)}'
class Menu:
@@ -122,21 +124,23 @@ def generate_directory(user, directory=False):
:type user: User
:type directory: Directory
"""
base_dir = Setting.objects.get(name="BASE_DIR").value
base_dir = settings.COMIC_BOOK_VOLUME
files = []
if directory:
ordered_dir_list = listdir(path.join(base_dir, directory.path))
dir_list = [x for x in ordered_dir_list if path.isdir(path.join(base_dir, directory.path, x))]
dir_path = Path(base_dir, directory.path)
# 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:
ordered_dir_list = listdir(base_dir)
dir_list = [x for x in ordered_dir_list if path.isdir(path.join(base_dir, x))]
file_list = [x for x in ordered_dir_list if x not in dir_list]
dir_path = base_dir
# ordered_dir_list = base_dir.glob('*')
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:
dir_list_obj = Directory.objects.filter(name__in=dir_list, parent=directory)
file_list_obj = ComicBook.objects.filter(file_name__in=file_list, directory=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=[x.name for x in file_list], directory=directory)
else:
dir_list_obj = Directory.objects.filter(name__in=dir_list, parent__isnull=True)
file_list_obj = ComicBook.objects.filter(file_name__in=file_list, directory__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=[x.name for x in file_list], directory__isnull=True)
dir_list_obj = dir_list_obj.annotate(
total=Count('comicbook', distinct=True),
@@ -162,19 +166,19 @@ def generate_directory(user, directory=False):
df = DirFile()
df.populate_directory(directory_obj, user)
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:
df = DirFile()
df.populate_comic(file_obj, user)
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:
if directory:
directory_obj = Directory(name=directory_name, parent=directory)
directory_obj = Directory(name=directory_name.name, parent=directory)
else:
directory_obj = Directory(name=directory_name)
directory_obj = Directory(name=directory_name.name)
directory_obj.save()
directory_obj.total = 0
directory_obj.total_read = 0
@@ -183,8 +187,8 @@ def generate_directory(user, directory=False):
files.append(df)
for file_name in file_list:
if file_name.lower()[-4:] in [".rar", ".zip", ".cbr", ".cbz", ".pdf"]:
book = ComicBook.process_comic_book(file_name, directory)
if file_name.suffix.lower() in [".rar", ".zip", ".cbr", ".cbz", ".pdf"]:
book = ComicBook.process_comic_book(file_name.name, directory)
df = DirFile()
df.populate_comic(book, user)
files.append(df)
@@ -226,14 +230,3 @@ def generate_dir_status(total, total_read):
elif total_read == 0:
return '<center><span class="label label-default">Unread</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)

View File

@@ -1,9 +1,5 @@
try:
import ujson as json
except ImportError:
import json
import json
import uuid
from os import path
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required, user_passes_test
@@ -32,12 +28,14 @@ from .util import (
@ensure_csrf_cookie
@login_required
def comic_list(request, directory_selector=False):
try:
base_dir = Setting.objects.get(name="BASE_DIR").value
except Setting.DoesNotExist:
return redirect("/comic/settings/")
if not path.isdir(base_dir):
if User.objects.all().count() == 0:
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:
selector = uuid.UUID(bytes=urlsafe_base64_decode(directory_selector))
@@ -311,7 +309,7 @@ def read_comic(request, comic_selector):
"book": book,
"pages": pages,
# "orig_file_name": book.page_name(page),
"nav": book.nav(0, request.user),
"nav": book.nav(request.user),
"status": status,
"breadcrumbs": generate_breadcrumbs_from_path(book.directory, book),
"menu": Menu(request.user),
@@ -369,9 +367,6 @@ def initial_setup(request):
)
user.set_password(form.cleaned_data["password"])
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"])
login(request, user)
return redirect("/comic/")