Compare commits

..

No commits in common. "49ca4bccdca028c103e55ab81e70cc2c23eb64c7" and "f9478f28944dead55a50fb959aca6c0ff946dbfb" have entirely different histories.

14 changed files with 25 additions and 318 deletions

View File

@ -66,7 +66,7 @@ STATIC_ROOT = BASE_DIR + "/static/"
logfile = usersettings["LOGFOLDER"] + "django.log" logfile = usersettings["LOGFILE"]
LOGGING = { LOGGING = {
"version": 1, "version": 1,
"disable_existing_loggers": False, "disable_existing_loggers": False,
@ -75,7 +75,7 @@ LOGGING = {
"file": { "file": {
"level": "INFO", "level": "INFO",
"class": "logging.FileHandler", "class": "logging.FileHandler",
"filename": logfile, "filename": usersettings["LOGFILE"],
"formatter": "app", "formatter": "app",
}, },
}, },
@ -97,7 +97,6 @@ LOGGING = {
}, },
} }
## ## ## ##
######################################################################## ########################################################################
## DERUG ## ## DERUG ##
@ -189,15 +188,10 @@ WSGI_APPLICATION = 'CalibreWebCompanion.wsgi.application'
## DATBASE ## ## DATBASE ##
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases # https://docs.djangoproject.com/en/3.0/ref/settings/#databases
if usersettings["ISDOCKER"]:
defaultdb_path = "/usr/src/app/data/"
else:
defaultdb_path = BASE_DIR
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(defaultdb_path, 'db.sqlite3'), 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}, },
'calibre': { 'calibre': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',

View File

@ -1,6 +1,4 @@
import multiprocessing import multiprocessing
import os
import json
bind = "127.0.0.1:8000" bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1 workers = multiprocessing.cpu_count() * 2 + 1
@ -9,20 +7,10 @@ keepalive = 5
# daemon = True # Detaches the server from the controlling terminal and enters the background. disabled for now # daemon = True # Detaches the server from the controlling terminal and enters the background. disabled for now
# logging # logging
with open("settings.json", "r") as jfile: errorlog = "/home/massiveatoms/Desktop/logs/gunicorn_error.log"
settings = json.load(jfile)
errorlog = settings["LOGFOLDER"] + "/gunicorn_error.log"
loglevel = "warning" loglevel = "warning"
accesslog = settings["LOGFOLDER"] + "/gunicorn_access.log" accesslog = "/home/massiveatoms/Desktop/logs/gunicorn_access.log"
# capture_output = True
if not os.path.isdir("/usr/src/app/data/logs"):
os.mkdir("/usr/src/app/data/logs")
if not os.path.isfile(errorlog):
os.system('touch {}'.format(errorlog))
if not os.path.isfile(accesslog):
os.system('touch {}'.format(accesslog))
capture_output = True
# debug settings which need to be commented out in prod # debug settings which need to be commented out in prod
# reload=True # reload=True

View File

@ -15,7 +15,7 @@ def filters(request):
unique_authors = Author.objects.only('name', "id").annotate(num_books=Count('book')).order_by('name') unique_authors = Author.objects.only('name', "id").annotate(num_books=Count('book')).order_by('name')
unique_tags = Tag.objects.annotate(num_books=Count('book')).order_by('name') unique_tags = Tag.objects.annotate(num_books=Count('book')).order_by('name')
unique_publishers = Publisher.objects.annotate(num_books=Count('book')).order_by('name') unique_publishers = Publisher.objects.annotate(num_books=Count('book')).order_by('name')
unique_languages = Language.objects.annotate(num_books=Count('book')).order_by('lang_code') unique_languages = Language.objects.annotate(num_books=Count('book')).order_by('rating')
unique_ratings = Rating.objects.annotate(num_books=Count('book')) unique_ratings = Rating.objects.annotate(num_books=Count('book'))
unique_series = Series.objects.annotate(num_books=Count('book')).order_by('sort') unique_series = Series.objects.annotate(num_books=Count('book')).order_by('sort')
@ -27,4 +27,4 @@ def filters(request):
"unique_languages": unique_languages, "unique_languages": unique_languages,
"unique_ratings": unique_ratings, "unique_ratings": unique_ratings,
"unique_series": unique_series "unique_series": unique_series
} }

View File

@ -1,166 +1,3 @@
from django.test import Client, TestCase from django.test import TestCase
from pprint import pprint
from django.test.utils import setup_test_environment
from .models import Book, Author, Publisher, Series, Rating, Tag, Identifier
from django.db.models import Count
# Create your tests here.
client = Client()
client.login(username="testuser", password="dumbeasypassword")
def booklisttest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
res = c.get("/books/")
assert res.status_code == 200
context = dict(res.context)
assert sorted(context["book_list"], key=lambda x: x.id)== sorted(Book.objects.all(), key=lambda x: x.id)
def authorlisttest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
res = c.get("/authors/")
assert res.status_code == 200
context = dict(res.context)
assert sorted(context["author_list"], key=lambda x: x.id)== sorted(Author.objects.all(), key=lambda x: x.id)
def publisherlisttest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
res = c.get("/publishers/")
assert res.status_code == 200
context = dict(res.context)
assert sorted(context["publisher_list"], key=lambda x: x.id)== sorted(Publisher.objects.all(), key=lambda x: x.id)
def serieslisttest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
res = c.get("/series/")
assert res.status_code == 200
context = dict(res.context)
assert sorted(context["series_list"], key=lambda x: x.id)== sorted(Series.objects.all(), key=lambda x: x.id)
def ratinglisttest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
res = c.get("/ratings/")
assert res.status_code == 200
context = dict(res.context)
assert sorted(context["rating_list"], key=lambda x: x.id)== sorted(Rating.objects.all(), key=lambda x: x.id)
def taglisttest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
res = c.get("/tags/")
assert res.status_code == 200
context = dict(res.context)
assert sorted(context["tag_list"], key=lambda x: x.id)== sorted(Tag.objects.all(), key=lambda x: x.id)
def bookdetailtest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
ids = [i.id for i in Book.objects.all()][:10]
for i in ids:
res = c.get(f"/book/{i}")
assert res.status_code == 200
context = dict(res.context)
assert context["book"] == Book.objects.get(id=i)
def authordetailtest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
ids = [i.id for i in Author.objects.all()][:10]
for i in ids:
res = c.get(f"/author/{i}")
assert res.status_code == 200
context = dict(res.context)
assert context["author"] == Author.objects.get(id=i)
def publisherdetailtest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
ids = [i.id for i in Publisher.objects.all()][:10]
for i in ids:
res = c.get(f"/publisher/{i}")
assert res.status_code == 200
context = dict(res.context)
assert context["publisher"] == Publisher.objects.get(id=i)
def seriesdetailtest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
ids = [i.id for i in Series.objects.all()][:10]
for i in ids:
res = c.get(f"/series/{i}")
assert res.status_code == 200
context = dict(res.context)
assert context["series"] == Series.objects.get(id=i)
def ratingdetailtest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
ids = [i.id for i in Rating.objects.all()][:10]
for i in ids:
res = c.get(f"/rating/{i}")
assert res.status_code == 200
context = dict(res.context)
assert context["rating"] == Rating.objects.get(id=i)
def tagdetailtest():
c = Client()
c.login(username="testuser", password="dumbeasypassword")
ids = [i.id for i in Tag.objects.all()][:10]
for i in ids:
res = c.get(f"/tag/{i}")
assert res.status_code == 200
context = dict(res.context)
assert context["tag"] == Tag.objects.get(id=i)
def search_partial(key, value, book=None):
c = Client()
c.login(username="testuser", password="dumbeasypassword")
res = c.get("/results/", {key : value})
if not book:
return dict(res.context)["book_list"]
return book in dict(res.context)["book_list"]
def searchtest():
books = [i for i in Book.objects.all()][:10]
for i in books:
assert search_partial("title", i.title, i)
assert search_partial("generic", i.title, i)
assert search_partial("author", i.author_sort, i)
author = i.authors.first()
if author:
assert search_partial("author", author.name, i)
assert search_partial("generic", author.name, i)
assert search_partial("generic", i.author_sort, i)
id = Identifier.objects.filter(book=i.id).first()
if id:
assert search_partial("identifier", id, i)
assert search_partial("generic", id, i)
booklisttest()
bookdetailtest()
authorlisttest()
authordetailtest()
publisherdetailtest()
publisherlisttest()
seriesdetailtest()
serieslisttest()
ratingdetailtest()
ratinglisttest()
tagdetailtest()
taglisttest()
searchtest()

View File

@ -1,4 +1,3 @@
from django.utils.decorators import method_decorator
from django.shortcuts import render from django.shortcuts import render
from django.views import generic from django.views import generic
from .models import Author, Book, Comment, Rating, BookAuthorLink, Publisher, Tag, BookTagLink, BookRatingLink, Data, Identifier, Series from .models import Author, Book, Comment, Rating, BookAuthorLink, Publisher, Tag, BookTagLink, BookRatingLink, Data, Identifier, Series
@ -14,6 +13,7 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# might be helpful for vary headers later # might be helpful for vary headers later
from django.utils.decorators import method_decorator
@login_required @login_required
@ -58,29 +58,13 @@ class ResultsView(generic.ListView): # no clue if this is secure.
if title: if title:
books = books.filter(sort__icontains=title) books = books.filter(sort__icontains=title)
if author: if author:
# authors are stored as author_sort and author, needs to be slightly more complex books = books.filter(author_sort__icontains=author)
author_obj = Author.objects.filter(name__icontains=author).first()
if not author_obj:
author_id = -1
else:
author_id = author_obj.id
books = books.filter(
Q(author_sort__icontains=author) |
Q(authors__id=author_id)
)
if identifier: if identifier:
books = books.filter(identifier__val=identifier) books = books.filter(identifier__val=identifier)
if generic: if generic:
author_obj = Author.objects.filter(name__icontains=generic).first()
if not author_obj:
author_id = -1
else:
author_id = author_obj.id
books = books.filter( books = books.filter(
Q(sort__icontains=generic) | Q(sort__icontains=generic) |
Q(author_sort__icontains=generic) | Q(author_sort__icontains=generic) |
Q(authors__id=author_id) |
Q(identifier__val=generic) Q(identifier__val=generic)
) )
return books return books

0
CalibreWebCompanion/manage.py Executable file → Normal file
View File

View File

View File

@ -1,29 +0,0 @@
## pull official base image
FROM python:3.8.3-alpine
EXPOSE 8080
## set work directory
WORKDIR /usr/src/app
## install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
RUN apk add nginx supervisor
# do nginx stuff
RUN adduser -D -g 'www' www
RUN mkdir -p /run/nginx
COPY ./deployment/nginx.conf /etc/nginx/
## copy project
COPY ./CalibreWebCompanion ./CalibreWebCompanion
copy ./deployment/startupscript.py ./
## gunicorn borks started with supervisord
COPY ./deployment/supervisord.conf /etc/
ENTRYPOINT /usr/bin/supervisord -c /etc/supervisord.conf
# docker run --publish 8000:80 \
# -v '/home/massiveatoms/Desktop/logs:/usr/src/app/data' \
# -v '/run/media/massiveatoms/1AEEEA6EEEEA421D1/Documents and Settings/MassiveAtoms/Documents/Calibre Library/:/usr/src/app/calibredir' \
# --name cw calibreweb:1.0.1

View File

@ -1,4 +0,0 @@
from os import environ

View File

@ -1,4 +1,4 @@
# non docker stuff
1. clone repo 1. clone repo
2. pip install -r requirements.txt 2. pip install -r requirements.txt
3. install gunicorn and nginx 3. install gunicorn and nginx
@ -18,19 +18,4 @@ Suggestions:
1. We might want to use sockets instead of ip/port? 1. We might want to use sockets instead of ip/port?
2. logging 2. logging
3. autostart gunicorn/nginx 3. autostart gunicorn/nginx
4. some extra instrumentation for gunicorn https://docs.gunicorn.org/en/latest/deploy.html 4. some extra instrumentation for gunicorn https://docs.gunicorn.org/en/latest/deploy.html
# docker stuff
here are the commands i use to build and run:
1. build --tag calibreweb:1.0.1 . -f ./deployment/Dockerfile
2.
```
docker run --publish 8000:80\
-v '/home/massiveatoms/Desktop/logs:/usr/src/app/data' \
-v '/run/media/massiveatoms/1AEEEA6EEEEA421D/Documents and Settings/MassiveAtoms/Documents/Calibre Library/:/usr/src/app/calibredir' \
--name cw calibreweb:1.0.1
```
Docker stuff to fix:
1. permission/ownership issue with volumes and nginx
2. we need a setup script that changes secretkey, and generates the default db

View File

@ -1,9 +1,9 @@
worker_processes 1; worker_processes 1;
# user nobody nogroup; # user nobody nogroup;
user www www; # TEMP disabled user massiveatoms;
# user nobody nobody; # for systems with 'nobody' as a group instead # user nobody nobody; # for systems with 'nobody' as a group instead
error_log /usr/src/app/data/logs/nginx.log warn; error_log /home/massiveatoms/Desktop/logs/nginx.log warn;
# pid /var/run/nginx.pid; # pid /var/run/nginx.pid;
events { events {
@ -44,18 +44,18 @@ http {
client_max_body_size 4G; client_max_body_size 4G;
# set the correct host(s) for your site # set the correct host(s) for your site
server_name localhost 0.0.0.0; # set this to the server url? or ip? we'll see MASSIVEATOMS server_name localhost 192.168.1.4; # set this to the server url? or ip? we'll see MASSIVEATOMS
keepalive_timeout 5; keepalive_timeout 5;
# # MASSIVEATOMS # MASSIVEATOMS
location /download/ { location /download/ {
alias "/usr/src/app/calibredir/"; alias "/run/media/massiveatoms/1AEEEA6EEEEA421D/Documents and Settings/MassiveAtoms/Documents/Calibre Library/";
# Never forget the fact that this little statement being root instead of alias caused us to lose more than a day troubleshooting # Never forget the fact that this little statement being root instead of alias caused us to lose more than a day troubleshooting
} }
location /static/ { location /static/ {
alias "/usr/src/app/CalibreWebCompanion/static/"; alias "/home/massiveatoms/Desktop/calibre-web-companion/CalibreWebCompanion/static/";
# Never forget the fact that this little statement being root instead of alias caused us to lose more than a day troubleshooting # Never forget the fact that this little statement being root instead of alias caused us to lose more than a day troubleshooting
} }

View File

@ -1,11 +0,0 @@
from os import system, chdir
# system("chown -R www:www /usr/src/app/calibredir")
# print("ownership of calibredir changed")
chdir("/usr/src/app/CalibreWebCompanion")
system("python ./manage.py makemigrations")
print("ran makemigrations")
system("python ./manage.py migrate")
print("migrate")

View File

@ -1,36 +0,0 @@
[supervisord]
nodaemon=true
logfile=/tmp/supervisord.log
childlogdir=/tmp
pidfile = /tmp/supervisord.pid
[program:gunicorn]
directory=/usr/src/app/CalibreWebCompanion
command=gunicorn CalibreWebCompanion.wsgi
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0
startsecs = 0
[program:nginx]
# user=www
command=nginx
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0
[program:startupscript]
directory=/usr/src/app
command=python ./startupscript.py
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0

View File

@ -1,9 +1,8 @@
django>=3.0.8 django>=3.0.8
inotify>=0.2.10 inotify>=0.2.10
gunicorn>=20.0
# development # development
# django-debug-toolbar>=2.2 django-debug-toolbar>=2.2
# django-silk>=4.0 django-silk>=4.0
# locust>=1.1 locust>=1.1
# sqlalchemy>=1.3.15 sqlalchemy>=1.3.15
# rich>=3.0 rich>=3.0