diff --git a/cbreader/settings/base.py b/cbreader/settings/base.py index edc5d76..ceab053 100644 --- a/cbreader/settings/base.py +++ b/cbreader/settings/base.py @@ -22,10 +22,10 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '=3tf-@u1t7x4%$yr++59+8tspl4ao&r3&!bb6l(t&$#6@bfkwg' +SECRET_KEY = "=3tf-@u1t7x4%$yr++59+8tspl4ao&r3&!bb6l(t&$#6@bfkwg" # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = os.environ.get('DJANGO_DEBUG', True) +DEBUG = os.environ.get("DJANGO_DEBUG", True) ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost").split(",") @@ -33,46 +33,46 @@ ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost").split(",") # Application definition INSTALLED_APPS = ( - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'snowpenguin.django.recaptcha2', - 'comic', - 'comic_auth', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "snowpenguin.django.recaptcha2", + "comic", + "comic_auth", ) 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', + "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", ] -ROOT_URLCONF = 'cbreader.urls' +ROOT_URLCONF = "cbreader.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': ['templates'], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": ["templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ] }, - }, + } ] -WSGI_APPLICATION = 'cbreader.wsgi.application' +WSGI_APPLICATION = "cbreader.wsgi.application" # Database @@ -83,20 +83,15 @@ DATABASE_URL = os.getenv("DATABASE_URL") if DATABASE_URL: DATABASES = {"default": dj_database_url.config(conn_max_age=500)} else: - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } - } + DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3")}} # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ -LANGUAGE_CODE = 'en-ie' +LANGUAGE_CODE = "en-ie" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -108,16 +103,16 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.8/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" -LOGIN_REDIRECT_URL = '/comic/' +LOGIN_REDIRECT_URL = "/comic/" -LOGIN_URL = '/login/' +LOGIN_URL = "/login/" UNRAR_TOOL = os.getenv("UNRAR_TOOL", None) CBREADER_USE_RECAPTCHA = False -RECAPTCHA_PRIVATE_KEY = '' -RECAPTCHA_PUBLIC_KEY = '' +RECAPTCHA_PRIVATE_KEY = "" +RECAPTCHA_PUBLIC_KEY = "" COMIC_DIR = "/media/comics" diff --git a/cbreader/settings/test.py b/cbreader/settings/test.py deleted file mode 100644 index 3c45e67..0000000 --- a/cbreader/settings/test.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -Django settings for cbreader project. - -Generated by 'django-admin startproject' using Django 1.8.2. - -For more information on this file, see -https://docs.djangoproject.com/en/1.8/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.8/ref/settings/ -""" - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import os - -import dj_database_url - -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '=3tf-@u1t7x4%$yr++59+8tspl4ao&r3&!bb6l(t&$#6@bfkwg' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = os.environ.get('DJANGO_DEBUG', True) - -ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost").split(",") - - -# Application definition - -INSTALLED_APPS = ( - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'snowpenguin.django.recaptcha2', - 'comic', - 'comic_auth', -) - -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', -] - -ROOT_URLCONF = 'cbreader.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': ['templates'], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'cbreader.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/1.8/ref/settings/#databases - -DATABASE_URL = os.getenv("TEST_DATABASE_URL") - -if DATABASE_URL: - DATABASES = {"default": dj_database_url.config(conn_max_age=500)} -else: - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } - } - - -# Internationalization -# https://docs.djangoproject.com/en/1.8/topics/i18n/ - -LANGUAGE_CODE = 'en-ie' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.8/howto/static-files/ - -STATIC_URL = '/static/' - -LOGIN_REDIRECT_URL = '/comic/' - -LOGIN_URL = '/login/' - -UNRAR_TOOL = os.getenv("UNRAR_TOOL", None) - -CBREADER_USE_RECAPTCHA = False -RECAPTCHA_PRIVATE_KEY = '' -RECAPTCHA_PUBLIC_KEY = '' - -COMIC_DIR = "/media/comics" diff --git a/cbreader/tests/test_settings.py b/cbreader/tests/test_settings.py new file mode 100644 index 0000000..38afc36 --- /dev/null +++ b/cbreader/tests/test_settings.py @@ -0,0 +1,118 @@ +""" +Django settings for cbreader project. + +Generated by 'django-admin startproject' using Django 1.8.2. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.8/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os + +import dj_database_url + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "=3tf-@u1t7x4%$yr++59+8tspl4ao&r3&!bb6l(t&$#6@bfkwg" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = os.environ.get("DJANGO_DEBUG", True) + +ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost").split(",") + + +# Application definition + +INSTALLED_APPS = ( + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "snowpenguin.django.recaptcha2", + "comic", + "comic_auth", +) + +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", +] + +ROOT_URLCONF = "cbreader.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": ["templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ] + }, + } +] + +WSGI_APPLICATION = "cbreader.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +DATABASE_URL = os.getenv("TEST_DATABASE_URL") + +if DATABASE_URL: + DATABASES = {"default": dj_database_url.config(conn_max_age=500)} +else: + DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3")}} + + +# Internationalization +# https://docs.djangoproject.com/en/1.8/topics/i18n/ + +LANGUAGE_CODE = "en-ie" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.8/howto/static-files/ + +STATIC_URL = "/static/" + +LOGIN_REDIRECT_URL = "/comic/" + +LOGIN_URL = "/login/" + +UNRAR_TOOL = os.getenv("UNRAR_TOOL", None) + +CBREADER_USE_RECAPTCHA = False +RECAPTCHA_PRIVATE_KEY = "" +RECAPTCHA_PUBLIC_KEY = "" + +COMIC_DIR = "/media/comics" diff --git a/cbreader/urls.py b/cbreader/urls.py index a150d95..776732d 100644 --- a/cbreader/urls.py +++ b/cbreader/urls.py @@ -20,11 +20,11 @@ import comic.views import comic_auth.views urlpatterns = [ - url(r'^$', comic.views.comic_redirect), - url(r'^login/', comic_auth.views.comic_login), - url(r'^logout/', comic_auth.views.comic_logout), - url(r'^setup/', comic.views.initial_setup), - url(r'^comic/', include('comic.urls')), - url(r'^admin/', admin.site.urls), + url(r"^$", comic.views.comic_redirect), + url(r"^login/", comic_auth.views.comic_login), + url(r"^logout/", comic_auth.views.comic_logout), + url(r"^setup/", comic.views.initial_setup), + url(r"^comic/", include("comic.urls")), + url(r"^admin/", admin.site.urls), # url(r'^silk/', include('silk.urls', namespace='silk')) ] diff --git a/comic/admin.py b/comic/admin.py index 091d064..516f47a 100644 --- a/comic/admin.py +++ b/comic/admin.py @@ -5,24 +5,24 @@ from comic.models import Setting, ComicBook, ComicPage, ComicStatus, Directory @admin.register(Setting) class SettingAdmin(admin.ModelAdmin): - list_display = ('name', 'value') + list_display = ("name", "value") @admin.register(ComicBook) class ComicBookAdmin(admin.ModelAdmin): - list_display = ['file_name', 'date_added'] - search_fields = ['file_name'] + list_display = ["file_name", "date_added"] + search_fields = ["file_name"] @admin.register(ComicPage) class ComicPageAdmin(admin.ModelAdmin): - list_display = ('Comic', 'index', 'page_file_name', 'content_type') - list_filter = ['Comic'] + list_display = ("Comic", "index", "page_file_name", "content_type") + list_filter = ["Comic"] @admin.register(ComicStatus) class ComicStatusAdmin(admin.ModelAdmin): - list_display = ['user', 'comic', 'last_read_page', 'unread'] + list_display = ["user", "comic", "last_read_page", "unread"] @admin.register(Directory) diff --git a/comic/feeds.py b/comic/feeds.py index 39af198..e340e27 100644 --- a/comic/feeds.py +++ b/comic/feeds.py @@ -19,14 +19,14 @@ class RecentComics(Feed): @staticmethod def items() -> ComicBook: - return ComicBook.objects.order_by('-date_added')[:10] + return ComicBook.objects.order_by("-date_added")[:10] def item_title(self, item: ComicBook) -> str: return item.file_name def item_description(self, item: ComicBook) -> str: - return item.date_added.strftime('%a, %e %b %Y %H:%M') + return item.date_added.strftime("%a, %e %b %Y %H:%M") # item_link is only needed if NewsItem has no get_absolute_url method. def item_link(self, item: ComicBook) -> str: - return '/comic/read/{0}/0/'.format(urlsafe_base64_encode(item.selector.bytes)) + return "/comic/read/{0}/0/".format(urlsafe_base64_encode(item.selector.bytes)) diff --git a/comic/forms.py b/comic/forms.py index 0f539e1..8a76e51 100644 --- a/comic/forms.py +++ b/comic/forms.py @@ -7,214 +7,137 @@ from comic.models import Setting class InitialSetupForm(forms.Form): - username = forms.CharField(help_text='Username', - widget=forms.TextInput( - attrs={ - 'class': 'form-control', - } - )) - email = forms.CharField(help_text='Email Address', - widget=forms.TextInput( - attrs={ - 'class': 'form-control' - } - )) - password = forms.CharField(help_text='New Password', - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - } - )) - password_confirm = forms.CharField(help_text='New Password Confirmation', - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - } - )) - base_dir = forms.CharField(help_text='Base Directory', - widget=forms.TextInput( - attrs={ - 'class': 'form-control' - } - )) + username = forms.CharField(help_text="Username", widget=forms.TextInput(attrs={"class": "form-control"})) + email = forms.CharField(help_text="Email Address", widget=forms.TextInput(attrs={"class": "form-control"})) + password = forms.CharField(help_text="New Password", widget=forms.PasswordInput(attrs={"class": "form-control"})) + password_confirm = forms.CharField( + help_text="New Password Confirmation", widget=forms.PasswordInput(attrs={"class": "form-control"}) + ) + base_dir = forms.CharField(help_text="Base Directory", widget=forms.TextInput(attrs={"class": "form-control"})) def clean_base_dir(self): - data = self.cleaned_data['base_dir'] + data = self.cleaned_data["base_dir"] if not path.isdir(data): - raise forms.ValidationError('This is not a valid Directory') + raise forms.ValidationError("This is not a valid Directory") return data def clean(self): form_data = self.cleaned_data - if form_data['password'] != form_data['password_confirm']: - raise forms.ValidationError('Passwords do not match.') - if len(form_data['password']) < 8: - raise forms.ValidationError('Password is too short') + if form_data["password"] != form_data["password_confirm"]: + raise forms.ValidationError("Passwords do not match.") + if len(form_data["password"]) < 8: + raise forms.ValidationError("Password is too short") return form_data class AccountForm(forms.Form): - username = forms.CharField(help_text='Username', - required=False, - widget=forms.TextInput( - attrs={ - 'class': 'form-control disabled', - 'readonly': True, - } - )) - email = forms.CharField(help_text='Email Address', - widget=forms.TextInput( - attrs={ - 'class': 'form-control' - } - )) - password = forms.CharField(help_text='New Password', - required=False, - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - } - )) - password_confirm = forms.CharField(help_text='New Password Confirmation', - required=False, - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - } - )) + username = forms.CharField( + help_text="Username", + required=False, + widget=forms.TextInput(attrs={"class": "form-control disabled", "readonly": True}), + ) + email = forms.CharField(help_text="Email Address", widget=forms.TextInput(attrs={"class": "form-control"})) + password = forms.CharField( + help_text="New Password", required=False, widget=forms.PasswordInput(attrs={"class": "form-control"}) + ) + password_confirm = forms.CharField( + help_text="New Password Confirmation", + required=False, + widget=forms.PasswordInput(attrs={"class": "form-control"}), + ) def clean_email(self): - data = self.cleaned_data['email'] - user = User.objects.get(username=self.cleaned_data['username']) + data = self.cleaned_data["email"] + user = User.objects.get(username=self.cleaned_data["username"]) if data == user.email: return data if User.objects.filter(email=data).exists(): - raise forms.ValidationError('Email Address is in use') + raise forms.ValidationError("Email Address is in use") return data def clean(self): form_data = self.cleaned_data - if form_data['password'] != form_data['password_confirm']: - raise forms.ValidationError('Passwords do not match.') - if len(form_data['password']) < 8 & len(form_data['password']) != 0: - raise forms.ValidationError('Password is too short') + if form_data["password"] != form_data["password_confirm"]: + raise forms.ValidationError("Passwords do not match.") + if len(form_data["password"]) < 8 & len(form_data["password"]) != 0: + raise forms.ValidationError("Password is too short") return form_data class AddUserForm(forms.Form): - username = forms.CharField(help_text='Username', - widget=forms.TextInput( - attrs={ - 'class': 'form-control', - } - )) - email = forms.CharField(help_text='Email Address', - widget=forms.TextInput( - attrs={ - 'class': 'form-control' - } - )) - password = forms.CharField(help_text='New Password', - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - } - )) - password_confirm = forms.CharField(help_text='New Password Confirmation', - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - } - )) + username = forms.CharField(help_text="Username", widget=forms.TextInput(attrs={"class": "form-control"})) + email = forms.CharField(help_text="Email Address", widget=forms.TextInput(attrs={"class": "form-control"})) + password = forms.CharField(help_text="New Password", widget=forms.PasswordInput(attrs={"class": "form-control"})) + password_confirm = forms.CharField( + help_text="New Password Confirmation", widget=forms.PasswordInput(attrs={"class": "form-control"}) + ) def clean_username(self): - data = self.cleaned_data['username'] + data = self.cleaned_data["username"] if User.objects.filter(username=data).exists(): - raise forms.ValidationError('This username Exists.') + raise forms.ValidationError("This username Exists.") return data def clean_email(self): - data = self.cleaned_data['email'] + data = self.cleaned_data["email"] if User.objects.filter(email=data).exists(): - raise forms.ValidationError('Email Address is in use') + raise forms.ValidationError("Email Address is in use") return data def clean(self): form_data = self.cleaned_data - if form_data['password'] != form_data['password_confirm']: - raise forms.ValidationError('Passwords do not match.') - if len(form_data['password']) < 8: - raise forms.ValidationError('Password is too short') + if form_data["password"] != form_data["password_confirm"]: + raise forms.ValidationError("Passwords do not match.") + if len(form_data["password"]) < 8: + raise forms.ValidationError("Password is too short") return form_data class EditUserForm(forms.Form): - username = forms.CharField(help_text='Username', - required=False, - widget=forms.TextInput( - attrs={ - 'class': 'form-control disabled', - 'readonly': True, - } - )) - email = forms.CharField(help_text='Email Address', - widget=forms.TextInput( - attrs={ - 'class': 'form-control' - } - )) - password = forms.CharField(help_text='New Password', - required=False, - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - } - )) + username = forms.CharField( + help_text="Username", + required=False, + widget=forms.TextInput(attrs={"class": "form-control disabled", "readonly": True}), + ) + email = forms.CharField(help_text="Email Address", widget=forms.TextInput(attrs={"class": "form-control"})) + password = forms.CharField( + help_text="New Password", required=False, widget=forms.PasswordInput(attrs={"class": "form-control"}) + ) # TODO: allow setting superuser on users @staticmethod def get_initial_values(user): - out = { - 'username': user.username, - 'email': user.email - } + out = {"username": user.username, "email": user.email} return out def clean_email(self): - data = self.cleaned_data['email'] - user = User.objects.get(username=self.cleaned_data['username']) + data = self.cleaned_data["email"] + user = User.objects.get(username=self.cleaned_data["username"]) if data == user.email: return data if User.objects.filter(email=data).exists(): - raise forms.ValidationError('Email Address is in use') + raise forms.ValidationError("Email Address is in use") return data def clean_password(self): - data = self.cleaned_data['password'] + data = self.cleaned_data["password"] if len(data) < 8 & len(data) != 0: - raise forms.ValidationError('Password is too short') + raise forms.ValidationError("Password is too short") return data class SettingsForm(forms.Form): - base_dir = forms.CharField(help_text='Base Directory', - widget=forms.TextInput( - attrs={ - 'class': 'form-control' - } - )) + base_dir = forms.CharField(help_text="Base Directory", widget=forms.TextInput(attrs={"class": "form-control"})) def clean_base_dir(self): - data = self.cleaned_data['base_dir'] + data = self.cleaned_data["base_dir"] if not path.isdir(data): - raise forms.ValidationError('This is not a valid Directory') + raise forms.ValidationError("This is not a valid Directory") return data @staticmethod def get_initial_values(): - base_dir, _ = Setting.objects.get_or_create(name='BASE_DIR') + base_dir, _ = Setting.objects.get_or_create(name="BASE_DIR") - initial = { - 'base_dir': base_dir.value, - } + initial = {"base_dir": base_dir.value} return initial diff --git a/comic/management/commands/scan_comics.py b/comic/management/commands/scan_comics.py index cc94d06..0eb737e 100644 --- a/comic/management/commands/scan_comics.py +++ b/comic/management/commands/scan_comics.py @@ -7,11 +7,11 @@ from comic.models import Setting, Directory, ComicBook class Command(BaseCommand): - help = 'Scan directories to Update Comic DB' + help = "Scan directories to Update Comic DB" def __init__(self): super().__init__() - self.base_dir = Setting.objects.get(name='BASE_DIR').value + self.base_dir = Setting.objects.get(name="BASE_DIR").value def handle(self, *args, **options): self.scan_directory() @@ -36,25 +36,21 @@ class Command(BaseCommand): for file in os.listdir(comic_dir): if isdir(os.path.join(comic_dir, file)): if directory: - next_directory, created = Directory.objects.get_or_create(name=file, - parent=directory) + next_directory, created = Directory.objects.get_or_create(name=file, parent=directory) else: - next_directory, created = Directory.objects.get_or_create(name=file, - parent__isnull=True) + next_directory, created = Directory.objects.get_or_create(name=file, parent__isnull=True) if created: next_directory.save() self.scan_directory(next_directory) else: try: if directory: - book = ComicBook.objects.get(file_name=file, - directory=directory) + book = ComicBook.objects.get(file_name=file, directory=directory) if book.version == 0: book.version = 1 book.save() else: - book = ComicBook.objects.get(file_name=file, - directory__isnull=True) + book = ComicBook.objects.get(file_name=file, directory__isnull=True) if book.version == 0: if directory: book.directory = directory diff --git a/comic/migrations/0001_initial.py b/comic/migrations/0001_initial.py index 5c7731c..6b4c8b5 100644 --- a/comic/migrations/0001_initial.py +++ b/comic/migrations/0001_initial.py @@ -6,16 +6,15 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Setting', + name="Setting", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=50)), - ('value', models.TextField()), + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ("name", models.CharField(max_length=50)), + ("value", models.TextField()), ], - ), + ) ] diff --git a/comic/migrations/0002_auto_20150616_1613.py b/comic/migrations/0002_auto_20150616_1613.py index 4602a93..bc24987 100644 --- a/comic/migrations/0002_auto_20150616_1613.py +++ b/comic/migrations/0002_auto_20150616_1613.py @@ -6,14 +6,8 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [ - ('comic', '0001_initial'), - ] + dependencies = [("comic", "0001_initial")] operations = [ - migrations.AlterField( - model_name='setting', - name='name', - field=models.CharField(unique=True, max_length=50), - ), + migrations.AlterField(model_name="setting", name="name", field=models.CharField(unique=True, max_length=50)) ] diff --git a/comic/migrations/0003_comicbook_comicpage.py b/comic/migrations/0003_comicbook_comicpage.py index b73f50f..3decbaa 100644 --- a/comic/migrations/0003_comicbook_comicpage.py +++ b/comic/migrations/0003_comicbook_comicpage.py @@ -6,27 +6,25 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [ - ('comic', '0002_auto_20150616_1613'), - ] + dependencies = [("comic", "0002_auto_20150616_1613")] operations = [ migrations.CreateModel( - name='ComicBook', + name="ComicBook", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('file_name', models.CharField(unique=True, max_length=100)), - ('last_read_page', models.IntegerField()), + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ("file_name", models.CharField(unique=True, max_length=100)), + ("last_read_page", models.IntegerField()), ], ), migrations.CreateModel( - name='ComicPage', + name="ComicPage", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('index', models.IntegerField()), - ('page_file_name', models.CharField(max_length=100)), - ('content_type', models.CharField(max_length=30)), - ('Comic', models.ForeignKey(to='comic.ComicBook', on_delete=models.CASCADE)), + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ("index", models.IntegerField()), + ("page_file_name", models.CharField(max_length=100)), + ("content_type", models.CharField(max_length=30)), + ("Comic", models.ForeignKey(to="comic.ComicBook", on_delete=models.CASCADE)), ], ), ] diff --git a/comic/migrations/0004_comicbook_unread.py b/comic/migrations/0004_comicbook_unread.py index 83b68ab..20bdb62 100644 --- a/comic/migrations/0004_comicbook_unread.py +++ b/comic/migrations/0004_comicbook_unread.py @@ -6,15 +6,10 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [ - ('comic', '0003_comicbook_comicpage'), - ] + dependencies = [("comic", "0003_comicbook_comicpage")] operations = [ migrations.AddField( - model_name='comicbook', - name='unread', - field=models.BooleanField(default=True), - preserve_default=False, - ), + model_name="comicbook", name="unread", field=models.BooleanField(default=True), preserve_default=False + ) ] diff --git a/comic/migrations/0005_auto_20150625_1400.py b/comic/migrations/0005_auto_20150625_1400.py index cde7c19..c5ee393 100644 --- a/comic/migrations/0005_auto_20150625_1400.py +++ b/comic/migrations/0005_auto_20150625_1400.py @@ -7,36 +7,27 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('comic', '0004_comicbook_unread'), - ] + dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL), ("comic", "0004_comicbook_unread")] operations = [ migrations.CreateModel( - name='ComicStatus', + name="ComicStatus", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('last_read_page', models.IntegerField()), - ('unread', models.BooleanField()), + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ("last_read_page", models.IntegerField()), + ("unread", models.BooleanField()), ], ), - migrations.RemoveField( - model_name='comicbook', - name='last_read_page', - ), - migrations.RemoveField( - model_name='comicbook', - name='unread', + migrations.RemoveField(model_name="comicbook", name="last_read_page"), + migrations.RemoveField(model_name="comicbook", name="unread"), + migrations.AddField( + model_name="comicstatus", + name="comic", + field=models.ForeignKey(to="comic.ComicBook", on_delete=models.CASCADE), ), migrations.AddField( - model_name='comicstatus', - name='comic', - field=models.ForeignKey(to='comic.ComicBook', on_delete=models.CASCADE), - ), - migrations.AddField( - model_name='comicstatus', - name='user', + model_name="comicstatus", + name="user", field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), ), ] diff --git a/comic/migrations/0006_auto_20150625_1411.py b/comic/migrations/0006_auto_20150625_1411.py index 58a9af6..6563179 100644 --- a/comic/migrations/0006_auto_20150625_1411.py +++ b/comic/migrations/0006_auto_20150625_1411.py @@ -6,19 +6,9 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [ - ('comic', '0005_auto_20150625_1400'), - ] + dependencies = [("comic", "0005_auto_20150625_1400")] operations = [ - migrations.AlterField( - model_name='comicstatus', - name='last_read_page', - field=models.IntegerField(default=0), - ), - migrations.AlterField( - model_name='comicstatus', - name='unread', - field=models.BooleanField(default=True), - ), + migrations.AlterField(model_name="comicstatus", name="last_read_page", field=models.IntegerField(default=0)), + migrations.AlterField(model_name="comicstatus", name="unread", field=models.BooleanField(default=True)), ] diff --git a/comic/migrations/0007_auto_20150626_1820.py b/comic/migrations/0007_auto_20150626_1820.py index 10a9d1f..daa26be 100644 --- a/comic/migrations/0007_auto_20150626_1820.py +++ b/comic/migrations/0007_auto_20150626_1820.py @@ -6,14 +6,8 @@ from django.db import models, migrations class Migration(migrations.Migration): - dependencies = [ - ('comic', '0006_auto_20150625_1411'), - ] + dependencies = [("comic", "0006_auto_20150625_1411")] operations = [ - migrations.AlterField( - model_name='setting', - name='name', - field=models.CharField(unique=True, max_length=100), - ), + migrations.AlterField(model_name="setting", name="name", field=models.CharField(unique=True, max_length=100)) ] diff --git a/comic/migrations/0008_auto_20160331_1140.py b/comic/migrations/0008_auto_20160331_1140.py index 9c76dfb..ea762d4 100644 --- a/comic/migrations/0008_auto_20160331_1140.py +++ b/comic/migrations/0008_auto_20160331_1140.py @@ -11,39 +11,40 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('comic', '0007_auto_20150626_1820'), - ] + dependencies = [("comic", "0007_auto_20150626_1820")] operations = [ migrations.CreateModel( - name='Directory', + name="Directory", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('selector', models.UUIDField(default=uuid.uuid4, null=True)), - ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comic.Directory')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("name", models.CharField(max_length=100)), + ("selector", models.UUIDField(default=uuid.uuid4, null=True)), + ( + "parent", + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="comic.Directory" + ), + ), ], ), migrations.AddField( - model_name='comicbook', - name='date_added', - field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2016, 3, 31, 10, 40, 30, 62170, tzinfo=utc)), + model_name="comicbook", + name="date_added", + field=models.DateTimeField( + auto_now_add=True, default=datetime.datetime(2016, 3, 31, 10, 40, 30, 62170, tzinfo=utc) + ), preserve_default=False, ), migrations.AddField( - model_name='comicbook', - name='selector', - field=models.UUIDField(default=uuid.uuid4, null=True), + model_name="comicbook", name="selector", field=models.UUIDField(default=uuid.uuid4, null=True) ), + migrations.AddField(model_name="comicbook", name="version", field=models.IntegerField(default=0)), migrations.AddField( - model_name='comicbook', - name='version', - field=models.IntegerField(default=0), - ), - migrations.AddField( - model_name='comicbook', - name='directory', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comic.Directory'), + model_name="comicbook", + name="directory", + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="comic.Directory" + ), ), ] diff --git a/comic/migrations/0009_auto_20160331_1140.py b/comic/migrations/0009_auto_20160331_1140.py index 7734683..198cbcf 100644 --- a/comic/migrations/0009_auto_20160331_1140.py +++ b/comic/migrations/0009_auto_20160331_1140.py @@ -8,11 +8,11 @@ import uuid def gen_uuid(apps, schema_editor): - comicbook = apps.get_model('comic', 'comicbook') + comicbook = apps.get_model("comic", "comicbook") for row in comicbook.objects.all(): row.selector = uuid.uuid4() row.save() - directory = apps.get_model('comic', 'directory') + directory = apps.get_model("comic", "directory") for row in directory.objects.all(): row.selector = uuid.uuid4() row.save() @@ -20,10 +20,6 @@ def gen_uuid(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('comic', '0008_auto_20160331_1140'), - ] + dependencies = [("comic", "0008_auto_20160331_1140")] - operations = [ - migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop), - ] + operations = [migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop)] diff --git a/comic/migrations/0010_auto_20160331_1140.py b/comic/migrations/0010_auto_20160331_1140.py index f31a3b0..9bb6366 100644 --- a/comic/migrations/0010_auto_20160331_1140.py +++ b/comic/migrations/0010_auto_20160331_1140.py @@ -9,19 +9,13 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('comic', '0009_auto_20160331_1140'), - ] + dependencies = [("comic", "0009_auto_20160331_1140")] operations = [ migrations.AlterField( - model_name='comicbook', - name='selector', - field=models.UUIDField(default=uuid.uuid4, unique=True), + model_name="comicbook", name="selector", field=models.UUIDField(default=uuid.uuid4, unique=True) ), migrations.AlterField( - model_name='directory', - name='selector', - field=models.UUIDField(default=uuid.uuid4, unique=True), + model_name="directory", name="selector", field=models.UUIDField(default=uuid.uuid4, unique=True) ), ] diff --git a/comic/migrations/0011_auto_20160331_1141.py b/comic/migrations/0011_auto_20160331_1141.py index 28a52df..d885ff7 100644 --- a/comic/migrations/0011_auto_20160331_1141.py +++ b/comic/migrations/0011_auto_20160331_1141.py @@ -7,14 +7,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('comic', '0010_auto_20160331_1140'), - ] + dependencies = [("comic", "0010_auto_20160331_1140")] - operations = [ - migrations.AlterField( - model_name='comicbook', - name='version', - field=models.IntegerField(default=1), - ), - ] + operations = [migrations.AlterField(model_name="comicbook", name="version", field=models.IntegerField(default=1))] diff --git a/comic/migrations/0012_auto_20160401_0949.py b/comic/migrations/0012_auto_20160401_0949.py index d3fe969..0b4c8e3 100644 --- a/comic/migrations/0012_auto_20160401_0949.py +++ b/comic/migrations/0012_auto_20160401_0949.py @@ -8,19 +8,17 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('comic', '0011_auto_20160331_1141'), - ] + dependencies = [("comic", "0011_auto_20160331_1141")] operations = [ migrations.AlterField( - model_name='comicbook', - name='selector', + model_name="comicbook", + name="selector", field=models.UUIDField(db_index=True, default=uuid.uuid4, unique=True), ), migrations.AlterField( - model_name='directory', - name='selector', + model_name="directory", + name="selector", field=models.UUIDField(db_index=True, default=uuid.uuid4, unique=True), ), ] diff --git a/comic/migrations/0013_comicstatus_finished.py b/comic/migrations/0013_comicstatus_finished.py index 06c4cd5..a19ad9b 100644 --- a/comic/migrations/0013_comicstatus_finished.py +++ b/comic/migrations/0013_comicstatus_finished.py @@ -7,14 +7,8 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('comic', '0012_auto_20160401_0949'), - ] + dependencies = [("comic", "0012_auto_20160401_0949")] operations = [ - migrations.AddField( - model_name='comicstatus', - name='finished', - field=models.BooleanField(default=False), - ), + migrations.AddField(model_name="comicstatus", name="finished", field=models.BooleanField(default=False)) ] diff --git a/comic/migrations/0014_auto_20160404_1402.py b/comic/migrations/0014_auto_20160404_1402.py index b458aa2..c65f1c8 100644 --- a/comic/migrations/0014_auto_20160404_1402.py +++ b/comic/migrations/0014_auto_20160404_1402.py @@ -5,22 +5,19 @@ from __future__ import unicode_literals from django.db import migrations from django.db.models import Max + def set_finished(apps, schema_editor): - comicstatus = apps.get_model('comic', 'comicstatus') - comicpage = apps.get_model('comic', 'ComicPage') + comicstatus = apps.get_model("comic", "comicstatus") + comicpage = apps.get_model("comic", "ComicPage") for row in comicstatus.objects.all(): - last_page = comicpage.objects.filter(Comic=row.comic).aggregate(Max('index')) - if row.last_read_page == last_page['index__max']: + last_page = comicpage.objects.filter(Comic=row.comic).aggregate(Max("index")) + if row.last_read_page == last_page["index__max"]: row.finished = True row.save() class Migration(migrations.Migration): - dependencies = [ - ('comic', '0013_comicstatus_finished'), - ] + dependencies = [("comic", "0013_comicstatus_finished")] - operations = [ - migrations.RunPython(set_finished, reverse_code=migrations.RunPython.noop), - ] + operations = [migrations.RunPython(set_finished, reverse_code=migrations.RunPython.noop)] diff --git a/comic/migrations/0015_auto_20160405_1126.py b/comic/migrations/0015_auto_20160405_1126.py index af78659..a24c560 100644 --- a/comic/migrations/0015_auto_20160405_1126.py +++ b/comic/migrations/0015_auto_20160405_1126.py @@ -7,14 +7,8 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('comic', '0014_auto_20160404_1402'), - ] + dependencies = [("comic", "0014_auto_20160404_1402")] operations = [ - migrations.AlterField( - model_name='comicbook', - name='file_name', - field=models.CharField(max_length=100), - ), + migrations.AlterField(model_name="comicbook", name="file_name", field=models.CharField(max_length=100)) ] diff --git a/comic/migrations/0016_auto_20160414_1335.py b/comic/migrations/0016_auto_20160414_1335.py index 90d11f4..7b561cd 100644 --- a/comic/migrations/0016_auto_20160414_1335.py +++ b/comic/migrations/0016_auto_20160414_1335.py @@ -6,14 +6,8 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('comic', '0015_auto_20160405_1126'), - ] + dependencies = [("comic", "0015_auto_20160405_1126")] operations = [ - migrations.AlterField( - model_name='comicpage', - name='page_file_name', - field=models.CharField(max_length=200), - ), + migrations.AlterField(model_name="comicpage", name="page_file_name", field=models.CharField(max_length=200)) ] diff --git a/comic/migrations/0017_usermisc.py b/comic/migrations/0017_usermisc.py index 606b76f..6d785c1 100644 --- a/comic/migrations/0017_usermisc.py +++ b/comic/migrations/0017_usermisc.py @@ -10,19 +10,18 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('comic', '0016_auto_20160414_1335'), - ] + dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL), ("comic", "0016_auto_20160414_1335")] operations = [ migrations.CreateModel( - name='UserMisc', + name="UserMisc", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('feed_id', models.UUIDField(db_index=True, default=uuid.uuid4, unique=True)), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("feed_id", models.UUIDField(db_index=True, default=uuid.uuid4, unique=True)), ( - 'user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + "user", + models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), ], - ), + ) ] diff --git a/comic/migrations/0018_auto_20170113_1531.py b/comic/migrations/0018_auto_20170113_1531.py index df2f4c8..9e927db 100644 --- a/comic/migrations/0018_auto_20170113_1531.py +++ b/comic/migrations/0018_auto_20170113_1531.py @@ -6,17 +6,13 @@ from django.db import migrations def gen_feeds(apps, schema_editor): - user_misc = apps.get_model('comic', 'UserMisc') - User = apps.get_model('auth', 'user') + user_misc = apps.get_model("comic", "UserMisc") + User = apps.get_model("auth", "user") for user in User.objects.all(): um = user_misc.objects.create(user=user) class Migration(migrations.Migration): - dependencies = [ - ('comic', '0017_usermisc'), - ] + dependencies = [("comic", "0017_usermisc")] - operations = [ - migrations.RunPython(gen_feeds, reverse_code=migrations.RunPython.noop), - ] + operations = [migrations.RunPython(gen_feeds, reverse_code=migrations.RunPython.noop)] diff --git a/comic/models.py b/comic/models.py index 635d118..d9e585d 100644 --- a/comic/models.py +++ b/comic/models.py @@ -27,11 +27,11 @@ class Setting(models.Model): class Directory(models.Model): name = models.CharField(max_length=100) - parent = models.ForeignKey('Directory', null=True, blank=True, on_delete=models.CASCADE) + parent = models.ForeignKey("Directory", null=True, blank=True, on_delete=models.CASCADE) selector = models.UUIDField(unique=True, default=uuid.uuid4, db_index=True) def __str__(self): - return 'Directory: {0}; {1}'.format(self.name, self.parent) + return "Directory: {0}; {1}".format(self.name, self.parent) @property def path(self): @@ -83,7 +83,7 @@ class ComicBook(models.Model): return urlsafe_base64_encode(self.selector.bytes) def get_image(self, page): - base_dir = Setting.objects.get(name='BASE_DIR').value + base_dir = Setting.objects.get(name="BASE_DIR").value if self.directory: archive_path = path.join(base_dir, self.directory.path, self.file_name) else: @@ -109,11 +109,11 @@ class ComicBook(models.Model): class Navigation: next_index = 0 - next_path = '' + next_path = "" prev_index = 0 - prev_path = '' + prev_path = "" cur_index = 0 - cur_path = '' + cur_path = "" q_prev_to_directory = False q_next_to_directory = False @@ -122,10 +122,7 @@ class ComicBook(models.Model): setattr(self, arg, kwargs[arg]) def nav(self, page, user): - out = self.Navigation( - cur_index=page, - cur_path=urlsafe_base64_encode(self.selector.bytes) - ) + 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: @@ -144,7 +141,7 @@ class ComicBook(models.Model): return out def nav_get_prev_comic(self, user): - base_dir = Setting.objects.get(name='BASE_DIR').value + base_dir = Setting.objects.get(name="BASE_DIR").value if self.directory: folder = path.join(base_dir, self.directory.path) else: @@ -155,7 +152,7 @@ class ComicBook(models.Model): if self.directory: comic_path = urlsafe_base64_encode(self.directory.selector.bytes) else: - comic_path = '' + comic_path = "" index = -1 else: prev_comic = dir_list[comic_index - 1] @@ -163,11 +160,9 @@ class ComicBook(models.Model): if not path.isdir(path.join(folder, prev_comic)): try: 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) else: - book = ComicBook.objects.get(file_name=prev_comic, - directory__isnull=True) + book = ComicBook.objects.get(file_name=prev_comic, directory__isnull=True) except ComicBook.DoesNotExist: if self.directory: book = ComicBook.process_comic_book(prev_comic, self.directory) @@ -180,12 +175,12 @@ class ComicBook(models.Model): if self.directory: comic_path = urlsafe_base64_encode(self.directory.selector.bytes) else: - comic_path = '' + comic_path = "" index = -1 return comic_path, index def nav_get_next_comic(self, user): - base_dir = Setting.objects.get(name='BASE_DIR').value + base_dir = Setting.objects.get(name="BASE_DIR").value if self.directory: folder = path.join(base_dir, self.directory.path) else: @@ -196,11 +191,9 @@ class ComicBook(models.Model): next_comic = dir_list[comic_index + 1] try: if self.directory: - book = ComicBook.objects.get(file_name=next_comic, - directory=self.directory) + book = ComicBook.objects.get(file_name=next_comic, directory=self.directory) else: - book = ComicBook.objects.get(file_name=next_comic, - directory__isnull=True) + book = ComicBook.objects.get(file_name=next_comic, directory__isnull=True) except ComicBook.DoesNotExist: if self.directory: book = ComicBook.process_comic_book(next_comic, self.directory) @@ -215,18 +208,18 @@ class ComicBook(models.Model): if self.directory: comic_path = urlsafe_base64_encode(self.directory.selector.bytes) else: - comic_path = '' + comic_path = "" index = -1 return comic_path, index class DirFile: def __init__(self): - self.name = '' + self.name = "" self.isdir = False - self.icon = '' + self.icon = "" self.iscb = False - self.location = '' - self.label = '' + self.location = "" + self.label = "" self.cur_page = 0 def __str__(self): @@ -234,7 +227,7 @@ class ComicBook(models.Model): @property def pages(self): - return [cp for cp in ComicPage.objects.filter(Comic=self).order_by('index')] + return [cp for cp in ComicPage.objects.filter(Comic=self).order_by("index")] def page_name(self, index): return ComicPage.objects.get(Comic=self, index=index).page_file_name @@ -247,15 +240,14 @@ class ComicBook(models.Model): :type directory: Directory """ try: - book = ComicBook.objects.get(file_name=comic_file_name, - version=0) + book = ComicBook.objects.get(file_name=comic_file_name, version=0) book.directory = directory book.version = 1 book.save() return book except ComicBook.DoesNotExist: pass - base_dir = Setting.objects.get(name='BASE_DIR').value + base_dir = Setting.objects.get(name="BASE_DIR").value if directory: comic_full_path = path.join(base_dir, directory.get_path(), comic_file_name) else: @@ -272,32 +264,30 @@ class ComicBook(models.Model): return comic_file_name with atomic(): if directory: - book = ComicBook(file_name=comic_file_name, - directory=directory) + book = ComicBook(file_name=comic_file_name, directory=directory) else: book = ComicBook(file_name=comic_file_name) book.save() page_index = 0 for page_file_name in sorted([str(x) for x in cbx.namelist()], key=str.lower): try: - dot_index = page_file_name.rindex('.') + 1 + dot_index = page_file_name.rindex(".") + 1 except ValueError: continue ext = page_file_name.lower()[dot_index:] - if ext in ['jpg', 'jpeg']: - content_type = 'image/jpeg' - elif ext == 'png': - content_type = 'image/png' - elif ext == 'bmp': - content_type = 'image/bmp' - elif ext == 'gif': - content_type = 'image/gif' + if ext in ["jpg", "jpeg"]: + content_type = "image/jpeg" + elif ext == "png": + content_type = "image/png" + elif ext == "bmp": + content_type = "image/bmp" + elif ext == "gif": + content_type = "image/gif" else: - content_type = 'text/plain' - page = ComicPage(Comic=book, - index=page_index, - page_file_name=page_file_name, - content_type=content_type) + content_type = "text/plain" + page = ComicPage( + Comic=book, index=page_index, page_file_name=page_file_name, content_type=content_type + ) page.save() page_index += 1 return book @@ -336,8 +326,12 @@ class ComicStatus(models.Model): return self.__repr__() def __repr__(self): - return f'> 3*4, data, pos, h.date_time) - h.ctime, pos = self._parse_xtime(flags >> 2*4, data, pos) - h.atime, pos = self._parse_xtime(flags >> 1*4, data, pos) - h.arctime, pos = self._parse_xtime(flags >> 0*4, data, pos) + h.mtime, pos = self._parse_xtime(flags >> 3 * 4, data, pos, h.date_time) + h.ctime, pos = self._parse_xtime(flags >> 2 * 4, data, pos) + h.atime, pos = self._parse_xtime(flags >> 1 * 4, data, pos) + h.arctime, pos = self._parse_xtime(flags >> 0 * 4, data, pos) return pos - def _parse_xtime(self, flag, data, pos, dostime = None): - unit = 10000000.0 # 100 ns units + def _parse_xtime(self, flag, data, pos, dostime=None): + unit = 10000000.0 # 100 ns units if flag & 8: if not dostime: t = S_LONG.unpack_from(data, pos)[0] @@ -1100,47 +1154,47 @@ class RarFile(object): def _next_newvol(self, volfile): i = len(volfile) - 1 while i >= 0: - if volfile[i] >= '0' and volfile[i] <= '9': + if volfile[i] >= "0" and volfile[i] <= "9": return self._inc_volname(volfile, i) i -= 1 - raise BadRarName("Cannot construct volume name: "+volfile) + raise BadRarName("Cannot construct volume name: " + volfile) # old-style next volume def _next_oldvol(self, volfile): # rar -> r00 - if volfile[-4:].lower() == '.rar': - return volfile[:-2] + '00' + if volfile[-4:].lower() == ".rar": + return volfile[:-2] + "00" return self._inc_volname(volfile, len(volfile) - 1) # increase digits with carry, otherwise just increment char def _inc_volname(self, volfile, i): fn = list(volfile) while i >= 0: - if fn[i] != '9': + if fn[i] != "9": fn[i] = chr(ord(fn[i]) + 1) break - fn[i] = '0' + fn[i] = "0" i -= 1 - return ''.join(fn) + return "".join(fn) def _open_clear(self, inf): return DirectReader(self, inf) # put file compressed data into temporary .rar archive, and run # unrar on that, thus avoiding unrar going over whole archive - def _open_hack(self, inf, psw = None): - BSIZE = 32*1024 + def _open_hack(self, inf, psw=None): + BSIZE = 32 * 1024 size = inf.compress_size + inf.header_size rf = XFile(inf.volume_file, 0) rf.seek(inf.header_offset) - tmpfd, tmpname = mkstemp(suffix='.rar') + tmpfd, tmpname = mkstemp(suffix=".rar") tmpf = os.fdopen(tmpfd, "wb") try: # create main header: crc, type, flags, size, res1, res2 - mh = S_BLK_HDR.pack(0x90CF, 0x73, 0, 13) + ZERO * (2+4) + mh = S_BLK_HDR.pack(0x90CF, 0x73, 0, 13) + ZERO * (2 + 4) tmpf.write(RAR_ID + mh) while size > 0: if size > BSIZE: @@ -1148,7 +1202,7 @@ class RarFile(object): else: buf = rf.read(size) if not buf: - raise BadRarFile('read failed: ' + inf.filename) + raise BadRarFile("read failed: " + inf.filename) tmpf.write(buf) size -= len(buf) tmpf.close() @@ -1170,21 +1224,22 @@ class RarFile(object): rf.close() # decompress - cmt = rar_decompress(inf.extract_version, inf.compress_type, data, - inf.file_size, inf.flags, inf.CRC, psw, inf.salt) + cmt = rar_decompress( + inf.extract_version, inf.compress_type, data, inf.file_size, inf.flags, inf.CRC, psw, inf.salt + ) # check crc if self._crc_check: crc = crc32(cmt) if crc < 0: - crc += (long(1) << 32) + crc += long(1) << 32 if crc != inf.CRC: return None return self._decode_comment(cmt) # extract using unrar - def _open_unrar(self, rarfile, inf, psw = None, tmpfile = None): + def _open_unrar(self, rarfile, inf, psw=None, tmpfile=None): if is_filelike(rarfile): raise ValueError("Cannot use unrar directly on memory buffer") cmd = [UNRAR_TOOL] + list(OPEN_ARGS) @@ -1208,7 +1263,7 @@ class RarFile(object): return val.decode(c) except UnicodeError: pass - return val.decode(self._charset, 'replace') + return val.decode(self._charset, "replace") def _decode_comment(self, val): if UNICODE_COMMENTS: @@ -1241,10 +1296,12 @@ class RarFile(object): output = p.communicate()[0] check_returncode(p, output) + ## ## Utility classes ## + class UnicodeFilename: """Handle unicode filename decompression""" @@ -1269,7 +1326,7 @@ class UnicodeFilename: return self.std_name[self.pos] except IndexError: self.failed = 1 - return ord('?') + return ord("?") def put(self, lo, hi): self.buf.append(lo) @@ -1295,7 +1352,7 @@ class UnicodeFilename: n = self.enc_byte() if n & 0x80: c = self.enc_byte() - for i in range((n & 0x7f) + 2): + for i in range((n & 0x7F) + 2): lo = (self.std_byte() + c) & 0xFF self.put(lo, hi) else: @@ -1326,7 +1383,7 @@ class RarExtFile(RawIOBase): # standard io.* properties self.name = inf.filename - self.mode = 'rb' + self.mode = "rb" self.rf = rf self.inf = inf @@ -1345,7 +1402,7 @@ class RarExtFile(RawIOBase): self.CRC = 0 self.remain = self.inf.file_size - def read(self, cnt = None): + def read(self, cnt=None): """Read all or specified amount of data from archive entry.""" # sanitize cnt @@ -1366,7 +1423,7 @@ class RarExtFile(RawIOBase): # done? if not data or self.remain == 0: - #self.close() + # self.close() self._check() return data @@ -1375,12 +1432,12 @@ class RarExtFile(RawIOBase): if not self.crc_check: return if self.returncode: - check_returncode(self, '') + check_returncode(self, "") if self.remain != 0: raise BadRarFile("Failed the read enough data") crc = self.CRC if crc < 0: - crc += (long(1) << 32) + crc += long(1) << 32 if crc != self.inf.CRC: raise BadRarFile("Corrupt file - CRC check failed: " + self.inf.filename) @@ -1412,6 +1469,7 @@ class RarExtFile(RawIOBase): buf[:n] = data except TypeError: import array + if not isinstance(buf, array.array): raise buf[:n] = array.array(buf.typecode, data) @@ -1421,7 +1479,7 @@ class RarExtFile(RawIOBase): """Return current reading position in uncompressed data.""" return self.inf.file_size - self.remain - def seek(self, ofs, whence = 0): + def seek(self, ofs, whence=0): """Seek in data. On uncompressed files, the seeking works by actual @@ -1436,14 +1494,14 @@ class RarExtFile(RawIOBase): fsize = self.inf.file_size cur_ofs = self.tell() - if whence == 0: # seek from beginning of file + if whence == 0: # seek from beginning of file new_ofs = ofs - elif whence == 1: # seek from current position + elif whence == 1: # seek from current position new_ofs = cur_ofs + ofs - elif whence == 2: # seek from end of file + elif whence == 2: # seek from end of file new_ofs = fsize + ofs else: - raise ValueError('Invalid value for whence') + raise ValueError("Invalid value for whence") # sanity check if new_ofs < 0: @@ -1456,7 +1514,7 @@ class RarExtFile(RawIOBase): self._skip(new_ofs - cur_ofs) else: # process old data ? - #self._skip(fsize - cur_ofs) + # self._skip(fsize - cur_ofs) # reopen and seek self._open() self._skip(new_ofs) @@ -1566,6 +1624,7 @@ class PipeReader(RarExtFile): self.tempfile = None if have_memoryview: + def readinto(self, buf): """Zero-copy read directly into buffer.""" cnt = len(buf) @@ -1574,7 +1633,7 @@ class PipeReader(RarExtFile): vbuf = memoryview(buf) res = got = 0 while got < cnt: - res = self.fd.readinto(vbuf[got : cnt]) + res = self.fd.readinto(vbuf[got:cnt]) if not res: break if self.crc_check: @@ -1676,6 +1735,7 @@ class DirectReader(RarExtFile): return True if have_memoryview: + def readinto(self, buf): """Zero-copy read directly into buffer.""" got = 0 @@ -1705,6 +1765,7 @@ class DirectReader(RarExtFile): class HeaderDecrypt: """File-like object that decrypts from another file""" + def __init__(self, f, key, iv): self.f = f self.ciph = AES.new(key, AES.MODE_CBC, iv) @@ -1714,8 +1775,8 @@ class HeaderDecrypt: return self.f.tell() def read(self, cnt=None): - if cnt > 8*1024: - raise BadRarFile('Bad count to header decrypt - wrong password?') + if cnt > 8 * 1024: + raise BadRarFile("Bad count to header decrypt - wrong password?") # consume old data if cnt <= len(self.buf): @@ -1743,56 +1804,68 @@ class HeaderDecrypt: return res + # handle (filename|filelike) object class XFile(object): - __slots__ = ('_fd', '_need_close') - def __init__(self, xfile, bufsize = 1024): + __slots__ = ("_fd", "_need_close") + + def __init__(self, xfile, bufsize=1024): if is_filelike(xfile): self._need_close = False self._fd = xfile self._fd.seek(0) else: self._need_close = True - self._fd = open(xfile, 'rb', bufsize) + self._fd = open(xfile, "rb", bufsize) + def read(self, n=None): return self._fd.read(n) + def tell(self): return self._fd.tell() + def seek(self, ofs, whence=0): return self._fd.seek(ofs, whence) + def readinto(self, dst): return self._fd.readinto(dst) + def close(self): if self._need_close: self._fd.close() + def __enter__(self): return self + def __exit__(self, typ, val, tb): self.close() + ## ## Utility functions ## + def is_filelike(obj): if isinstance(obj, str) or isinstance(obj, unicode): return False res = True - for a in ('read', 'tell', 'seek'): + for a in ("read", "tell", "seek"): res = res and hasattr(obj, a) if not res: raise ValueError("Invalid object passed as file") return True + def rar3_s2k(psw, salt): """String-to-key hash for RAR3.""" - seed = psw.encode('utf-16le') + salt + seed = psw.encode("utf-16le") + salt iv = EMPTY h = sha1() for i in range(16): for j in range(0x4000): - cnt = S_LONG.pack(i*0x4000 + j) + cnt = S_LONG.pack(i * 0x4000 + j) h.update(seed + cnt[:3]) if j == 0: iv += h.digest()[19:20] @@ -1800,6 +1873,7 @@ def rar3_s2k(psw, salt): key_le = pack("LLLL", key_be)) return key_le, iv + def rar_decompress(vers, meth, data, declen=0, flags=0, crc=0, psw=None, salt=None): """Decompress blob of compressed data. @@ -1815,11 +1889,10 @@ def rar_decompress(vers, meth, data, declen=0, flags=0, crc=0, psw=None, salt=No flags |= RAR_LONG_BLOCK # file header - fname = bytes('data', 'ascii') + fname = bytes("data", "ascii") date = 0 mode = 0x20 - fhdr = S_FILE_HDR.pack(len(data), declen, RAR_OS_MSDOS, crc, - date, vers, meth, len(fname), mode) + fhdr = S_FILE_HDR.pack(len(data), declen, RAR_OS_MSDOS, crc, date, vers, meth, len(fname), mode) fhdr += fname if flags & RAR_FILE_SALT: if not salt: @@ -1833,10 +1906,10 @@ def rar_decompress(vers, meth, data, declen=0, flags=0, crc=0, psw=None, salt=No hdr = S_BLK_HDR.pack(hcrc, RAR_BLOCK_FILE, flags, hlen) + fhdr # archive main header - mh = S_BLK_HDR.pack(0x90CF, RAR_BLOCK_MAIN, 0, 13) + ZERO * (2+4) + mh = S_BLK_HDR.pack(0x90CF, RAR_BLOCK_MAIN, 0, 13) + ZERO * (2 + 4) # decompress via temp rar - tmpfd, tmpname = mkstemp(suffix='.rar') + tmpfd, tmpname = mkstemp(suffix=".rar") tmpf = os.fdopen(tmpfd, "wb") try: tmpf.write(RAR_ID + mh + hdr + data) @@ -1852,6 +1925,7 @@ def rar_decompress(vers, meth, data, declen=0, flags=0, crc=0, psw=None, salt=No tmpf.close() os.unlink(tmpname) + def to_datetime(t): """Convert 6-part time tuple into datetime object.""" @@ -1871,13 +1945,20 @@ def to_datetime(t): # sanitize invalid values MDAY = (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) - if mon < 1: mon = 1 - if mon > 12: mon = 12 - if day < 1: day = 1 - if day > MDAY[mon]: day = MDAY[mon] - if h > 23: h = 23 - if m > 59: m = 59 - if s > 59: s = 59 + if mon < 1: + mon = 1 + if mon > 12: + mon = 12 + if day < 1: + day = 1 + if day > MDAY[mon]: + day = MDAY[mon] + if h > 23: + h = 23 + if m > 59: + m = 59 + if s > 59: + s = 59 if mon == 2 and day == 29: try: return datetime(year, mon, day, h, m, s, us) @@ -1885,30 +1966,35 @@ def to_datetime(t): day = 28 return datetime(year, mon, day, h, m, s, us) + def parse_dos_time(stamp): """Parse standard 32-bit DOS timestamp.""" - sec = stamp & 0x1F; stamp = stamp >> 5 - min = stamp & 0x3F; stamp = stamp >> 6 - hr = stamp & 0x1F; stamp = stamp >> 5 - day = stamp & 0x1F; stamp = stamp >> 5 - mon = stamp & 0x0F; stamp = stamp >> 4 + sec = stamp & 0x1F + stamp = stamp >> 5 + min = stamp & 0x3F + stamp = stamp >> 6 + hr = stamp & 0x1F + stamp = stamp >> 5 + day = stamp & 0x1F + stamp = stamp >> 5 + mon = stamp & 0x0F + stamp = stamp >> 4 yr = (stamp & 0x7F) + 1980 return (yr, mon, day, hr, min, sec * 2) + def custom_popen(cmd): """Disconnect cmd from parent fds, read only from stdout.""" # needed for py2exe creationflags = 0 - if sys.platform == 'win32': - creationflags = 0x08000000 # CREATE_NO_WINDOW + if sys.platform == "win32": + creationflags = 0x08000000 # CREATE_NO_WINDOW # run command try: - p = Popen(cmd, bufsize = 0, - stdout = PIPE, stdin = PIPE, stderr = STDOUT, - creationflags = creationflags) + p = Popen(cmd, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, creationflags=creationflags) except OSError: ex = sys.exc_info()[1] if ex.errno == errno.ENOENT: @@ -1916,6 +2002,7 @@ def custom_popen(cmd): raise return p + def custom_check(cmd, ignore_retcode=False): """Run command, collect output, raise error if needed.""" p = custom_popen(cmd) @@ -1924,14 +2011,16 @@ def custom_check(cmd, ignore_retcode=False): raise RarExecError("Check-run failed") return out + def add_password_arg(cmd, psw, required=False): """Append password switch to commandline.""" if UNRAR_TOOL == ALT_TOOL: return if psw is not None: - cmd.append('-p' + psw) + cmd.append("-p" + psw) else: - cmd.append('-p-') + cmd.append("-p-") + def check_returncode(p, out): """Raise exception according to unrar exit code""" @@ -1941,10 +2030,19 @@ def check_returncode(p, out): return # map return code to exception class - errmap = [None, - RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError, - RarWriteError, RarOpenError, RarUserError, RarMemoryError, - RarCreateError, RarNoFilesError] # codes from rar.txt + errmap = [ + None, + RarWarning, + RarFatalError, + RarCRCError, + RarLockedArchiveError, + RarWriteError, + RarOpenError, + RarUserError, + RarMemoryError, + RarCreateError, + RarNoFilesError, + ] # codes from rar.txt if UNRAR_TOOL == ALT_TOOL: errmap = [None] if code > 0 and code < len(errmap): @@ -1964,6 +2062,7 @@ def check_returncode(p, out): raise exc(msg) + # # Check if unrar works # @@ -1983,4 +2082,3 @@ except RarCannotExec: except RarCannotExec: # no usable tool, only uncompressed archives work pass - diff --git a/comic/tests/test_models.py b/comic/tests/test_models.py index 8725899..7231049 100644 --- a/comic/tests/test_models.py +++ b/comic/tests/test_models.py @@ -12,9 +12,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") - ) + Setting.objects.create(name="BASE_DIR", value=path.join(os.getcwd(), "comic", "test")) User.objects.create_user("test", "test@test.com", "test") user = User.objects.first() ComicBook.process_comic_book("test1.rar") @@ -133,51 +131,34 @@ class ComicBookTests(TestCase): 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) - self.assertEqual( - dir1.label, - '
Empty
', - ) + self.assertEqual(dir1.label, '
Empty
') dir2 = folders[1] self.assertEqual(dir2.name, "test1.rar") self.assertEqual(dir2.type, "book") self.assertEqual(dir2.icon, "glyphicon-book") 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}/{1}/".format(urlsafe_base64_encode(c.selector.bytes), "0") self.assertEqual(dir2.location, location) - self.assertEqual( - dir2.label, - '
Unread
', - ) + self.assertEqual(dir2.label, '
Unread
') dir3 = folders[2] self.assertEqual(dir3.name, "test2.rar") self.assertEqual(dir3.type, "book") self.assertEqual(dir3.icon, "glyphicon-book") 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}/{1}/".format(urlsafe_base64_encode(c.selector.bytes), "2") self.assertEqual(dir3.location, location) - self.assertEqual( - dir3.label, '
3/4
' - ) + self.assertEqual(dir3.label, '
3/4
') dir4 = folders[3] self.assertEqual(dir4.name, "test3.rar") self.assertEqual(dir4.type, "book") self.assertEqual(dir3.icon, "glyphicon-book") 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}/{1}/".format(urlsafe_base64_encode(c.selector.bytes), "0") self.assertEqual(dir4.location, location) - self.assertEqual( - dir4.label, - '
Unread
', - ) + self.assertEqual(dir4.label, '
Unread
') def test_pages(self): book = ComicBook.objects.get(file_name="test1.rar") @@ -217,9 +198,7 @@ class ComicBookTests(TestCase): response = c.post("/comic/list_json/") self.assertEqual(response.status_code, 200) directory = Directory.objects.first() - response = c.post( - f"/comic/list_json/{urlsafe_base64_encode(directory.selector.bytes)}/" - ) + response = c.post(f"/comic/list_json/{urlsafe_base64_encode(directory.selector.bytes)}/") self.assertEqual(response.status_code, 200) def test_recent_comics(self): @@ -241,12 +220,7 @@ class ComicBookTests(TestCase): generate_directory(User.objects.first()) ComicStatus.objects.all().delete() - 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) self.assertEqual(response.status_code, 200) req_data["search[value]"] = "test1.rar" @@ -261,13 +235,11 @@ class ComicBookTests(TestCase): { "date": book.date_added.strftime("%d/%m/%y-%H:%M"), "icon": '', - "label": '
Unread
', + "label": '
Unread
', "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)}/0/", } ], "recordsFiltered": 1, @@ -302,11 +274,7 @@ class ComicBookTests(TestCase): response = c.get("/comic/edit/") self.assertEqual(response.status_code, 405) - req_data = { - "comic_list_length": 10, - "func": "unread", - "selected": book.selector_string, - } + req_data = {"comic_list_length": 10, "func": "unread", "selected": book.selector_string} response = c.post("/comic/edit/", req_data) self.assertEqual(response.status_code, 200) diff --git a/comic/urls.py b/comic/urls.py index 889e1e8..6d15471 100644 --- a/comic/urls.py +++ b/comic/urls.py @@ -2,20 +2,21 @@ from django.conf.urls import url from . import feeds from . import views + urlpatterns = [ - url(r'^$', views.comic_list, name='index'), - url(r'^settings/$', views.settings_page, name='settings'), - url(r'^settings/users/$', views.users_page, name='users'), - url(r'^settings/users/(?P[0-9]+)/$', views.user_config_page, name='users'), - url(r'^settings/users/add/$', views.user_add_page, name='users'), - url(r'^account/$', views.account_page, name='account'), - url(r'^read/(?P[\w-]+)/(?P[0-9]+)/$', views.read_comic, name='read_comic'), - url(r'^read/(?P[\w-]+)/(?P[0-9]+)/img$', views.get_image, name='get_image'), - url(r'^list_json/$', views.comic_list_json, name='comic_list_json1'), - url(r'^list_json/(?P[\w-]+)/$', views.comic_list_json, name='comic_list_json2'), - url(r'^recent/$', views.recent_comics, name='recent_comics'), - url(r'^recent/json/$', views.recent_comics_json, name='recent_comics_json'), - url(r'^edit/$', views.comic_edit, name='comic_edit'), - url(r'^feed/(?P[\w-]+)/$', feeds.RecentComics()), - url(r'^(?P[\w-]+)/$', views.comic_list, name='comic_list'), + url(r"^$", views.comic_list, name="index"), + url(r"^settings/$", views.settings_page, name="settings"), + url(r"^settings/users/$", views.users_page, name="users"), + url(r"^settings/users/(?P[0-9]+)/$", views.user_config_page, name="users"), + url(r"^settings/users/add/$", views.user_add_page, name="users"), + url(r"^account/$", views.account_page, name="account"), + url(r"^read/(?P[\w-]+)/(?P[0-9]+)/$", views.read_comic, name="read_comic"), + url(r"^read/(?P[\w-]+)/(?P[0-9]+)/img$", views.get_image, name="get_image"), + url(r"^list_json/$", views.comic_list_json, name="comic_list_json1"), + url(r"^list_json/(?P[\w-]+)/$", views.comic_list_json, name="comic_list_json2"), + url(r"^recent/$", views.recent_comics, name="recent_comics"), + url(r"^recent/json/$", views.recent_comics_json, name="recent_comics_json"), + url(r"^edit/$", views.comic_edit, name="comic_edit"), + url(r"^feed/(?P[\w-]+)/$", feeds.RecentComics()), + url(r"^(?P[\w-]+)/$", views.comic_list, name="comic_list"), ] diff --git a/comic/util.py b/comic/util.py index 72a2d60..7baeb22 100644 --- a/comic/util.py +++ b/comic/util.py @@ -8,32 +8,32 @@ from .models import ComicBook, Setting, ComicStatus, Directory def generate_title_from_path(file_path): - if file_path == '': - return 'CBWebReader' - return 'CBWebReader - ' + ' - '.join(file_path.split(path.sep)) + if file_path == "": + return "CBWebReader" + return "CBWebReader - " + " - ".join(file_path.split(path.sep)) class Menu: - def __init__(self, user, page=''): + def __init__(self, user, page=""): """ :type page: str """ self.menu_items = OrderedDict() - self.menu_items['Browse'] = '/comic/' - self.menu_items['Recent'] = '/comic/recent/' - self.menu_items['Account'] = '/comic/account/' + self.menu_items["Browse"] = "/comic/" + self.menu_items["Recent"] = "/comic/recent/" + self.menu_items["Account"] = "/comic/account/" if user.is_superuser: - self.menu_items['Settings'] = '/comic/settings/' - self.menu_items['Users'] = '/comic/settings/users/' - self.menu_items['Logout'] = '/logout/' + self.menu_items["Settings"] = "/comic/settings/" + self.menu_items["Users"] = "/comic/settings/users/" + self.menu_items["Logout"] = "/logout/" self.current_page = page class Breadcrumb: def __init__(self): - self.name = 'Home' - self.url = '/comic/' + self.name = "Home" + self.url = "/comic/" def __str__(self): return self.name @@ -56,12 +56,12 @@ def generate_breadcrumbs_from_path(directory=False, book=False): for item in folders[::-1]: bc = Breadcrumb() bc.name = item.name - bc.url = '/comic/' + urlsafe_base64_encode(item.selector.bytes) + bc.url = "/comic/" + urlsafe_base64_encode(item.selector.bytes) output.append(bc) if book: bc = Breadcrumb() bc.name = book.file_name - bc.url = '/read/' + urlsafe_base64_encode(book.selector.bytes) + bc.url = "/read/" + urlsafe_base64_encode(book.selector.bytes) output.append(bc) return output @@ -79,46 +79,45 @@ def generate_breadcrumbs_from_menu(paths): class DirFile: def __init__(self): - self.name = '' - self.icon = '' - self.location = '' - self.label = '' - self.type = '' - self.selector = '' + self.name = "" + self.icon = "" + self.location = "" + self.label = "" + self.type = "" + self.selector = "" def __str__(self): return self.name def populate_directory(self, directory, user): self.name = directory.name - self.icon = 'glyphicon-folder-open' + self.icon = "glyphicon-folder-open" self.selector = urlsafe_base64_encode(directory.selector.bytes) - self.location = '/comic/{0}/'.format(self.selector) + self.location = "/comic/{0}/".format(self.selector) self.label = generate_dir_status(user, directory) - self.type = 'directory' + self.type = "directory" def populate_comic(self, comic, user): if type(comic) == str: - self.icon = 'glyphicon-remove' + self.icon = "glyphicon-remove" self.name = comic - self.selector = '0' - self.location = '/' + self.selector = "0" + self.location = "/" self.label = '
Error
' - self.type = 'book' + self.type = "book" else: - self.icon = 'glyphicon-book' + self.icon = "glyphicon-book" self.name = comic.file_name status, created = ComicStatus.objects.get_or_create(comic=comic, user=user) if created: status.save() self.selector = urlsafe_base64_encode(comic.selector.bytes) - self.location = '/comic/read/{0}/{1}/'.format(self.selector, - status.last_read_page) + self.location = "/comic/read/{0}/{1}/".format(self.selector, status.last_read_page) self.label = generate_label(comic, status) - self.type = 'book' + self.type = "book" def __repr__(self): - return f'' + return f"" def generate_directory(user, directory=False): @@ -126,7 +125,7 @@ def generate_directory(user, directory=False): :type user: User :type directory: Directory """ - base_dir = Setting.objects.get(name='BASE_DIR').value + base_dir = Setting.objects.get(name="BASE_DIR").value files = [] if directory: ordered_dir_list = listdir(path.join(base_dir, directory.path)) @@ -136,15 +135,11 @@ def generate_directory(user, directory=False): 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] 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=dir_list, parent=directory) + file_list_obj = ComicBook.objects.filter(file_name__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=dir_list, parent__isnull=True) + file_list_obj = ComicBook.objects.filter(file_name__in=file_list, directory__isnull=True) for directory_obj in dir_list_obj: df = DirFile() df.populate_directory(directory_obj, user) @@ -159,8 +154,7 @@ def generate_directory(user, directory=False): file_list.remove(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, parent=directory) else: directory_obj = Directory(name=directory_name) directory_obj.save() @@ -168,7 +162,7 @@ def generate_directory(user, directory=False): df.populate_directory(directory_obj, user) files.append(df) for file_name in file_list: - if file_name.lower()[-4:] in ['.rar', '.zip', '.cbr', '.cbz']: + if file_name.lower()[-4:] in [".rar", ".zip", ".cbr", ".cbz"]: book = ComicBook.process_comic_book(file_name, directory) df = DirFile() df.populate_comic(book, user) @@ -184,17 +178,17 @@ def generate_label(book, status): elif (status.last_read_page + 1) == book.page_count: label_text = '
Read
' else: - label_text = '
%s/%s
' % \ - (status.last_read_page + 1, book.page_count) + label_text = '
%s/%s
' % ( + status.last_read_page + 1, + book.page_count, + ) return label_text def generate_dir_status(user, directory): cb_list = ComicBook.objects.filter(directory=directory) total = cb_list.count() - total_read = ComicStatus.objects.filter(user=user, - comic__in=cb_list, - finished=True).count() + total_read = ComicStatus.objects.filter(user=user, comic__in=cb_list, finished=True).count() if total == 0: return '
Empty
' elif total == total_read: diff --git a/comic/views.py b/comic/views.py index 480d3fc..a2a4b47 100644 --- a/comic/views.py +++ b/comic/views.py @@ -18,8 +18,14 @@ from django.views.decorators.http import require_POST from .forms import SettingsForm, AccountForm, EditUserForm, AddUserForm, InitialSetupForm from .models import Setting, ComicBook, ComicStatus, Directory, ComicPage, UserMisc -from .util import generate_breadcrumbs_from_path, generate_breadcrumbs_from_menu, \ - generate_title_from_path, Menu, generate_directory, generate_label +from .util import ( + generate_breadcrumbs_from_path, + generate_breadcrumbs_from_menu, + generate_title_from_path, + Menu, + generate_directory, + generate_label, +) # noinspection PyTypeChecker @@ -27,11 +33,11 @@ from .util import generate_breadcrumbs_from_path, generate_breadcrumbs_from_menu @login_required def comic_list(request, directory_selector=False): try: - base_dir = Setting.objects.get(name='BASE_DIR').value + base_dir = Setting.objects.get(name="BASE_DIR").value except Setting.DoesNotExist: - return redirect('/comic/settings/') + return redirect("/comic/settings/") if not path.isdir(base_dir): - return redirect('/comic/settings/') + return redirect("/comic/settings/") if directory_selector: selector = uuid.UUID(bytes=urlsafe_base64_decode(directory_selector)) @@ -42,18 +48,17 @@ def comic_list(request, directory_selector=False): if directory: title = generate_title_from_path(directory.path) breadcrumbs = generate_breadcrumbs_from_path(directory) - json_url = '/comic/list_json/{0}/'.format(directory_selector) + json_url = "/comic/list_json/{0}/".format(directory_selector) else: - title = generate_title_from_path('Home') + title = generate_title_from_path("Home") breadcrumbs = generate_breadcrumbs_from_path() - json_url = '/comic/list_json/' + json_url = "/comic/list_json/" - return render(request, 'comic/comic_list.html', { - 'breadcrumbs': breadcrumbs, - 'menu': Menu(request.user, 'Browse'), - 'title': title, - 'json_url': json_url - }) + return render( + request, + "comic/comic_list.html", + {"breadcrumbs": breadcrumbs, "menu": Menu(request.user, "Browse"), "title": title, "json_url": json_url}, + ) @login_required @@ -67,100 +72,99 @@ def comic_list_json(request, directory_selector=False): directory = False files = generate_directory(request.user, directory) response_data = dict() - response_data['data'] = [] + response_data["data"] = [] for file in files: - response_data['data'].append({ - 'blank': '', - 'selector': file.selector, - 'type': file.type, - 'icon': icon_str.format(file.icon), - 'name': file.name, - 'label': file.label, - 'url': file.location, - }) - return HttpResponse( - json.dumps(response_data), - content_type="application/json" - ) + response_data["data"].append( + { + "blank": "", + "selector": file.selector, + "type": file.type, + "icon": icon_str.format(file.icon), + "name": file.name, + "label": file.label, + "url": file.location, + } + ) + return HttpResponse(json.dumps(response_data), content_type="application/json") @login_required def recent_comics(request): feed_id, _ = UserMisc.objects.get_or_create(user=request.user) - return render(request, - 'comic/recent_comics.html', - { - 'breadcrumbs': generate_breadcrumbs_from_menu([('Recent', '/comic/recent/')]), - 'menu': Menu(request.user, 'Recent'), - 'title': 'Recent Comics', - 'feed_id': urlsafe_base64_encode(feed_id.feed_id.bytes), - }) - - -@login_required -@require_POST -def recent_comics_json(request): - start = int(request.POST['start']) - end = start + int(request.POST['length']) - icon = '' - comics = ComicBook.objects.all() - response_data = dict() - response_data['recordsTotal'] = comics.count() - if request.POST['search[value]']: - comics = comics.filter(file_name__contains=request.POST['search[value]']) - order_string = '' - # Ordering - if request.POST['order[0][dir]'] == 'desc': - order_string += '-' - if request.POST['order[0][dir]'] == '3': - order_string += 'date_added' - elif request.POST['order[0][dir]'] == '2': - order_string += 'date_added' - else: - order_string += 'date_added' - comics = comics.order_by(order_string) - response_data['recordsFiltered'] = comics.count() - response_data['data'] = list() - for book in comics[start:end]: - status, created = ComicStatus.objects.get_or_create(comic=book, - user=request.user) - if created: - status.save() - response_data['data'].append({ - 'selector': urlsafe_base64_encode(book.selector.bytes), - 'icon': icon, - 'type': 'book', - 'name': book.file_name, - 'date': book.date_added.strftime('%d/%m/%y-%H:%M'), - 'label': generate_label(book, status), - 'url': '/comic/read/{0}/{1}/'.format(urlsafe_base64_encode(book.selector.bytes), - status.last_read_page) - }) - return HttpResponse( - json.dumps(response_data), - content_type="application/json" + return render( + request, + "comic/recent_comics.html", + { + "breadcrumbs": generate_breadcrumbs_from_menu([("Recent", "/comic/recent/")]), + "menu": Menu(request.user, "Recent"), + "title": "Recent Comics", + "feed_id": urlsafe_base64_encode(feed_id.feed_id.bytes), + }, ) +@login_required +@require_POST +def recent_comics_json(request): + start = int(request.POST["start"]) + end = start + int(request.POST["length"]) + icon = '' + comics = ComicBook.objects.all() + response_data = dict() + response_data["recordsTotal"] = comics.count() + if request.POST["search[value]"]: + comics = comics.filter(file_name__contains=request.POST["search[value]"]) + order_string = "" + # Ordering + if request.POST["order[0][dir]"] == "desc": + order_string += "-" + if request.POST["order[0][dir]"] == "3": + order_string += "date_added" + elif request.POST["order[0][dir]"] == "2": + order_string += "date_added" + else: + order_string += "date_added" + comics = comics.order_by(order_string) + response_data["recordsFiltered"] = comics.count() + response_data["data"] = list() + for book in comics[start:end]: + status, created = ComicStatus.objects.get_or_create(comic=book, user=request.user) + if created: + status.save() + response_data["data"].append( + { + "selector": urlsafe_base64_encode(book.selector.bytes), + "icon": icon, + "type": "book", + "name": book.file_name, + "date": book.date_added.strftime("%d/%m/%y-%H:%M"), + "label": generate_label(book, status), + "url": "/comic/read/{0}/{1}/".format( + urlsafe_base64_encode(book.selector.bytes), status.last_read_page + ), + } + ) + return HttpResponse(json.dumps(response_data), content_type="application/json") + + @login_required @require_POST def comic_edit(request): - if 'selected' not in request.POST: + if "selected" not in request.POST: return HttpResponse(status=200) - if request.POST['func'] == 'choose': + if request.POST["func"] == "choose": return HttpResponse(status=200) - selected = [uuid.UUID(bytes=urlsafe_base64_decode(item)) for item in request.POST.getlist('selected')] + selected = [uuid.UUID(bytes=urlsafe_base64_decode(item)) for item in request.POST.getlist("selected")] comics = ComicBook.objects.filter(selector__in=selected) with atomic(): for comic in comics: - status, _ = ComicStatus.objects.get_or_create(comic=comic, - user=request.user) - if request.POST['func'] == 'read': + status, _ = ComicStatus.objects.get_or_create(comic=comic, user=request.user) + if request.POST["func"] == "read": status.unread = False status.finished = True status.last_read_page = comic.page_count - 1 - elif request.POST['func'] == 'unread': + elif request.POST["func"] == "unread": status.unread = True status.finished = False status.last_read_page = 0 @@ -174,44 +178,37 @@ def account_page(request): if request.POST: form = AccountForm(request.POST) if form.is_valid(): - if form.cleaned_data['email'] != request.user.email: - request.user.email = form.cleaned_data['email'] - success_message.append('Email Updated.') - if len(form.cleaned_data['password']) != 0: - request.user.set_password(form.cleaned_data['password']) - success_message.append('Password Updated.') + if form.cleaned_data["email"] != request.user.email: + request.user.email = form.cleaned_data["email"] + success_message.append("Email Updated.") + if len(form.cleaned_data["password"]) != 0: + request.user.set_password(form.cleaned_data["password"]) + success_message.append("Password Updated.") request.user.save() else: - form = AccountForm(initial={ - 'username': request.user.username, - 'email': request.user.email, - }) - crumbs = [ - ('Account', '/comic/account/'), - ] + form = AccountForm(initial={"username": request.user.username, "email": request.user.email}) + crumbs = [("Account", "/comic/account/")] context = { - 'form': form, - 'menu': Menu(request.user, 'Account'), - 'error_message': form.errors, - 'success_message': '
'.join(success_message), - 'breadcrumbs': generate_breadcrumbs_from_menu(crumbs), - 'title': 'CBWebReader - Account', + "form": form, + "menu": Menu(request.user, "Account"), + "error_message": form.errors, + "success_message": "
".join(success_message), + "breadcrumbs": generate_breadcrumbs_from_menu(crumbs), + "title": "CBWebReader - Account", } - return render(request, 'comic/settings_page.html', context) + return render(request, "comic/settings_page.html", context) @user_passes_test(lambda u: u.is_superuser) def users_page(request): users = User.objects.all() - crumbs = [ - ('Users', '/comic/settings/users/'), - ] + crumbs = [("Users", "/comic/settings/users/")] context = { - 'users': users, - 'menu': Menu(request.user, 'Users'), - 'breadcrumbs': generate_breadcrumbs_from_menu(crumbs), + "users": users, + "menu": Menu(request.user, "Users"), + "breadcrumbs": generate_breadcrumbs_from_menu(crumbs), } - return render(request, 'comic/users_page.html', context) + return render(request, "comic/users_page.html", context) @user_passes_test(lambda u: u.is_superuser) @@ -221,89 +218,78 @@ def user_config_page(request, user_id): if request.POST: form = EditUserForm(request.POST) if form.is_valid(): - if 'password' in form.cleaned_data: - if len(form.cleaned_data['password']) != 0: - user.set_password(form.cleaned_data['password']) - success_message.append('Password Updated.') - if form.cleaned_data['email'] != user.email: - user.email = form.cleaned_data['email'] - success_message.append('Email Updated.
') + if "password" in form.cleaned_data: + if len(form.cleaned_data["password"]) != 0: + user.set_password(form.cleaned_data["password"]) + success_message.append("Password Updated.") + if form.cleaned_data["email"] != user.email: + user.email = form.cleaned_data["email"] + success_message.append("Email Updated.
") user.save() else: form = EditUserForm(initial=EditUserForm.get_initial_values(user)) users = User.objects.all() - crumbs = [ - ('Users', '/comic/settings/users/'), - (user.username, '/comic/settings/users/' + str(user.id)), - ] + crumbs = [("Users", "/comic/settings/users/"), (user.username, "/comic/settings/users/" + str(user.id))] context = { - 'form': form, - 'users': users, - 'menu': Menu(request.user, 'Users'), - 'error_message': form.errors, - 'breadcrumbs': generate_breadcrumbs_from_menu(crumbs), - 'success_message': '
'.join(success_message), - 'title': 'CBWebReader - Edit User - ' + user.username, + "form": form, + "users": users, + "menu": Menu(request.user, "Users"), + "error_message": form.errors, + "breadcrumbs": generate_breadcrumbs_from_menu(crumbs), + "success_message": "
".join(success_message), + "title": "CBWebReader - Edit User - " + user.username, } - return render(request, 'comic/settings_page.html', context) + return render(request, "comic/settings_page.html", context) @user_passes_test(lambda u: u.is_superuser) def user_add_page(request): - success_message = '' + success_message = "" if request.POST: form = AddUserForm(request.POST) if form.is_valid(): - user = User( - username=form.cleaned_data['username'], - email=form.cleaned_data['email'], - ) - user.set_password(form.cleaned_data['password']) + user = User(username=form.cleaned_data["username"], email=form.cleaned_data["email"]) + user.set_password(form.cleaned_data["password"]) user.save() UserMisc.objects.create(user=user) - success_message = 'User {} created.'.format(user.username) + success_message = "User {} created.".format(user.username) else: form = AddUserForm() - crumbs = [ - ('Users', '/comic/settings/users/'), - ('Add', '/comic/settings/users/add/'), - ] + crumbs = [("Users", "/comic/settings/users/"), ("Add", "/comic/settings/users/add/")] context = { - 'form': form, - 'menu': Menu(request.user, 'Users'), - 'breadcrumbs': generate_breadcrumbs_from_menu(crumbs), - 'error_message': form.errors, - 'success_message': success_message, - 'title': 'CBWebReader - Add User', + "form": form, + "menu": Menu(request.user, "Users"), + "breadcrumbs": generate_breadcrumbs_from_menu(crumbs), + "error_message": form.errors, + "success_message": success_message, + "title": "CBWebReader - Add User", } - return render(request, 'comic/settings_page.html', context) + return render(request, "comic/settings_page.html", context) @user_passes_test(lambda u: u.is_superuser) def settings_page(request): success_message = [] - crumbs = [ - ('Settings', '/comic/settings/'), - ] + crumbs = [("Settings", "/comic/settings/")] if request.POST: form = SettingsForm(request.POST) if form.is_valid(): - base_dir = Setting.objects.get(name='BASE_DIR') - base_dir.value = form.cleaned_data['base_dir'] + base_dir = Setting.objects.get(name="BASE_DIR") + base_dir.value = form.cleaned_data["base_dir"] base_dir.save() - success_message.append('Settings updated.') + success_message.append("Settings updated.") form = SettingsForm(initial=SettingsForm.get_initial_values()) context = { - 'error_message': form.errors, - 'success_message': '
'.join(success_message), - 'form': form, - 'menu': Menu(request.user, 'Settings'), - 'title': 'CBWebReader - Settings', - 'breadcrumbs': generate_breadcrumbs_from_menu(crumbs), + "error_message": form.errors, + "success_message": "
".join(success_message), + "form": form, + "menu": Menu(request.user, "Settings"), + "title": "CBWebReader - Settings", + "breadcrumbs": generate_breadcrumbs_from_menu(crumbs), } - return render(request, 'comic/settings_page.html', context) + return render(request, "comic/settings_page.html", context) @login_required @@ -317,21 +303,21 @@ def read_comic(request, comic_selector, page): status, _ = ComicStatus.objects.get_or_create(comic=book, user=request.user) status.unread = False status.last_read_page = page - if ComicPage.objects.filter(Comic=book).aggregate(Max('index'))['index__max'] == status.last_read_page: + if ComicPage.objects.filter(Comic=book).aggregate(Max("index"))["index__max"] == status.last_read_page: status.finished = True else: status.finished = False status.save() - title = 'CBWebReader - ' + book.file_name + ' - Page: ' + str(page) + title = "CBWebReader - " + book.file_name + " - Page: " + str(page) context = { - 'book': book, - 'orig_file_name': book.page_name(page), - 'nav': book.nav(page, request.user), - 'breadcrumbs': breadcrumbs, - 'menu': Menu(request.user), - 'title': title, + "book": book, + "orig_file_name": book.page_name(page), + "nav": book.nav(page, request.user), + "breadcrumbs": breadcrumbs, + "menu": Menu(request.user), + "title": title, } - return render(request, 'comic/read_comic.html', context) + return render(request, "comic/read_comic.html", context) @login_required @@ -344,34 +330,29 @@ def get_image(_, comic_selector, page): def initial_setup(request): if User.objects.all().exists(): - return redirect('/comic/') + return redirect("/comic/") if request.POST: form = InitialSetupForm(request.POST) if form.is_valid(): user = User( - username=form.cleaned_data['username'], - email=form.cleaned_data['email'], + username=form.cleaned_data["username"], + email=form.cleaned_data["email"], is_staff=True, is_superuser=True, ) - user.set_password(form.cleaned_data['password']) + 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, _ = 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) - return redirect('/comic/') + return redirect("/comic/") else: form = InitialSetupForm() - context = { - 'form': form, - 'title': 'CBWebReader - Setup', - 'error_message': form.errors, - } - return render(request, 'comic/settings_page.html', context) + context = {"form": form, "title": "CBWebReader - Setup", "error_message": form.errors} + return render(request, "comic/settings_page.html", context) def comic_redirect(_): - return redirect('/comic/') + return redirect("/comic/") diff --git a/comic_auth/forms.py b/comic_auth/forms.py index 0466730..2593bda 100644 --- a/comic_auth/forms.py +++ b/comic_auth/forms.py @@ -6,26 +6,19 @@ from snowpenguin.django.recaptcha2.widgets import ReCaptchaWidget class LoginForm(forms.Form): - username = forms.CharField(max_length=50, - label='', - widget=forms.TextInput( - attrs={ - 'class': 'form-control', - 'placeholder': 'Username', - 'autofocus': True, - 'required': True, - } - )) - password = forms.CharField(label='Password', - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - 'placeholder': 'Username', - 'required': True, - } - )) + username = forms.CharField( + max_length=50, + label="", + widget=forms.TextInput( + attrs={"class": "form-control", "placeholder": "Username", "autofocus": True, "required": True} + ), + ) + password = forms.CharField( + label="Password", + widget=forms.PasswordInput(attrs={"class": "form-control", "placeholder": "Username", "required": True}), + ) def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) - if settings.CBREADER_USE_RECAPTCHA if hasattr(settings, 'CBREADER_USE_RECAPTCHA') else False: - self.fields['captcha'] = ReCaptchaField(widget=ReCaptchaWidget()) + if settings.CBREADER_USE_RECAPTCHA if hasattr(settings, "CBREADER_USE_RECAPTCHA") else False: + self.fields["captcha"] = ReCaptchaField(widget=ReCaptchaWidget()) diff --git a/comic_auth/views.py b/comic_auth/views.py index bc8b435..1967fdb 100644 --- a/comic_auth/views.py +++ b/comic_auth/views.py @@ -9,45 +9,28 @@ def comic_login(request): if request.POST: form = LoginForm(request.POST) if form.is_valid(): - user = authenticate(username=form.cleaned_data['username'], - password=form.cleaned_data['password']) + user = authenticate(username=form.cleaned_data["username"], password=form.cleaned_data["password"]) if user is not None: if user.is_active: login(request, user) - if 'next' in request.GET: - return redirect(request.GET['next']) + if "next" in request.GET: + return redirect(request.GET["next"]) else: - return redirect('/comic/') + return redirect("/comic/") else: - return render(request, - 'comic_auth/login.html', - { - 'error': True, - }) + return render(request, "comic_auth/login.html", {"error": True}) else: - return render(request, - 'comic_auth/login.html', - { - 'error': True, - 'form': form - }) + return render(request, "comic_auth/login.html", {"error": True, "form": form}) else: - return render(request, - 'comic_auth/login.html', - { - 'error': True, - 'form': form - }) + return render(request, "comic_auth/login.html", {"error": True, "form": form}) else: if not User.objects.all().exists(): - return redirect('/setup/') + return redirect("/setup/") form = LoginForm() - context = { - 'form': form - } - return render(request, 'comic_auth/login.html', context) + context = {"form": form} + return render(request, "comic_auth/login.html", context) def comic_logout(request): logout(request) - return redirect('/login/') + return redirect("/login/") diff --git a/docker-compose.yml b/docker-compose.yml index 52c290d..41e0540 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,11 +14,11 @@ services: - database ports: - "8000:8000" - volumes: - - ./cbreader:/src/cbreader - - ./comic:/src/comic - - ./comic_auth:/src/comic_auth - - ${COMIC_BOOK_VOLUME}:/data + # volumes: + # - ./cbreader:/src/cbreader + # - ./comic:/src/comic + # - ./comic_auth:/src/comic_auth + # - ${COMIC_BOOK_VOLUME}:/data command: python manage.py runserver 0.0.0.0:8000 database: diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0bcf827 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.black] +line_length = 119 diff --git a/setup.py b/setup.py index 721b9b5..d470b51 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,13 @@ from distutils.core import setup setup( - name='cbwebreader', - version='', - packages=['comic', 'comic.migrations', 'cbreader', 'comic_auth', 'comic_auth.migrations'], - url='https://github.com/ajurna/cbwebreader', - license='http://creativecommons.org/licenses/by-sa/4.0/', - author='Ajurna', - author_email='ajurna@gmail.com', - description='Comic Book Web Reader', requires=['django-recaptcha', 'django', 'ujson'] + name="cbwebreader", + version="", + packages=["comic", "comic.migrations", "cbreader", "comic_auth", "comic_auth.migrations"], + url="https://github.com/ajurna/cbwebreader", + license="http://creativecommons.org/licenses/by-sa/4.0/", + author="Ajurna", + author_email="ajurna@gmail.com", + description="Comic Book Web Reader", + requires=["django-recaptcha", "django", "ujson"], )