33 Commits

Author SHA1 Message Date
fd06a6c72e python3 uwsgi plugin 2021-02-15 12:08:23 +01:00
85fa11315b fixed(??) start.sh 2021-02-15 11:58:30 +01:00
12d13635ad fixed(?) start.sh 2021-02-15 11:55:52 +01:00
5fe4e94e73 added uwsgi to install 2021-02-15 11:53:34 +01:00
e1c66fd034 removed hardcoded static files 2021-02-15 11:51:18 +01:00
7375ffe830 changed db path 2021-02-15 11:50:20 +01:00
8ba82ef0db changed log folder 2021-02-15 11:49:33 +01:00
f7093e5e58 added calibre dir 2021-02-15 11:48:22 +01:00
19c5b0830a remove settings from gitignore 2021-02-15 11:46:49 +01:00
23c1ff7140 no more .bak settings 2021-02-15 11:43:39 +01:00
8187817752 even more fixes 2021-02-15 11:42:40 +01:00
9160a37378 more fixes 2021-02-15 11:41:31 +01:00
fd77792688 fixes 2021-02-15 11:39:49 +01:00
9f5e2e93dd slight fixes to file structure in dockerfile 2021-02-15 11:38:04 +01:00
3a2a2ce268 updates 2021-02-15 11:35:11 +01:00
48443d9855 initial commit 2021-02-15 11:13:30 +01:00
TinyAtoms
d7a385fd45 changed instructions 2020-08-16 02:11:33 -03:00
TinyAtoms
edc9366a5b changed instructions 2020-08-16 02:06:22 -03:00
TinyAtoms
88fcb17dc5 dev fixes
Merge branch 'development'
2020-08-16 01:47:38 -03:00
TinyAtoms
49ca4bccdc fixed nginx, bug in context processors 2020-08-16 01:45:28 -03:00
TinyAtoms
3985a5635d Merge branch 'development' of https://git.tau.aperturect.com/MassiveAtoms/calibre-web-companion into development 2020-08-15 11:04:49 -03:00
TinyAtoms
9843299ef6 fixed some db stuff 2020-08-15 11:00:25 -03:00
TinyAtoms
75099ca05e Removed hardcoded paths, make workflow changes
I removed some hardcoded paths for logging and where the default db should be located.
I also organized deployment stuff a bit
2020-08-15 02:40:03 -03:00
TinyAtoms
1b8d81bd3c pre fixing problems 2020-08-15 01:56:27 -03:00
MassiveAtoms
75e78d606f added tests, fixed search bug, and 'global context variable' bug 2020-08-04 12:21:17 -03:00
TinyAtoms
f9478f2894 merge dev to master 2020-08-02 11:56:32 -03:00
TinyAtoms
b235f67be3 some readme stuff 2020-08-02 11:54:47 -03:00
TinyAtoms
43e5d71cec logging, deployment stuff and readme 2020-08-02 11:21:43 -03:00
TinyAtoms
e11ae55ed9 deployment, and some load testing 2020-08-01 23:51:34 -03:00
TinyAtoms
5182b2cdb6 tested deployment 2020-08-01 22:47:12 -03:00
MassiveAtoms
0806b55cbe clarification 2020-07-31 17:24:55 -03:00
MassiveAtoms
10648a6d0a Revert "clarification readme"
This reverts commit e62e54757a.
2020-07-31 17:07:30 -03:00
MassiveAtoms
e62e54757a clarification readme 2020-07-31 17:05:23 -03:00
35 changed files with 715 additions and 79 deletions

2
.gitignore vendored
View File

@@ -1,5 +1,5 @@
# project specific
settings.json
#settings.json
db.sqlite3
dummyusers.json
*.prof

View File

@@ -12,34 +12,36 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
import os
import json
import logging
logger = logging.getLogger(__name__)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
with open( BASE_DIR + "/settings.json", "r") as userfile:
with open(BASE_DIR + "/settings.json", "r") as userfile:
usersettings = json.load(userfile)
CALIBRE_DIR = os.path.abspath(usersettings["CALIBRE_DIR"])
SECRET_KEY = usersettings["SECRET_KEY"]
ALLOWED_HOSTS = usersettings["ALLOWED_HOSTS"]
INTERNAL_IPS = usersettings["INTERNAL_IPS"]
DEBUG = usersettings["DEBUG"]
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# optimisation stuff ###############################################3
# #
CONN_MAX_AGE = 60 * 5
CONN_MAX_AGE = 60 * 5
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
"TIMEOUT" : 60 * 5,
"TIMEOUT": 60 * 5,
}
}
@@ -51,17 +53,57 @@ CACHES = {
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATICFILES_DIRS = [
os.path.abspath(CALIBRE_DIR),
# os.path.abspath(CALIBRE_DIR),
# '/static/',
]
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR + "/static/"
## ##
#########################################################################
# LOGGING
logfile = usersettings["LOGFOLDER"] + "django.log"
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"root": {"level": "INFO", "handlers": ["file"]},
"handlers": {
"file": {
"level": "INFO",
"class": "logging.FileHandler",
"filename": logfile,
"formatter": "app",
},
},
"loggers": {
"django": {
"handlers": ["file"],
"level": "INFO",
"propagate": True
},
},
"formatters": {
"app": {
"format": (
u"%(asctime)s [%(levelname)-8s] "
"(%(module)s.%(funcName)s) %(message)s"
),
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
}
## ##
########################################################################
## DERUG ##
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG_TOOLBAR_PANELS = [
'debug_toolbar.panels.timer.TimerPanel',
@@ -105,7 +147,7 @@ INSTALLED_APPS = [
MIDDLEWARE = [
# 'silk.middleware.SilkyMiddleware', # DEBUG/profiling purposes
# 'debug_toolbar.middleware.DebugToolbarMiddleware', # DEBUG purposes
'django.middleware.cache.UpdateCacheMiddleware', # cache
'django.middleware.cache.UpdateCacheMiddleware', # cache
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
@@ -113,11 +155,11 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware', # cache
'django.middleware.cache.FetchFromCacheMiddleware', # cache
]
## ##
########################################################################
DEFAULT_CHARSET = "utf-8"
ROOT_URLCONF = 'CalibreWebCompanion.urls'
@@ -147,10 +189,15 @@ WSGI_APPLICATION = 'CalibreWebCompanion.wsgi.application'
## DATBASE ##
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
if usersettings["ISDOCKER"]:
defaultdb_path = "calibre"
else:
defaultdb_path = BASE_DIR
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'NAME': os.path.join(defaultdb_path, 'db.sqlite3'),
},
'calibre': {
'ENGINE': 'django.db.backends.sqlite3',
@@ -159,7 +206,7 @@ DATABASES = {
}
DATABASE_ROUTERS = [ "db_routers.CalibreRouter", "db_routers.DjangoRouter"]
DATABASE_ROUTERS = ["db_routers.CalibreRouter", "db_routers.DjangoRouter"]
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators

View File

@@ -0,0 +1,21 @@
[uwsgi]
base = /cwebcomp
chdir = %(base)
home = %(base)
pidfile= %(base)/cwebcomp.pid
pythonpath= /usr/local
uid = www-data
gid = www-data
module = wsgi:application # path to wsgy.py file
socket = :8000
processes = 8
threads = 4
master = true
chmod-socket = 660
vacuum = true
die-on-term = true
harakiri = 20
max-requests = 5000
logs = %(base)/uwsgi_info.logs
daemonize = %(base)/uwsgi.logs
plugins = python3

View File

@@ -1,4 +1,6 @@
import logging
logger = logging.getLogger(__name__)
class DjangoRouter:
"""

View File

@@ -0,0 +1,33 @@
import multiprocessing
import os
import json
bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1
preload_app = True # By preloading an application you can save some RAM resources as well as speed up server boot times
keepalive = 5
# daemon = True # Detaches the server from the controlling terminal and enters the background. disabled for now
# logging
with open("settings.json", "r") as jfile:
settings = json.load(jfile)
errorlog = settings["LOGFOLDER"] + "/gunicorn_error.log"
loglevel = "warning"
accesslog = settings["LOGFOLDER"] + "/gunicorn_access.log"
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
# reload=True
# reload_engine = "inotify"
# I only went till the section https://docs.gunicorn.org/en/latest/settings.html#logging there are more settings
# some of them might be useful

View File

@@ -1,6 +1,8 @@
from .models import Author, Tag, Publisher, Language, Rating, Series
from django.db.models import Count
import logging
logger = logging.getLogger(__name__)
def filters(request):
# unique_authors = Author.objects.all().order_by('sort')
@@ -13,7 +15,7 @@ def filters(request):
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_publishers = Publisher.objects.annotate(num_books=Count('book')).order_by('name')
unique_languages = Language.objects.annotate(num_books=Count('book')).order_by('rating')
unique_languages = Language.objects.annotate(num_books=Count('book')).order_by('lang_code')
unique_ratings = Rating.objects.annotate(num_books=Count('book'))
unique_series = Series.objects.annotate(num_books=Count('book')).order_by('sort')
@@ -25,4 +27,4 @@ def filters(request):
"unique_languages": unique_languages,
"unique_ratings": unique_ratings,
"unique_series": unique_series
}
}

View File

@@ -1,7 +1,9 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
import logging
logger = logging.getLogger(__name__)
class SearchForm(forms.Form):
title = forms.CharField(label="Title", max_length=200)

View File

@@ -8,7 +8,9 @@
from django.db import models
from django.urls import reverse
from django.utils.functional import cached_property
import logging
logger = logging.getLogger(__name__)
class Author(models.Model):
name = models.TextField()

View File

@@ -6,7 +6,7 @@
<div class="col s12 m7">
<div class="card z-depth-0 horizontal">
<div class="card-image">
<a style="padding-top:15%" href=" {% static "" %}{{download}}"><img src=" {% static "" %}{{imgpath}}"
<a style="padding-top:15%" href=" /download/{{download}}"><img src=" /download/{{imgpath}}"
alt="download" srcset=""></a>
</div>
<div class="card-stacked">

View File

@@ -1,3 +1,166 @@
from django.test import TestCase
from django.test import Client, 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,7 +1,9 @@
from django.urls import path
from . import views
from django.views.decorators.cache import cache_page
import logging
logger = logging.getLogger(__name__)
urlpatterns = [
path('authors/', views.AuthorListView.as_view(), name='authors'),

View File

@@ -1,3 +1,4 @@
from django.utils.decorators import method_decorator
from django.shortcuts import render
from django.views import generic
from .models import Author, Book, Comment, Rating, BookAuthorLink, Publisher, Tag, BookTagLink, BookRatingLink, Data, Identifier, Series
@@ -8,10 +9,11 @@ from django.db.models import Q
from django.contrib.auth.models import User
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
import logging
logger = logging.getLogger(__name__)
# might be helpful for vary headers later
from django.utils.decorators import method_decorator
@login_required
@@ -56,13 +58,29 @@ class ResultsView(generic.ListView): # no clue if this is secure.
if title:
books = books.filter(sort__icontains=title)
if author:
books = books.filter(author_sort__icontains=author)
# authors are stored as author_sort and author, needs to be slightly more complex
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:
books = books.filter(identifier__val=identifier)
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(
Q(sort__icontains=generic) |
Q(author_sort__icontains=generic) |
Q(authors__id=author_id) |
Q(identifier__val=generic)
)
return books

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

View File

@@ -1,10 +1,15 @@
{
"CALIBRE_DIR": "PATH\\TO\\your\\calibre\library",
"CALIBRE_DIR": "calibre",
"SECRET_KEY": "u(8^+rb%rz5hsx4v^^y(ul7g(4n7a8!db@s*9(m5cs*2_ppy8+",
"ALLOWED_HOSTS": [
"127.0.0.1"
],
"INTERNAL_IPS": [
"127.0.0.1"
]
],
"DEBUG" : true,
"LOGFOLDER" : "/cwebcomp/logs",
"ISDOCKER" : true
}

35
Dockerfile Normal file
View File

@@ -0,0 +1,35 @@
FROM python:3.9.1-slim-buster
RUN apt-get clean && \
apt-get update && \
apt-get install -y nginx smbclient default-libmysqlclient-dev \
gcc python3-cffi libcairo2 libpango-1.0-0 libpangocairo-1.0-0 \
libgdk-pixbuf2.0-0 libffi-dev shared-mime-info uwsgi-core uwsgi-plugin-python3
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN mkdir /cwebcomp
WORKDIR /cwebcomp
ADD . /cwebcomp/
# only add this next one if you have static files
RUN mkdir static
RUN pip install -r requirements.txt
RUN python CalibreWebCompanion/manage.py collectstatic
# only if you need celery
#RUN useradd -ms /bin/bash celery
#COPY broker/init.d_celeryd /etc/init.d/celeryd
#COPY broker/celeryd /etc/default/celeryd
# nginx config and script to be run
COPY deployment/docker/nginx.conf /etc/nginx/sites-available/default
COPY deployment/docker/start.sh /usr/local/bin/start.sh
# set proper file permissions
RUN chmod u+x /usr/local/bin/start.sh
EXPOSE 80
CMD ["/bin/bash", "-c", "start.sh"]

View File

@@ -1,11 +1,34 @@
# What is CalibreWebAlternative?
This is a web server to the popular book management application Calibre. We found that the builtin webserver was kinda shit, so we're building our own. (make this friendlier later)
# Features
- navbar with tags, series, authors, etc
- Search by author, identifier, title
- authentication
# Some screenshots
Here's how the various lists look like
![booklist](./screenshots/booklist.png)
Book detail
![bookdetail](./screenshots/bookdetail.png)
navbar
![nav](./screenshots/navbar.png)
Adanced search
![booklist](./screenshots/search.png)
# requirements
Django 3.0
Django 3.0
Calibre 4.13 (I have not tested it with anything else atm, will be resolved later)
# how to use:
EDIT `./CalibreWebCompanion/settings.json.bak`
Remove the `.bak` from `db.sqlite3.bak` and `settings.json.bak`
`./CalibreWebCompanion`
run `./manage.py runserver`
1. [Docker setup](./deployment/instructions.md#user-content-docker-detup)
2. [Non Docker setup](./deployment/instructions.md#user-content-non-docker-detup)
# Ignore pretty much everything below if you're not working on the project
# Profiling
@@ -22,24 +45,25 @@ headless = true
in `locust.conf`, and then run `./bench.py`
You can then go to [http://localhost:8089/](http://localhost:8089/) to see live graphs, tweak the number of users and more.
this is in development mode. don't actually use it or release it like this. The debug info it shows is spicy.
# Features
# Finished Features
- [x] Books
- [x] navbar with tags, series, authors, etc
- [x] Search
- [x] authentication
- [x] Cache
- [x] Profiling with logging
- [x] logging
- [x] deploy instructions
# TODO
# TODO ROADMAP
- [ ] cache with vary headers
- [ ] localisation
- [ ] Beautifying template
- [ ] Setup email functionality
- [ ] Beautifying template (only works well on 720p, no other viewports)
- [ ] Setup email functionality (atm, there's only a dummy one, and you can't reset passwords)
- [ ] isolate the styling and templates, so we can swap them out by just swapping directory content
- [ ] deploy

0
Running Normal file
View File

29
deployment/Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
## pull official base image
FROM python:slim-buster
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

4
deployment/deploy.py Normal file
View File

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

View File

@@ -0,0 +1,20 @@
server {
listen 80;
server_name 127.0.0.1;
charset utf-8;
client_max_body_size 75M;
location /static/ {
alias /cwebcomp/static/;
}
location /media/ {
alias /cwebcomp/media/;
}
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:8000;
}
}

View File

@@ -0,0 +1,4 @@
#!/bin/bash
uwsgi --ini CalibreWebCompanion/CalibreWebCompanion/uwsgi.ini
nginx -g 'daemon off;'

View File

@@ -0,0 +1,45 @@
# Docker setup (no provided docker image atm)
1. clone the repo
2. rename ./calireWebCompanion/settings.json.bak to settings.json
3. change the secret key
4. run `build --tag calibreweb:1.0 . -f ./deployment/Dockerfile` to build the image
5. run your container with your bind/mount your volumes/paths/things
Here's an example of step 5
```
docker run --publish 80: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
```
your Calibre path/volume/whatever needs to be mounted at `/usr/src/app/calibredir`, and you need to mount a volume for the db and logs at `/usr/src/app/data`
Issues with it at the moment:
1. we still need to do something to create a random secret key. Atm, this would still
# Docker (provided image)
not done yet
# non docker setup
this might need to be modified, since some things have changed to adapt it for docker setup
1. clone repo
2. pip install -r requirements.txt
3. rename the settings.json.bak to settings.json, change logging folder, change secret key, set isdocker to false
4. install gunicorn and nginx
5. move this nginx.conf to /etc/nginx
6. create a user and group `www`
7. make whatever user nginx runs as (for now, www) the owner of calibredir
8. give execute permissions to parent of calibredir
9. cd to repo, run `gunicorn CalibreWebCompanion.wsgi`
10. start nginx `sudo systemctl restart nginx`
11. make steps 9 and 10 happen on startup?
Slight issues with this atm:
1. where to do ssl?
Suggestions:
1. We might want to use sockets instead of ip/port?
2. autostart gunicorn/nginx
3. some extra instrumentation for gunicorn https://docs.gunicorn.org/en/latest/deploy.html

81
deployment/nginx.conf Normal file
View File

@@ -0,0 +1,81 @@
worker_processes 1;
# user nobody nogroup;
user www www; # TEMP disabled
# user nobody nobody; # for systems with 'nobody' as a group instead
error_log /usr/src/app/data/logs/nginx.log warn;
# pid /var/run/nginx.pid;
events {
worker_connections 1024; # increase if you have lots of clients
accept_mutex off; # set to 'on' if nginx worker_processes > 1
use epoll; # to enable for Linux 2.6+ MASSIVEATOMS
# 'use kqueue;' to enable for FreeBSD, OSX
}
http {
include mime.types;
# fallback in case we can't determine a type
default_type application/octet-stream;
access_log /var/log/nginx/access.log combined;
sendfile on;
upstream app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response
# for UNIX domain socket setups
# server unix:/tmp/gunicorn.sock fail_timeout=0;
# for a TCP configuration
server 127.0.0.1:8000 fail_timeout=0;
}
server {
# if no Host match, close the connection to prevent host spoofing
listen 80 default_server;
return 444;
}
server {
listen 80 deferred; # for Linux massiveatoms
# use 'listen 80 accept_filter=httpready;' for FreeBSD
# listen 80;
client_max_body_size 4G;
# 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
keepalive_timeout 5;
# # MASSIVEATOMS
location /download/ {
alias "/usr/src/app/calibredir/";
# Never forget the fact that this little statement being root instead of alias caused us to lose more than a day troubleshooting
}
location /static/ {
alias "/usr/src/app/CalibreWebCompanion/static/";
# Never forget the fact that this little statement being root instead of alias caused us to lose more than a day troubleshooting
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
proxy_pass http://127.0.0.1:8000;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /path/to/app/current/public;
}
}
}

View File

@@ -0,0 +1,11 @@
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

@@ -0,0 +1,36 @@
[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 +1,2 @@
Method,Name,Error,Occurrences
Method,Name,Error,Occurrences
GET,/book/<id>,500 Server Error: Internal Server Error for url: /book/<id>,3
1 Method Name Error Occurrences Occurrences
2 GET /book/<id> 500 Server Error: Internal Server Error for url: /book/<id> 3

View File

@@ -1,20 +1,20 @@
Type,Name,Request Count,Failure Count,Median Response Time,Average Response Time,Min Response Time,Max Response Time,Average Content Size,Requests/s,Failures/s,50%,66%,75%,80%,90%,95%,98%,99%,99.9%,99.99%,99.999%,100%
GET,/accounts/login/,20,0,36,59.85466241836548,14.995336532592773,151.90577507019043,2024.0,0.8592061926044773,0.0,41,72,110,120,140,150,150,150,150,150,150,150
POST,/accounts/login/,20,0,540.0,504.5383930206299,198.87518882751465,746.5388774871826,61528.8,0.8592061926044773,0.0,560,580,600,600,680,750,750,750,750,750,750,750
GET,/author/<id>,5,0,37,57.76419639587402,28.97953987121582,138.9150619506836,25477.6,0.21480154815111932,0.0,37,53,53,140,140,140,140,140,140,140,140,140
GET,/authors/,8,0,49,45.841872692108154,3.998994827270508,121.92440032958984,30653.75,0.3436824770417909,0.0,50,50,73,73,120,120,120,120,120,120,120,120
GET,/book/<id>,8,0,31,44.84483599662781,22.98569679260254,111.92703247070312,15194.75,0.3436824770417909,0.0,37,42,55,55,110,110,110,110,110,110,110,110
GET,/books/,10,0,6,13.889431953430176,3.998994827270508,51.969289779663086,54841.6,0.42960309630223864,0.0,7,11,17,28,52,52,52,52,52,52,52,52
GET,/publisher/<id>,9,0,46,47.74824778238932,32.979726791381836,68.95899772644043,27992.444444444445,0.3866427866720148,0.0,46,52,53,63,69,69,69,69,69,69,69,69
GET,/publishers/,7,0,33,27.697699410574778,4.996776580810547,51.969289779663086,25000.14285714286,0.30072216741156704,0.0,33,37,39,39,52,52,52,52,52,52,52,52
GET,/rating/<id>,8,0,66,77.861487865448,44.24405097961426,121.92630767822266,39245.375,0.3436824770417909,0.0,68,110,120,120,120,120,120,120,120,120,120,120
Type,Name,Request Count,Failure Count,Median Response Time,Average Response Time,Min Response Time,Max Response Time,Average Content Size,Requests/s,Failures/s,50%,66%,75%,80%,90%,95%,98%,99%,99.9%,99.99%,99.999%,100%
GET,/accounts/login/,20,0,15,14.807796478271484,8.188486099243164,21.71945571899414,2024.0,0.16739911642581679,0.0,16,17,18,18,20,22,22,22,22,22,22,22
POST,/accounts/login/,20,0,320.0,335.94770431518555,302.3109436035156,504.45032119750977,76378.0,0.16739911642581679,0.0,330,340,340,350,370,500,500,500,500,500,500,500
GET,/author/<id>,40,0,27,27.45213508605957,20.0350284576416,67.98744201660156,30708.725,0.33479823285163357,0.0,27,28,29,29,32,34,68,68,68,68,68,68
GET,/authors/,53,0,33,32.4410987350176,3.3960342407226562,151.21984481811523,39339.0,0.4436076585284145,0.0,33,33,35,36,43,47,73,150,150,150,150,150
GET,/book/<id>,63,3,31,30.65679186866397,20.604372024536133,50.59003829956055,30451.74603174603,0.5273072167413229,0.025109867463872518,31,32,33,33,35,37,37,51,51,51,51,51
GET,/books/,42,0,130.0,110.17770994277228,3.3218860626220703,274.7330665588379,76378.0,0.3515381444942153,0.0,130,130,130,140,160,190,270,270,270,270,270,270
GET,/publisher/<id>,48,0,27,26.85883641242981,17.744064331054688,69.56601142883301,30890.270833333332,0.4017578794219603,0.0,27,27,29,29,31,34,70,70,70,70,70,70
GET,/publishers/,40,0,24,22.84235954284668,4.957437515258789,34.004926681518555,33158.0,0.33479823285163357,0.0,24,26,27,27,28,31,34,34,34,34,34,34
GET,/rating/<id>,53,0,30,36.589348091269436,9.972572326660156,93.70565414428711,35127.301886792455,0.4436076585284145,0.0,30,40,46,51,57,68,85,94,94,94,94,94
GET,/ratings/,42,0,23,21.207468850272043,6.12950325012207,37.114858627319336,30028.0,0.3515381444942153,0.0,23,23,24,24,26,28,37,37,37,37,37,37
GET,/search/,54,0,17,15.807677198339391,3.0889511108398438,24.329662322998047,30164.0,0.45197761434970535,0.0,18,19,20,21,22,23,23,24,24,24,24,24
GET,/series/,44,0,20,19.72411437468095,5.149126052856445,28.244972229003906,29798.0,0.3682780561367969,0.0,21,23,23,24,25,26,28,28,28,28,28,28
GET,/tag/<id>,45,0,26,26.674440171983505,16.248226165771484,37.3835563659668,31041.577777777777,0.3766480119580878,0.0,26,27,28,29,30,35,37,37,37,37,37,37
GET,/tags/,45,0,32,29.666270150078667,3.5097599029541016,77.78573036193848,38073.0,0.3766480119580878,0.0,32,32,33,34,36,39,78,78,78,78,78,78
GET,search_by_author,55,0,22,22.336192564530805,12.028932571411133,51.23710632324219,30187.963636363635,0.4603475701709962,0.0,22,24,24,25,28,30,30,51,51,51,51,51
GET,search_by_identifier,45,0,26,24.89140298631456,17.200946807861328,34.82961654663086,30556.533333333333,0.3766480119580878,0.0,26,27,27,28,30,31,35,35,35,35,35,35
GET,search_by_title,43,0,26,24.867362754289495,12.270927429199219,32.17816352844238,30419.883720930233,0.35990810031550613,0.0,26,27,27,28,29,30,32,32,32,32,32,32
GET,search_generic,62,0,26,26.363442021031535,16.860246658325195,69.95797157287598,30512.548387096773,0.518937260920032,0.0,26,27,28,28,32,34,38,70,70,70,70,70
,Aggregated,814,3,26,37.75617238637563,3.0889511108398438,504.45032119750977,34674.45577395577,6.813144038530743,0.025109867463872518,26,28,31,32,43,130,310,330,500,500,500,500
1 Type Name Request Count Failure Count Median Response Time Average Response Time Min Response Time Max Response Time Average Content Size Requests/s Failures/s 50% 66% 75% 80% 90% 95% 98% 99% 99.9% 99.99% 99.999% 100% 100%
2 GET /accounts/login/ 20 0 36 15 59.85466241836548 14.807796478271484 14.995336532592773 8.188486099243164 151.90577507019043 21.71945571899414 2024.0 0.8592061926044773 0.16739911642581679 0.0 41 16 72 17 110 18 120 18 140 20 150 22 150 22 150 22 150 22 150 22 150 22 150 22
3 POST /accounts/login/ 20 0 540.0 320.0 504.5383930206299 335.94770431518555 198.87518882751465 302.3109436035156 746.5388774871826 504.45032119750977 61528.8 76378.0 0.8592061926044773 0.16739911642581679 0.0 560 330 580 340 600 340 600 350 680 370 750 500 750 500 750 500 750 500 750 500 750 500 750 500
4 GET /author/<id> 5 40 0 37 27 57.76419639587402 27.45213508605957 28.97953987121582 20.0350284576416 138.9150619506836 67.98744201660156 25477.6 30708.725 0.21480154815111932 0.33479823285163357 0.0 37 27 53 28 53 29 140 29 140 32 140 34 140 68 140 68 140 68 140 68 140 68 140 68
5 GET /authors/ 8 53 0 49 33 45.841872692108154 32.4410987350176 3.998994827270508 3.3960342407226562 121.92440032958984 151.21984481811523 30653.75 39339.0 0.3436824770417909 0.4436076585284145 0.0 50 33 50 33 73 35 73 36 120 43 120 47 120 73 120 150 120 150 120 150 120 150 120 150
6 GET /book/<id> 8 63 0 3 31 44.84483599662781 30.65679186866397 22.98569679260254 20.604372024536133 111.92703247070312 50.59003829956055 15194.75 30451.74603174603 0.3436824770417909 0.5273072167413229 0.0 0.025109867463872518 37 31 42 32 55 33 55 33 110 35 110 37 110 37 110 51 110 51 110 51 110 51 110 51
7 GET /books/ 10 42 0 6 130.0 13.889431953430176 110.17770994277228 3.998994827270508 3.3218860626220703 51.969289779663086 274.7330665588379 54841.6 76378.0 0.42960309630223864 0.3515381444942153 0.0 7 130 11 130 17 130 28 140 52 160 52 190 52 270 52 270 52 270 52 270 52 270 52 270
8 GET /publisher/<id> 9 48 0 46 27 47.74824778238932 26.85883641242981 32.979726791381836 17.744064331054688 68.95899772644043 69.56601142883301 27992.444444444445 30890.270833333332 0.3866427866720148 0.4017578794219603 0.0 46 27 52 27 53 29 63 29 69 31 69 34 69 70 69 70 69 70 69 70 69 70 69 70
9 GET /publishers/ 7 40 0 33 24 27.697699410574778 22.84235954284668 4.996776580810547 4.957437515258789 51.969289779663086 34.004926681518555 25000.14285714286 33158.0 0.30072216741156704 0.33479823285163357 0.0 33 24 37 26 39 27 39 27 52 28 52 31 52 34 52 34 52 34 52 34 52 34 52 34
10 GET /rating/<id> 8 53 0 66 30 77.861487865448 36.589348091269436 44.24405097961426 9.972572326660156 121.92630767822266 93.70565414428711 39245.375 35127.301886792455 0.3436824770417909 0.4436076585284145 0.0 68 30 110 40 120 46 120 51 120 57 120 68 120 85 120 94 120 94 120 94 120 94 120 94
11 GET /ratings/ 8 42 0 26 23 34.85342860221863 21.207468850272043 3.9975643157958984 6.12950325012207 69.95558738708496 37.114858627319336 26850.75 30028.0 0.3436824770417909 0.3515381444942153 0.0 34 23 42 23 56 24 56 24 70 26 70 28 70 37 70 37 70 37 70 37 70 37 70 37
12 GET /search/ 11 54 0 26 17 27.622352946888316 15.807677198339391 11.99197769165039 3.0889511108398438 51.96666717529297 24.329662322998047 27841.454545454544 30164.0 0.4725634059324625 0.45197761434970535 0.0 26 18 30 19 34 20 34 21 44 22 52 23 52 23 52 24 52 24 52 24 52 24 52 24
13 GET /series/ 8 44 0 26 20 31.429588794708252 19.72411437468095 3.9980411529541016 5.149126052856445 53.01809310913086 28.244972229003906 20345.375 29798.0 0.3436824770417909 0.3682780561367969 0.0 34 21 50 23 52 23 52 24 53 25 53 26 53 28 53 28 53 28 53 28 53 28 53 28
14 GET /tag/<id> 7 45 0 43 26 50.228118896484375 26.674440171983505 33.979177474975586 16.248226165771484 73.95386695861816 37.3835563659668 27211.85714285714 31041.577777777777 0.30072216741156704 0.3766480119580878 0.0 43 26 54 27 71 28 71 29 74 30 74 35 74 37 74 37 74 37 74 37 74 37 74 37
15 GET /tags/ 5 45 0 56 32 61.562395095825195 29.666270150078667 14.991283416748047 3.5097599029541016 119.92549896240234 77.78573036193848 31376.8 38073.0 0.21480154815111932 0.3766480119580878 0.0 56 32 83 32 83 33 120 34 120 36 120 39 120 78 120 78 120 78 120 78 120 78 120 78
16 GET search_by_author 7 55 0 37 22 46.945163181849885 22.336192564530805 21.98958396911621 12.028932571411133 138.9153003692627 51.23710632324219 26497.571428571428 30187.963636363635 0.30072216741156704 0.4603475701709962 0.0 37 22 39 24 45 24 45 25 140 28 140 30 140 30 140 51 140 51 140 51 140 51 140 51
17 GET search_by_identifier 9 45 0 38 26 55.72432941860623 24.89140298631456 28.980731964111328 17.200946807861328 174.89361763000488 34.82961654663086 30605.11111111111 30556.533333333333 0.3866427866720148 0.3766480119580878 0.0 38 26 38 27 47 27 67 28 170 30 170 31 170 35 170 35 170 35 170 35 170 35 170 35
18 GET search_by_title 7 43 0 38 26 48.82873807634626 24.867362754289495 22.993087768554688 12.270927429199219 101.93872451782227 32.17816352844238 30429.571428571428 30419.883720930233 0.30072216741156704 0.35990810031550613 0.0 38 26 53 27 62 27 62 28 100 29 100 30 100 32 100 32 100 32 100 32 100 32 100 32
19 GET search_generic 13 62 0 39 26 41.35936957139235 26.363442021031535 11.992692947387695 16.860246658325195 96.94075584411621 69.95797157287598 22713.076923076922 30512.548387096773 0.5584840251929102 0.518937260920032 0.0 39 26 49 27 54 28 62 28 62 32 97 34 97 38 97 70 97 70 97 70 97 70 97 70
20 Aggregated 170 814 0 3 40 26 99.35710009406594 37.75617238637563 3.9975643157958984 3.0889511108398438 746.5388774871826 504.45032119750977 29734.95294117647 34674.45577395577 7.303252637138057 6.813144038530743 0.0 0.025109867463872518 41 26 53 28 70 31 110 32 380 43 580 130 600 310 680 330 750 500 750 500 750 500 750 500

View File

@@ -1,15 +1,62 @@
"Timestamp","User Count","Type","Name","Requests/s","Failures/s","50%","66%","75%","80%","90%","95%","98%","99%","99.9%","99.99%","99.999%","100%","Total Request Count","Total Failure Count","Total Median Response Time","Total Average Response Time","Total Min Response Time","Total Max Response Time","Total Average Content Size"
"1594960389","0","","Aggregated",0.00,0.00,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A",0,0,0,0,0,0,0
"1594960391","1","","Aggregated",0.00,0.00,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A",0,0,0,0,0,0,0
"1594960394","5","","Aggregated",0.00,0.00,36,150,470,590,600,680,680,680,680,680,680,680,13,0,36,210,5,680,37214
"1594960396","9","","Aggregated",4.33,0.00,45,150,460,470,590,600,680,680,680,680,680,680,23,0,45,185,5,680,30962
"1594960398","13","","Aggregated",4.25,0.00,50,110,200,380,560,600,680,680,680,680,680,680,41,0,50,165,4,680,32896
"1594960400","17","","Aggregated",5.86,0.00,53,110,200,460,580,600,680,750,750,750,750,750,62,0,52,175,4,746,34112
"1594960402","20","","Aggregated",6.50,0.00,54,110,150,300,580,600,680,750,750,750,750,750,83,0,53,157,4,746,31425
"1594960404","20","","Aggregated",7.40,0.00,52,74,120,140,540,580,680,750,750,750,750,750,96,0,50,145,4,746,32075
"1594960406","20","","Aggregated",8.30,0.00,49,70,110,140,440,580,600,750,750,750,750,750,110,0,49,132,4,746,30740
"1594960408","20","","Aggregated",8.40,0.00,41,52,71,100,140,490,580,600,600,600,600,600,123,0,45,124,4,746,30477
"1594960410","20","","Aggregated",8.20,0.00,39,50,54,66,120,140,440,580,580,580,580,580,135,0,44,117,4,746,30262
"1594960412","20","","Aggregated",7.20,0.00,37,43,52,54,73,120,120,120,120,120,120,120,147,0,43,110,3,746,30003
"1594960414","20","","Aggregated",6.30,0.00,37,45,52,53,67,110,120,120,120,120,120,120,164,0,42,102,3,746,29411
"1594960415","0","","Aggregated",6.80,0.00,34,42,49,52,63,73,120,120,120,120,120,120,170,0,40,99,3,746,29734
"1596336131","1","","Aggregated",0.00,0.00,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A",0,0,0,0,0,0,0
"1596336133","4","","Aggregated",0.00,0.00,18,19,300,300,330,330,330,330,330,330,330,330,12,0,18,115,8,333,37040
"1596336135","8","","Aggregated",6.00,0.00,19,22,300,320,330,330,340,340,340,340,340,340,26,0,19,110,8,344,36889
"1596336137","12","","Aggregated",6.00,0.00,22,28,300,320,330,350,370,370,370,370,370,370,45,0,22,104,3,373,37421
"1596336139","16","","Aggregated",7.33,0.00,23,28,190,310,330,350,370,370,370,370,370,370,63,0,23,99,3,373,37468
"1596336141","20","","Aggregated",7.14,0.00,23,27,51,310,330,340,370,370,370,370,370,370,80,0,23,94,3,373,36325
"1596336143","20","","Aggregated",8.00,0.00,26,30,51,270,330,350,370,500,500,500,500,500,96,0,24,91,3,504,36582
"1596336145","20","","Aggregated",8.30,0.00,26,30,32,40,310,340,370,500,500,500,500,500,111,0,26,82,3,504,35951
"1596336147","20","","Aggregated",8.60,0.00,27,30,32,35,270,340,370,500,500,500,500,500,127,0,25,76,3,504,35750
"1596336149","20","","Aggregated",7.90,0.00,27,31,33,36,130,270,340,500,500,500,500,500,140,0,26,73,3,504,35941
"1596336151","20","","Aggregated",7.90,0.00,27,31,34,35,44,130,150,240,240,240,240,240,153,0,26,72,3,504,35966
"1596336153","20","","Aggregated",7.10,0.00,28,33,35,36,78,130,150,240,240,240,240,240,168,0,26,69,3,504,36026
"1596336155","20","","Aggregated",7.30,0.00,28,33,36,36,78,130,150,240,240,240,240,240,177,0,26,67,3,504,35843
"1596336157","20","","Aggregated",7.30,0.00,28,34,36,36,78,130,150,240,240,240,240,240,188,1,26,64,3,504,35397
"1596336159","20","","Aggregated",6.50,0.10,26,32,35,36,78,140,150,240,240,240,240,240,205,1,26,62,3,504,35527
"1596336161","20","","Aggregated",6.50,0.10,26,29,33,35,47,73,130,140,140,140,140,140,218,1,26,60,3,504,35650
"1596336163","20","","Aggregated",6.60,0.10,25,27,29,32,35,68,140,160,160,160,160,160,235,1,26,58,3,504,35495
"1596336165","20","","Aggregated",6.60,0.10,26,28,32,33,42,130,140,160,160,160,160,160,244,1,26,57,3,504,35601
"1596336167","20","","Aggregated",6.70,0.10,26,29,32,33,55,130,130,160,160,160,160,160,255,2,26,56,3,504,35369
"1596336169","20","","Aggregated",6.60,0.10,26,29,32,32,43,58,130,160,160,160,160,160,269,2,26,54,3,504,35229
"1596336171","20","","Aggregated",6.40,0.10,26,28,31,32,39,55,58,130,130,130,130,130,284,2,26,53,3,504,35026
"1596336173","20","","Aggregated",6.70,0.10,26,29,31,32,39,58,130,130,130,130,130,130,296,2,26,52,3,504,35030
"1596336175","20","","Aggregated",6.10,0.10,26,29,31,32,35,55,130,130,130,130,130,130,310,2,26,51,3,504,34991
"1596336177","20","","Aggregated",6.40,0.10,25,29,30,31,35,51,130,130,130,130,130,130,320,2,26,50,3,504,34890
"1596336179","20","","Aggregated",6.50,0.00,25,28,29,30,33,120,130,130,130,130,130,130,336,2,26,49,3,504,34830
"1596336181","20","","Aggregated",6.70,0.00,26,27,29,30,33,44,130,140,140,140,140,140,351,2,26,48,3,504,34829
"1596336183","20","","Aggregated",6.10,0.00,25,27,29,30,35,94,130,140,140,140,140,140,363,2,26,48,3,504,34787
"1596336185","20","","Aggregated",6.70,0.00,26,28,29,30,37,120,150,190,190,190,190,190,378,2,26,48,3,504,34880
"1596336187","20","","Aggregated",6.40,0.00,26,28,30,33,85,130,150,190,190,190,190,190,391,2,26,47,3,504,34874
"1596336189","20","","Aggregated",6.80,0.00,27,29,33,34,94,140,150,190,190,190,190,190,400,2,26,47,3,504,34951
"1596336191","20","","Aggregated",6.60,0.00,27,28,32,33,46,130,150,190,190,190,190,190,417,2,26,46,3,504,34925
"1596336193","20","","Aggregated",6.90,0.00,26,28,29,31,37,85,130,130,130,130,130,130,430,2,26,46,3,504,34937
"1596336195","20","","Aggregated",6.60,0.00,26,29,30,32,46,120,130,130,130,130,130,130,443,2,26,45,3,504,34959
"1596336197","20","","Aggregated",6.60,0.00,26,28,29,30,35,46,120,130,130,130,130,130,454,2,26,45,3,504,34988
"1596336199","20","","Aggregated",6.50,0.00,26,28,29,30,32,37,120,130,130,130,130,130,473,2,26,44,3,504,34866
"1596336201","20","","Aggregated",7.10,0.00,26,28,29,30,32,35,120,130,130,130,130,130,485,2,26,44,3,504,34795
"1596336203","20","","Aggregated",6.80,0.00,26,29,30,30,32,33,34,38,38,38,38,38,501,2,26,43,3,504,34747
"1596336205","20","","Aggregated",7.20,0.00,27,29,30,31,32,33,34,43,43,43,43,43,512,2,26,43,3,504,34754
"1596336207","20","","Aggregated",7.10,0.00,27,28,30,32,33,40,56,130,130,130,130,130,529,2,26,42,3,504,34777
"1596336209","20","","Aggregated",7.20,0.00,27,29,32,33,40,57,130,130,130,130,130,130,541,2,26,42,3,504,34936
"1596336211","20","","Aggregated",6.80,0.00,27,29,32,33,43,130,130,130,130,130,130,130,554,2,26,42,3,504,34959
"1596336213","20","","Aggregated",7.10,0.00,27,29,32,33,57,130,130,130,130,130,130,130,572,2,26,42,3,504,35016
"1596336215","20","","Aggregated",6.90,0.00,27,28,31,33,57,130,130,130,130,130,130,130,579,2,26,42,3,504,34976
"1596336217","20","","Aggregated",6.80,0.00,27,28,31,32,50,130,130,130,130,130,130,130,594,2,26,41,3,504,34913
"1596336219","20","","Aggregated",6.70,0.00,27,27,29,31,45,120,130,140,140,140,140,140,613,2,26,41,3,504,34870
"1596336221","20","","Aggregated",6.70,0.00,26,27,28,29,32,46,130,140,140,140,140,140,625,2,26,41,3,504,34803
"1596336223","20","","Aggregated",6.80,0.00,25,27,28,30,32,33,50,140,140,140,140,140,638,2,26,40,3,504,34731
"1596336225","20","","Aggregated",6.80,0.00,25,27,30,31,32,33,50,140,140,140,140,140,648,2,26,40,3,504,34695
"1596336227","20","","Aggregated",6.80,0.00,26,27,31,31,33,34,36,140,140,140,140,140,663,2,26,40,3,504,34632
"1596336229","20","","Aggregated",6.70,0.00,26,28,31,32,34,36,36,44,44,44,44,44,673,2,26,40,3,504,34673
"1596336231","20","","Aggregated",6.60,0.00,26,29,31,33,35,36,44,120,120,120,120,120,687,2,26,39,3,504,34659
"1596336233","20","","Aggregated",6.40,0.00,27,29,31,33,35,44,68,120,120,120,120,120,698,2,26,39,3,504,34624
"1596336235","20","","Aggregated",5.90,0.00,26,28,30,32,35,56,120,120,120,120,120,120,714,2,26,39,3,504,34690
"1596336237","20","","Aggregated",6.30,0.00,26,28,30,32,44,120,120,130,130,130,130,130,724,2,26,39,3,504,34719
"1596336239","20","","Aggregated",6.00,0.00,24,27,28,30,34,56,120,130,130,130,130,130,737,2,26,39,3,504,34667
"1596336241","20","","Aggregated",6.20,0.00,24,27,28,29,34,68,120,130,130,130,130,130,750,2,26,38,3,504,34630
"1596336243","20","","Aggregated",6.20,0.00,24,26,27,28,31,36,58,130,130,130,130,130,765,2,26,38,3,504,34688
"1596336245","20","","Aggregated",6.90,0.00,24,26,28,28,32,36,58,130,130,130,130,130,778,2,26,38,3,504,34700
"1596336247","20","","Aggregated",6.50,0.00,25,27,28,30,34,37,58,130,130,130,130,130,790,3,26,38,3,504,34677
"1596336249","20","","Aggregated",6.70,0.10,25,27,30,31,34,37,58,130,130,130,130,130,802,3,26,38,3,504,34713
"1596336250","0","","Aggregated",6.10,0.10,24,27,29,30,33,37,58,130,130,130,130,130,814,3,26,37,3,504,34674
1 Timestamp User Count Type Name Requests/s Failures/s 50% 66% 75% 80% 90% 95% 98% 99% 99.9% 99.99% 99.999% 100% Total Request Count Total Failure Count Total Median Response Time Total Average Response Time Total Min Response Time Total Max Response Time Total Average Content Size
2 1594960389 1596336131 0 1 Aggregated 0.00 0.00 N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A 0 0 0 0 0 0 0
3 1594960391 1596336133 1 4 Aggregated 0.00 0.00 N/A 18 N/A 19 N/A 300 N/A 300 N/A 330 N/A 330 N/A 330 N/A 330 N/A 330 N/A 330 N/A 330 N/A 330 0 12 0 0 18 0 115 0 8 0 333 0 37040
4 1594960394 1596336135 5 8 Aggregated 0.00 6.00 0.00 36 19 150 22 470 300 590 320 600 330 680 330 680 340 680 340 680 340 680 340 680 340 680 340 13 26 0 36 19 210 110 5 8 680 344 37214 36889
5 1594960396 1596336137 9 12 Aggregated 4.33 6.00 0.00 45 22 150 28 460 300 470 320 590 330 600 350 680 370 680 370 680 370 680 370 680 370 680 370 23 45 0 45 22 185 104 5 3 680 373 30962 37421
6 1594960398 1596336139 13 16 Aggregated 4.25 7.33 0.00 50 23 110 28 200 190 380 310 560 330 600 350 680 370 680 370 680 370 680 370 680 370 680 370 41 63 0 50 23 165 99 4 3 680 373 32896 37468
7 1594960400 1596336141 17 20 Aggregated 5.86 7.14 0.00 53 23 110 27 200 51 460 310 580 330 600 340 680 370 750 370 750 370 750 370 750 370 750 370 62 80 0 52 23 175 94 4 3 746 373 34112 36325
8 1594960402 1596336143 20 Aggregated 6.50 8.00 0.00 54 26 110 30 150 51 300 270 580 330 600 350 680 370 750 500 750 500 750 500 750 500 750 500 83 96 0 53 24 157 91 4 3 746 504 31425 36582
9 1594960404 1596336145 20 Aggregated 7.40 8.30 0.00 52 26 74 30 120 32 140 40 540 310 580 340 680 370 750 500 750 500 750 500 750 500 750 500 96 111 0 50 26 145 82 4 3 746 504 32075 35951
10 1594960406 1596336147 20 Aggregated 8.30 8.60 0.00 49 27 70 30 110 32 140 35 440 270 580 340 600 370 750 500 750 500 750 500 750 500 750 500 110 127 0 49 25 132 76 4 3 746 504 30740 35750
11 1594960408 1596336149 20 Aggregated 8.40 7.90 0.00 41 27 52 31 71 33 100 36 140 130 490 270 580 340 600 500 600 500 600 500 600 500 600 500 123 140 0 45 26 124 73 4 3 746 504 30477 35941
12 1594960410 1596336151 20 Aggregated 8.20 7.90 0.00 39 27 50 31 54 34 66 35 120 44 140 130 440 150 580 240 580 240 580 240 580 240 580 240 135 153 0 44 26 117 72 4 3 746 504 30262 35966
13 1594960412 1596336153 20 Aggregated 7.20 7.10 0.00 37 28 43 33 52 35 54 36 73 78 120 130 120 150 120 240 120 240 120 240 120 240 120 240 147 168 0 43 26 110 69 3 746 504 30003 36026
14 1594960414 1596336155 20 Aggregated 6.30 7.30 0.00 37 28 45 33 52 36 53 36 67 78 110 130 120 150 120 240 120 240 120 240 120 240 120 240 164 177 0 42 26 102 67 3 746 504 29411 35843
15 1594960415 1596336157 0 20 Aggregated 6.80 7.30 0.00 34 28 42 34 49 36 52 36 63 78 73 130 120 150 120 240 120 240 120 240 120 240 120 240 170 188 0 1 40 26 99 64 3 746 504 29734 35397
16 1596336159 20 Aggregated 6.50 0.10 26 32 35 36 78 140 150 240 240 240 240 240 205 1 26 62 3 504 35527
17 1596336161 20 Aggregated 6.50 0.10 26 29 33 35 47 73 130 140 140 140 140 140 218 1 26 60 3 504 35650
18 1596336163 20 Aggregated 6.60 0.10 25 27 29 32 35 68 140 160 160 160 160 160 235 1 26 58 3 504 35495
19 1596336165 20 Aggregated 6.60 0.10 26 28 32 33 42 130 140 160 160 160 160 160 244 1 26 57 3 504 35601
20 1596336167 20 Aggregated 6.70 0.10 26 29 32 33 55 130 130 160 160 160 160 160 255 2 26 56 3 504 35369
21 1596336169 20 Aggregated 6.60 0.10 26 29 32 32 43 58 130 160 160 160 160 160 269 2 26 54 3 504 35229
22 1596336171 20 Aggregated 6.40 0.10 26 28 31 32 39 55 58 130 130 130 130 130 284 2 26 53 3 504 35026
23 1596336173 20 Aggregated 6.70 0.10 26 29 31 32 39 58 130 130 130 130 130 130 296 2 26 52 3 504 35030
24 1596336175 20 Aggregated 6.10 0.10 26 29 31 32 35 55 130 130 130 130 130 130 310 2 26 51 3 504 34991
25 1596336177 20 Aggregated 6.40 0.10 25 29 30 31 35 51 130 130 130 130 130 130 320 2 26 50 3 504 34890
26 1596336179 20 Aggregated 6.50 0.00 25 28 29 30 33 120 130 130 130 130 130 130 336 2 26 49 3 504 34830
27 1596336181 20 Aggregated 6.70 0.00 26 27 29 30 33 44 130 140 140 140 140 140 351 2 26 48 3 504 34829
28 1596336183 20 Aggregated 6.10 0.00 25 27 29 30 35 94 130 140 140 140 140 140 363 2 26 48 3 504 34787
29 1596336185 20 Aggregated 6.70 0.00 26 28 29 30 37 120 150 190 190 190 190 190 378 2 26 48 3 504 34880
30 1596336187 20 Aggregated 6.40 0.00 26 28 30 33 85 130 150 190 190 190 190 190 391 2 26 47 3 504 34874
31 1596336189 20 Aggregated 6.80 0.00 27 29 33 34 94 140 150 190 190 190 190 190 400 2 26 47 3 504 34951
32 1596336191 20 Aggregated 6.60 0.00 27 28 32 33 46 130 150 190 190 190 190 190 417 2 26 46 3 504 34925
33 1596336193 20 Aggregated 6.90 0.00 26 28 29 31 37 85 130 130 130 130 130 130 430 2 26 46 3 504 34937
34 1596336195 20 Aggregated 6.60 0.00 26 29 30 32 46 120 130 130 130 130 130 130 443 2 26 45 3 504 34959
35 1596336197 20 Aggregated 6.60 0.00 26 28 29 30 35 46 120 130 130 130 130 130 454 2 26 45 3 504 34988
36 1596336199 20 Aggregated 6.50 0.00 26 28 29 30 32 37 120 130 130 130 130 130 473 2 26 44 3 504 34866
37 1596336201 20 Aggregated 7.10 0.00 26 28 29 30 32 35 120 130 130 130 130 130 485 2 26 44 3 504 34795
38 1596336203 20 Aggregated 6.80 0.00 26 29 30 30 32 33 34 38 38 38 38 38 501 2 26 43 3 504 34747
39 1596336205 20 Aggregated 7.20 0.00 27 29 30 31 32 33 34 43 43 43 43 43 512 2 26 43 3 504 34754
40 1596336207 20 Aggregated 7.10 0.00 27 28 30 32 33 40 56 130 130 130 130 130 529 2 26 42 3 504 34777
41 1596336209 20 Aggregated 7.20 0.00 27 29 32 33 40 57 130 130 130 130 130 130 541 2 26 42 3 504 34936
42 1596336211 20 Aggregated 6.80 0.00 27 29 32 33 43 130 130 130 130 130 130 130 554 2 26 42 3 504 34959
43 1596336213 20 Aggregated 7.10 0.00 27 29 32 33 57 130 130 130 130 130 130 130 572 2 26 42 3 504 35016
44 1596336215 20 Aggregated 6.90 0.00 27 28 31 33 57 130 130 130 130 130 130 130 579 2 26 42 3 504 34976
45 1596336217 20 Aggregated 6.80 0.00 27 28 31 32 50 130 130 130 130 130 130 130 594 2 26 41 3 504 34913
46 1596336219 20 Aggregated 6.70 0.00 27 27 29 31 45 120 130 140 140 140 140 140 613 2 26 41 3 504 34870
47 1596336221 20 Aggregated 6.70 0.00 26 27 28 29 32 46 130 140 140 140 140 140 625 2 26 41 3 504 34803
48 1596336223 20 Aggregated 6.80 0.00 25 27 28 30 32 33 50 140 140 140 140 140 638 2 26 40 3 504 34731
49 1596336225 20 Aggregated 6.80 0.00 25 27 30 31 32 33 50 140 140 140 140 140 648 2 26 40 3 504 34695
50 1596336227 20 Aggregated 6.80 0.00 26 27 31 31 33 34 36 140 140 140 140 140 663 2 26 40 3 504 34632
51 1596336229 20 Aggregated 6.70 0.00 26 28 31 32 34 36 36 44 44 44 44 44 673 2 26 40 3 504 34673
52 1596336231 20 Aggregated 6.60 0.00 26 29 31 33 35 36 44 120 120 120 120 120 687 2 26 39 3 504 34659
53 1596336233 20 Aggregated 6.40 0.00 27 29 31 33 35 44 68 120 120 120 120 120 698 2 26 39 3 504 34624
54 1596336235 20 Aggregated 5.90 0.00 26 28 30 32 35 56 120 120 120 120 120 120 714 2 26 39 3 504 34690
55 1596336237 20 Aggregated 6.30 0.00 26 28 30 32 44 120 120 130 130 130 130 130 724 2 26 39 3 504 34719
56 1596336239 20 Aggregated 6.00 0.00 24 27 28 30 34 56 120 130 130 130 130 130 737 2 26 39 3 504 34667
57 1596336241 20 Aggregated 6.20 0.00 24 27 28 29 34 68 120 130 130 130 130 130 750 2 26 38 3 504 34630
58 1596336243 20 Aggregated 6.20 0.00 24 26 27 28 31 36 58 130 130 130 130 130 765 2 26 38 3 504 34688
59 1596336245 20 Aggregated 6.90 0.00 24 26 28 28 32 36 58 130 130 130 130 130 778 2 26 38 3 504 34700
60 1596336247 20 Aggregated 6.50 0.00 25 27 28 30 34 37 58 130 130 130 130 130 790 3 26 38 3 504 34677
61 1596336249 20 Aggregated 6.70 0.10 25 27 30 31 34 37 58 130 130 130 130 130 802 3 26 38 3 504 34713
62 1596336250 0 Aggregated 6.10 0.10 24 27 29 30 33 37 58 130 130 130 130 130 814 3 26 37 3 504 34674

View File

@@ -1,6 +1,6 @@
locustfile = locustfile.py
expect-workers = 50
host = http://127.0.0.1:8000
expect-workers = 200
host = http://localhost
users = 20
hatch-rate = 2
run-time = 2m

View File

@@ -8,7 +8,7 @@ import json
# -------------------------------- fetching data to test with
with open("./../CalibreWebCompanion/settings.json", "r") as jfile:
calpath = json.load(jfile)["CALIBRE_DIR"] + "\\metadata.db"
calpath = json.load(jfile)["CALIBRE_DIR"] + "/metadata.db"
with open("dummyusers.json", "r") as jfile:
users = json.load(jfile)

View File

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

BIN
screenshots/bookdetail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

BIN
screenshots/booklist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
screenshots/navbar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
screenshots/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB