reasonably working calibreweb thing

This commit is contained in:
MassiveAtoms 2020-07-07 23:11:21 -03:00
commit 8ec8c686c6
38 changed files with 1752 additions and 0 deletions

View File

@ -0,0 +1,16 @@
"""
ASGI config for CalibreWebCompanion project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'CalibreWebCompanion.settings')
application = get_asgi_application()

View File

@ -0,0 +1,137 @@
"""
Django settings for CalibreWebCompanion project.
Generated by 'django-admin startproject' using Django 3.0.8.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CALIBRE_DIR = os.path.abspath("C:\\Users\\MassiveAtoms\\Documents\\Calibre Library")
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'u(8^+rb%rz5hsx4v^^y(ul7g(4n7a8!db@s*9(m5cs*2_ppy8+'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"library"
]
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 = 'CalibreWebCompanion.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, '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 = 'CalibreWebCompanion.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'calibre': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(CALIBRE_DIR, 'metadata.db'),
}
}
DATABASE_ROUTERS = ["db_routers.DjangoRouter", "db_routers.CalibreRouter"]
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Paramaribo'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATICFILES_DIRS = [
os.path.abspath(CALIBRE_DIR),
# '/static/',
]
STATIC_URL = '/static/'

View File

@ -0,0 +1,27 @@
"""CalibreWebCompanion URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from django.views.generic import RedirectView
urlpatterns = [
path('admin/', admin.site.urls),
path('library/', include('library.urls')),
path('', RedirectView.as_view(url='library/', permanent=True)),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

View File

@ -0,0 +1,16 @@
"""
WSGI config for CalibreWebCompanion project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'CalibreWebCompanion.settings')
application = get_wsgi_application()

Binary file not shown.

View File

@ -0,0 +1,2 @@
from .routers import DjangoRouter, CalibreRouter
__name__ = "db_routers"

View File

@ -0,0 +1,76 @@
class DjangoRouter:
"""
A router to control all database operations on models in the
auth and contenttypes applications.
"""
route_app_labels = {'auth', 'contenttypes', "sessions", "sites", "admin", "flatpages"}
def db_for_read(self, model, **hints):
"""
Attempts to read auth and contenttypes models go to default.
"""
if model._meta.app_label in self.route_app_labels:
return 'default'
return None
def db_for_write(self, model, **hints):
"""
Attempts to write auth and contenttypes models go to django.
"""
if model._meta.app_label in self.route_app_labels:
return 'default'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the auth or contenttypes apps is
involved.
"""
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the auth and contenttypes apps only appear in the
'django' database.
"""
if app_label in self.route_app_labels:
return db == 'default'
return None
class CalibreRouter:
"""
A router to control all database operations on models in the
auth and contenttypes applications.
"""
def db_for_read(self, model, **hints):
"""
Attempts to read anything else goes to calibre
"""
return 'calibre'
# def db_for_write(self, model, **hints): # might be prudent not to allow writes
# """
# Attempts to write auth and contenttypes models go to 'calibre'.
# """
# return 'calibre'
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations.
"""
return True
# def allow_migrate(self, db, app_label, model_name=None, **hints): # might be prudent not to allow migrations
# """
# Yes
# """
# return True

View File

View File

@ -0,0 +1,265 @@
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models
class Authors(models.Model):
name = models.TextField()
sort = models.TextField(blank=True, null=True)
link = models.TextField()
class Meta:
managed = False
db_table = 'authors'
class Comments(models.Model):
book = models.ForeignField("Book")
text = models.TextField()
class Meta:
managed = False
db_table = 'comments'
class Data(models.Model):
book = models.IntegerField()
format = models.TextField()
uncompressed_size = models.IntegerField()
name = models.TextField()
class Meta:
managed = False
db_table = 'data'
class Identifiers(models.Model):
book = models.IntegerField()
type = models.TextField()
val = models.TextField()
class Meta:
managed = False
db_table = 'identifiers'
class Languages(models.Model):
lang_code = models.TextField()
class Meta:
managed = False
db_table = 'languages'
class Publishers(models.Model):
name = models.TextField()
sort = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'publishers'
class Ratings(models.Model):
rating = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'ratings'
class Series(models.Model):
name = models.TextField()
sort = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'series'
class Tags(models.Model):
name = models.TextField()
class Meta:
managed = False
db_table = 'tags'
class Books(models.Model):
title = models.TextField()
sort = models.TextField(blank=True, null=True)
# This field type is a guess.
timestamp = models.TextField(blank=True, null=True)
# This field type is a guess.
pubdate = models.TextField(blank=True, null=True)
series_index = models.FloatField()
author_sort = models.TextField(blank=True, null=True)
isbn = models.TextField(blank=True, null=True)
lccn = models.TextField(blank=True, null=True)
path = models.TextField()
flags = models.IntegerField()
uuid = models.TextField(blank=True, null=True)
has_cover = models.BooleanField(blank=True, null=True)
last_modified = models.TextField() # This field type is a guess.
authors = models.ManyToManyField(
Authors,
through='BooksAuthorsLink',
through_fields=('book', 'author'))
languages = models.ManyToManyField(
Languages,
through='BooksLanguagesLink',
through_fields=('book', 'lang_code'))
publishers = models.ManyToManyField(
Publishers,
through='BooksPublishersLink',
through_fields=('book', 'publisher'))
series = models.ManyToManyField(
Series,
through='BooksSeriesLink',
through_fields=('book', 'series'))
tags = models.ManyToManyField(
Tags,
through='BooksTagsLink',
through_fields=('book', 'tag'))
class Meta:
managed = False
db_table = 'books'
class BooksAuthorsLink(models.Model):
book = models.ForeignKey(db_column="book")
author = models.ForeignKey(db_column="author")
class Meta:
managed = False
db_table = 'books_authors_link'
class BooksLanguagesLink(models.Model):
book = models.ForeignKey(db_colum="book")
lang_code = models.ForeignKey(db_column="lang_code")
item_order = models.IntegerField()
class Meta:
managed = False
db_table = 'books_languages_link'
class BooksPublishersLink(models.Model):
book = models.ForeignKey(db_column="book")
publisher = models.ForeignKey(db_column="publisher")
class Meta:
managed = False
db_table = 'books_publishers_link'
# class BooksRatingsLink(models.Model): # TODO add this somehow
# book = models.ForeignKey(db_column="book")
# rating = models.IntegerField()
# class Meta:
# managed = False
# db_table = 'books_ratings_link'
class BooksSeriesLink(models.Model):
book = models.ForeignKey(db_column="book")
series = models.ForeignKey(db_column="series")
class Meta:
managed = False
db_table = 'books_series_link'
class BooksTagsLink(models.Model):
book = models.ForeignKey(db_column="book")
tag = models.ForeignKey(db_column="tag")
class Meta:
managed = False
db_table = 'books_tags_link'
# class BooksPluginData(models.Model):
# book = models.IntegerField()
# name = models.TextField()
# val = models.TextField()
# class Meta:
# managed = False
# db_table = 'books_plugin_data'
# class ConversionOptions(models.Model):
# format = models.TextField()
# book = models.IntegerField(blank=True, null=True)
# data = models.BinaryField()
#
# class Meta:
# managed = False
# db_table = 'conversion_options'
#
# class LibraryId(models.Model):
# uuid = models.TextField()
#
# class Meta:
# managed = False
# db_table = 'library_id'
#
# class CustomColumns(models.Model):
# label = models.TextField()
# name = models.TextField()
# datatype = models.TextField()
# mark_for_delete = models.BooleanField()
# editable = models.BooleanField()
# display = models.TextField()
# is_multiple = models.BooleanField()
# normalized = models.BooleanField()
#
# class Meta:
# managed = False
# db_table = 'custom_columns'
#
# class Preferences(models.Model):
# key = models.TextField()
# val = models.TextField()
#
# class Meta:
# managed = False
# db_table = 'preferences'
#
# class Feeds(models.Model):
# title = models.TextField()
# script = models.TextField()
#
# class Meta:
# managed = False
# db_table = 'feeds'
#
#
# class LastReadPositions(models.Model):
# book = models.IntegerField()
# format = models.TextField()
# user = models.TextField()
# device = models.TextField()
# cfi = models.TextField()
# epoch = models.FloatField()
# pos_frac = models.FloatField()
#
# class Meta:
# managed = False
# db_table = 'last_read_positions'
# class MetadataDirtied(models.Model):
# book = models.IntegerField()
# class Meta:
# managed = False
# db_table = 'metadata_dirtied'

View File

@ -0,0 +1,27 @@
from django.contrib import admin
from .models import Authors, Books, Languages, Publishers, Series, Tags
# Register your models here.
@admin.register(Authors)
class AuthorAdmin(admin.ModelAdmin):
list_display = (["name"])
@admin.register(Languages)
class LanguageAdmin(admin.ModelAdmin):
list_display = (["id", "lang_code"])
@admin.register(Publishers)
class PublisherAdmin(admin.ModelAdmin):
list_display = (["id","name"])
@admin.register(Series)
class SeriesAdmin(admin.ModelAdmin):
list_display = (["id","name"])
@admin.register(Tags)
class TagAdmin(admin.ModelAdmin):
list_display = (["id","name"])
@admin.register(Books)
class BookAdmin(admin.ModelAdmin):
list_display = (["id","title", "author_sort"])

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class LibraryConfig(AppConfig):
name = 'library'

View File

@ -0,0 +1,107 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///C://Users//MassiveAtoms//Documents//Calibre Library//metadata.db.', echo=True)
Base = declarative_base(engine)
########################################################################
class Author(Base): # needed
""""""
__tablename__ = 'authors'
__table_args__ = {'autoload':True}
# has int id, text name, sort
class Comment(Base): # needed
""""""
__tablename__ = 'comments'
__table_args__ = {'autoload':True}
# has int id, int book, text text
# class Data(Base): # maybe
# """"""
# __tablename__ = 'data'
# __table_args__ = {'autoload':True}
# # has int id, int book, text format, text name
class Identifier(Base): # needed
""""""
__tablename__ = 'identifiers'
__table_args__ = {'autoload':True}
# has int id, int book, text value
class Publisher(Base): # needed
""""""
__tablename__ = 'publishers'
__table_args__ = {'autoload':True}
# has int id, text name
class Rating(Base): # needed
""""""
__tablename__ = 'ratings'
__table_args__ = {'autoload':True}
# has int id, int rating
class Series(Base): # needed
""""""
__tablename__ = 'series'
__table_args__ = {'autoload':True}
# has int id, text name
class Tag(Base): # needed
""""""
__tablename__ = 'tags'
__table_args__ = {'autoload':True}
# has int id, text name
class Book(Base): # needed
""""""
__tablename__ = 'books'
__table_args__ = {'autoload':True}
# has int id, text title, text sort, time timestamp, time pubdate,
# float series_index, text path
class Book_author_link(Base): # needed
""""""
__tablename__ = 'books_authors_link'
__table_args__ = {'autoload':True}
# has int id, id book, id author
class Book_publisher_link(Base): # needed
""""""
__tablename__ = 'books_publishers_link'
__table_args__ = {'autoload':True}
# has int id, id book, id publisher
class Book_rating_link(Base): # needed
""""""
__tablename__ = 'books_ratings_link'
__table_args__ = {'autoload':True}
# has int id, id book, id rating
class Book_series_link(Base): # needed
""""""
__tablename__ = 'books_series_link'
__table_args__ = {'autoload':True}
# has int id, id book, id series
class Book_tags_link(Base): # needed
""""""
__tablename__ = 'books_tags_link'
__table_args__ = {'autoload':True}
# has int id, id book, id tag
#----------------------------------------------------------------------
def loadSession():
""""""
metadata = Base.metadata
Session = sessionmaker(bind=engine)
session = Session()
return session
if __name__ == "__main__":
session = loadSession()
res = session.query(Book).all()
for i in res:
print(i.id, i.title, i.sort, i.timestamp, i.pubdate, i.series_index, i.path)

View File

@ -0,0 +1,206 @@
# Generated by Django 3.0.8 on 2020-07-07 17:56
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Authors',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField()),
('sort', models.TextField(blank=True, null=True)),
('link', models.TextField()),
],
options={
'db_table': 'authors',
'managed': False,
},
),
migrations.CreateModel(
name='Books',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.TextField()),
('sort', models.TextField(blank=True, null=True)),
('timestamp', models.TextField(blank=True, null=True)),
('pubdate', models.TextField(blank=True, null=True)),
('series_index', models.FloatField()),
('author_sort', models.TextField(blank=True, null=True)),
('isbn', models.TextField(blank=True, null=True)),
('lccn', models.TextField(blank=True, null=True)),
('path', models.TextField()),
('flags', models.IntegerField()),
('uuid', models.TextField(blank=True, null=True)),
('has_cover', models.BooleanField(blank=True, null=True)),
('last_modified', models.TextField()),
],
options={
'db_table': 'books',
'managed': False,
},
),
migrations.CreateModel(
name='BooksAuthorsLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'db_table': 'books_authors_link',
'managed': False,
},
),
migrations.CreateModel(
name='BooksLanguagesLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('item_order', models.IntegerField()),
],
options={
'db_table': 'books_languages_link',
'managed': False,
},
),
migrations.CreateModel(
name='BooksPublishersLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'db_table': 'books_publishers_link',
'managed': False,
},
),
migrations.CreateModel(
name='BooksRatingsLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'db_table': 'books_ratings_link',
'managed': False,
},
),
migrations.CreateModel(
name='BooksSeriesLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'db_table': 'books_series_link',
'managed': False,
},
),
migrations.CreateModel(
name='BooksTagsLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'db_table': 'books_tags_link',
'managed': False,
},
),
migrations.CreateModel(
name='Comments',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField()),
],
options={
'db_table': 'comments',
'managed': False,
},
),
migrations.CreateModel(
name='Data',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('book', models.IntegerField()),
('format', models.TextField()),
('uncompressed_size', models.IntegerField()),
('name', models.TextField()),
],
options={
'db_table': 'data',
'managed': False,
},
),
migrations.CreateModel(
name='Identifiers',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('book', models.IntegerField()),
('type', models.TextField()),
('val', models.TextField()),
],
options={
'db_table': 'identifiers',
'managed': False,
},
),
migrations.CreateModel(
name='Languages',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lang_code', models.TextField()),
],
options={
'db_table': 'languages',
'managed': False,
},
),
migrations.CreateModel(
name='Publishers',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField()),
('sort', models.TextField(blank=True, null=True)),
],
options={
'db_table': 'publishers',
'managed': False,
},
),
migrations.CreateModel(
name='Ratings',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('rating', models.IntegerField(blank=True, null=True)),
],
options={
'db_table': 'ratings',
'managed': False,
},
),
migrations.CreateModel(
name='Series',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField()),
('sort', models.TextField(blank=True, null=True)),
],
options={
'db_table': 'series',
'managed': False,
},
),
migrations.CreateModel(
name='Tags',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField()),
],
options={
'db_table': 'tags',
'managed': False,
},
),
]

View File

@ -0,0 +1,387 @@
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models
from django.urls import reverse
class Authors(models.Model):
name = models.TextField()
sort = models.TextField(blank=True, null=True)
link = models.TextField()
def get_absolute_url(self):
"""Returns the url to access a particular instance of MyModelName."""
return reverse('author-detail-view', args=[str(self.id)])
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.name
class Meta:
managed = False
db_table = 'authors'
class Comments(models.Model):
book = models.ForeignKey("Books", db_column="book", on_delete=models.CASCADE)
text = models.TextField()
class Meta:
managed = False
db_table = 'comments'
indexes = [
models.Index(fields=["book"], name="comments_idx"),
]
class Data(models.Model):
book = models.IntegerField()
format = models.TextField()
uncompressed_size = models.IntegerField()
name = models.TextField()
class Meta:
managed = False
db_table = 'data'
indexes = [
models.Index(fields=["format"], name="formats_idx"),
models.Index(fields=["book"], name="data_idx"),
]
class Identifiers(models.Model):
book = models.IntegerField()
type = models.TextField()
val = models.TextField()
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.val
class Meta:
managed = False
db_table = 'identifiers'
class Languages(models.Model):
lang_code = models.TextField()
def get_absolute_url(self):
"""Returns the url to access a particular instance of MyModelName."""
return reverse('language-detail-view', args=[str(self.lang_code)])
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.lang_code
class Meta:
managed = False
db_table = 'languages'
indexes = [
models.Index(fields=["lang_code"], name="languages_idx"),
]
class Publishers(models.Model):
name = models.TextField()
sort = models.TextField(blank=True, null=True)
released = models.ManyToManyField(
"Books",
through='BooksPublishersLink',
through_fields=('publisher', 'book'),
related_name="released"
)
def get_absolute_url(self):
"""Returns the url to access a particular instance of MyModelName."""
return reverse('publisher-detail-view', args=[str(self.id)])
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.name
class Meta:
managed = False
db_table = 'publishers'
indexes = [
models.Index(fields=["name"], name="publishers_idx"),
]
class Ratings(models.Model):
rating = models.IntegerField(blank=True, null=True)
def get_absolute_url(self):
"""Returns the url to access a particular instance of MyModelName."""
return reverse('rating-detail-view', args=[str(self.id)])
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return str(self.rating)
class Meta:
managed = False
db_table = 'ratings'
class Series(models.Model):
name = models.TextField()
sort = models.TextField(blank=True, null=True)
def get_absolute_url(self):
"""Returns the url to access a particular instance of MyModelName."""
return reverse('series-detail-view', args=[str(self.id)])
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.name
class Meta:
managed = False
db_table = 'series'
indexes = [
models.Index(fields=["name"], name="series_idx"),
]
class Tags(models.Model):
name = models.TextField()
def get_absolute_url(self):
"""Returns the url to access a particular instance of MyModelName."""
return reverse('tag-detail-view', args=[str(self.id)])
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.name
class Meta:
managed = False
db_table = 'tags'
indexes = [
models.Index(fields=["name"], name="tags_idx"),
]
class Books(models.Model):
title = models.TextField()
sort = models.TextField(blank=True, null=True)
# This field type is a guess.
timestamp = models.TextField(blank=True, null=True)
# This field type is a guess.
pubdate = models.TextField(blank=True, null=True)
series_index = models.FloatField()
author_sort = models.TextField(blank=True, null=True)
isbn = models.TextField(blank=True, null=True)
lccn = models.TextField(blank=True, null=True)
path = models.TextField()
flags = models.IntegerField()
uuid = models.TextField(blank=True, null=True)
has_cover = models.BooleanField(blank=True, null=True)
last_modified = models.TextField() # This field type is a guess.
authors = models.ManyToManyField(
Authors,
through='BooksAuthorsLink',
through_fields=('book', 'author'))
languages = models.ManyToManyField(
Languages,
through='BooksLanguagesLink',
through_fields=('book', 'lang_code'))
publishers = models.ManyToManyField(
Publishers,
through='BooksPublishersLink',
through_fields=('book', 'publisher'))
series = models.ManyToManyField(
Series,
through='BooksSeriesLink',
through_fields=('book', 'series'))
tags = models.ManyToManyField(
Tags,
through='BooksTagsLink',
through_fields=('book', 'tag'))
ratings = models.ManyToManyField(
Ratings,
through='BooksRatingsLink',
through_fields=('book', 'rating'))
def get_absolute_url(self):
"""Returns the url to access a particular instance of MyModelName."""
return reverse('book-detail-view', args=[str(self.id)])
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.title
class Meta:
managed = False
db_table = 'books'
indexes = [
models.Index(fields=["sort"], name="books_idx"),
models.Index(fields=["author_sort"], name="authors_idx"),
]
class BooksAuthorsLink(models.Model):
book = models.ForeignKey(Books, db_column="book", on_delete=models.CASCADE)
author = models.ForeignKey(
Authors, db_column="author", on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'books_authors_link'
indexes = [
models.Index(fields=["book"], name="books_authors_link_bidx"),
models.Index(fields=["author"], name="books_authors_link_aidx"),
]
class BooksLanguagesLink(models.Model):
book = models.ForeignKey(Books, db_column="book", on_delete=models.CASCADE)
lang_code = models.ForeignKey(
Languages, db_column="lang_code", on_delete=models.CASCADE)
item_order = models.IntegerField()
class Meta:
managed = False
db_table = 'books_languages_link'
indexes = [
models.Index(fields=["book"], name="books_languages_link_bidx"),
models.Index(fields=["lang_code"],
name="books_languages_link_aidx"),
]
class BooksPublishersLink(models.Model):
book = models.ForeignKey(Books, db_column="book", on_delete=models.CASCADE)
publisher = models.ForeignKey(
Publishers, db_column="publisher", on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'books_publishers_link'
indexes = [
models.Index(fields=["book"], name="books_publishers_link_bidx"),
models.Index(fields=["publisher"],
name="books_publishers_link_aidx"),
]
class BooksRatingsLink(models.Model): # TODO add this somehow
book = models.ForeignKey(Books, db_column="book", on_delete=models.CASCADE)
rating = models.ForeignKey(
Ratings, db_column="rating", on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'books_ratings_link'
class BooksSeriesLink(models.Model):
book = models.ForeignKey(Books, db_column="book", on_delete=models.CASCADE)
series = models.ForeignKey(
Series, db_column="series", on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'books_series_link'
indexes = [
models.Index(fields=["book"], name="books_series_link_bidx"),
models.Index(fields=["series"], name="books_series_link_aidx"),
]
class BooksTagsLink(models.Model):
book = models.ForeignKey(Books, db_column="book", on_delete=models.CASCADE)
tag = models.ForeignKey(Tags, db_column="tag", on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'books_tags_link'
indexes = [
models.Index(fields=["book"], name="books_tags_link_bidx"),
models.Index(fields=["tag"], name="books_tags_link_aidx"),
]
# class BooksPluginData(models.Model):
# book = models.IntegerField()
# name = models.TextField()
# val = models.TextField()
# class Meta:
# managed = False
# db_table = 'books_plugin_data'
# class ConversionOptions(models.Model):
# format = models.TextField()
# book = models.IntegerField(blank=True, null=True)
# data = models.BinaryField()
#
# class Meta:
# managed = False
# db_table = 'conversion_options'
#
# class LibraryId(models.Model):
# uuid = models.TextField()
#
# class Meta:
# managed = False
# db_table = 'library_id'
#
# class CustomColumns(models.Model):
# label = models.TextField()
# name = models.TextField()
# datatype = models.TextField()
# mark_for_delete = models.BooleanField()
# editable = models.BooleanField()
# display = models.TextField()
# is_multiple = models.BooleanField()
# normalized = models.BooleanField()
#
# class Meta:
# managed = False
# db_table = 'custom_columns'
#
# class Preferences(models.Model):
# key = models.TextField()
# val = models.TextField()
#
# class Meta:
# managed = False
# db_table = 'preferences'
#
# class Feeds(models.Model):
# title = models.TextField()
# script = models.TextField()
#
# class Meta:
# managed = False
# db_table = 'feeds'
#
#
# class LastReadPositions(models.Model):
# book = models.IntegerField()
# format = models.TextField()
# user = models.TextField()
# device = models.TextField()
# cfi = models.TextField()
# epoch = models.FloatField()
# pos_frac = models.FloatField()
#
# class Meta:
# managed = False
# db_table = 'last_read_positions'
# class MetadataDirtied(models.Model):
# book = models.IntegerField()
# class Meta:
# managed = False
# db_table = 'metadata_dirtied'

View File

@ -0,0 +1,58 @@
/* Fixed sidenav, full height */
.sidenav {
height: 100%;
width: 200px;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #111;
overflow-x: hidden;
padding-top: 20px;
}
/* Style the sidenav links and the dropdown button */
.sidenav a, .dropdown-btn {
padding: 6px 8px 6px 16px;
text-decoration: none;
font-size: 20px;
color: #818181;
display: block;
border: none;
background: none;
width:100%;
text-align: left;
cursor: pointer;
outline: none;
}
/* On mouse-over */
.sidenav a:hover, .dropdown-btn:hover {
color: #f1f1f1;
}
/* Main content */
.main {
margin-left: 200px; /* Same as the width of the sidenav */
font-size: 20px; /* Increased text to enable scrolling */
padding: 0px 10px;
}
/* Add an active class to the active dropdown button */
.active {
background-color: green;
color: white;
}
/* Dropdown container (hidden by default). Optional: add a lighter background color and some left padding to change the design of the dropdown content */
.dropdown-container {
display: none;
background-color: #262626;
padding-left: 8px;
}
/* Optional: Style the caret down icon */
.fa-caret-down {
float: right;
padding-right: 8px;
}

View File

@ -0,0 +1,14 @@
var dropdown = document.getElementsByClassName("dropdown-btn");
var i;
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", function() {
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
});
}

View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
{% block title %}<title>Local Library</title>{% endblock %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<!-- Add additional CSS in static file -->
{% load static %}
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-sm-2">
{% block sidebar %}
<div class="sidenav">
<a href="#about">About</a>
<a href="#services">Services</a>
<a href="#clients">Clients</a>
<a href="#contact">Contact</a>
<button class="dropdown-btn">Dropdown
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-container">
<a href="#">Link 1</a>
<a href="#">Link 2</a>
<a href="#">Link 3</a>
</div>
<a href="#contact">{{counter}} books</a>
</div>
{% endblock %}
</div>
<div class="col-sm-10 ">{% block content %}{% endblock %}</div>
</div>
</div>
<script>
var dropdown = document.getElementsByClassName("dropdown-btn");
var i;
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", function() {
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
});
}
</script>
</body>
</html>

View File

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>{{authors}}</h1>
{% if books %}
<ul>
{% for book in books %}
<li><a href="{{book.get_absolute_url}}">{{book.title}}</a></li>
{%endfor%}
</ul>
{% else %}
{%endif%}
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>Author List</h1>
{% if authors_list %}
<ul>
{% for author in authors_list %}
<li>
<a href="{{ author.get_absolute_url }}">{{ author.name }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no authors in the library.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>{{books.title}} by
{% if books.authors %}
{% for author in books.authors.all %}
<a href="{{author.get_absolute_url}}">{{author.name}}</a>
{%endfor%}
{% else %}
{{books.author_sort}}
{%endif%}
Published by
{% if books.publishers %}
{% for pub in books.publishers.all %}
<a href="{{pub.get_absolute_url}}">{{pub.name}}</a>
{%endfor%}
{% else %}
Unknown
{%endif%}
Tags:
{% if books.tags %}
{% for tag in books.tags.all %}
<a href="{{tag.get_absolute_url}}">{{tag.name}}</a>,
{%endfor%}
{% else %}
{%endif%}
Rating:
{% if books.ratings %}
{% for rating in books.ratings.all %}
<a href="{{rating.get_absolute_url}}">{{rating}}</a>
{%endfor%}
{% else %}
{%endif%}
<a href="{{book.publisher.get_absolute_url}}">{{book.publisher}}</a>
</h1>
{{comment}}
<a href="{% static "" %}{{download}}"><img src="{% static "" %}{{imgpath}}" alt="" srcset=""></a>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>Book List</h1>
{% if books_list %}
<ul>
{% for book in books_list %}
<li>
<a href="{{ book.get_absolute_url }}">{{ book.title }}</a> ({{book.author_sort}})
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no books in the library.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,18 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>{{authors}} </h1>
{% if publishers.released %}
<ul>
{% for book in publishers.released.all %}
<li><a href="{{book.get_absolute_url}}">{{book.title}}</a></li>
{%endfor%}
</ul>
{% else %}
{%endif%}
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>Publishers List</h1>
{% if publishers_list %}
<ul>
{% for publisher in publishers_list %}
<li>
<a href="{{ publisher.get_absolute_url }}">{{ publisher.name }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no publishers in the library.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>{{ratings}}</h1>
{% if books %}
<ul>
{% for book in books %}
<li><a href="{{book.get_absolute_url}}">{{book.title}}</a></li>
{%endfor%}
</ul>
{% else %}
{%endif%}
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>Ratings List</h1>
{% if ratings_list %}
<ul>
{% for rating in ratings_list %}
<li>
<a href="{{ rating.get_absolute_url }}">{{ rating.rating }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no ratings in the library.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>{{tags}}</h1>
{% if books %}
<ul>
{% for book in books %}
<li><a href="{{book.get_absolute_url}}">{{book.title}}</a></li>
{%endfor%}
</ul>
{% else %}
{%endif%}
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
{% load static %}
<h1>Tags List</h1>
{% if tags_list %}
<ul>
{% for tag in tags_list %}
<li>
<a href="{{ tag.get_absolute_url }}">{{ tag.name }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no tags in the library.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1 @@
from . import custom

View File

@ -0,0 +1,9 @@
from django import template
from ..models import Books
register = template.Library
print("I ACTUALLY CAME HEReEe")
@register.simple_tag
def dummy():
return Books.objects.count()

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,20 @@
from django.urls import path
from . import views
urlpatterns = [
path('authors/', views.AuthorListView.as_view(), name='authors'),
path('books/', views.BookListView.as_view(), name='books'),
path('publishers/', views.PublisherListView.as_view(), name='publishers'),
path('ratings/', views.RatingListView.as_view(), name='ratings'),
path('tags/', views.TagListView.as_view(), name='tags'),
path('author/<int:pk>', views.AuthorDetailView.as_view(), name='author-detail-view'),
path('book/<int:pk>', views.BookDetailView.as_view(), name='book-detail-view'),
path('publisher/<int:pk>', views.PublisherDetailView.as_view(), name='publisher-detail-view'),
path('rating/<int:pk>', views.RatingDetailView.as_view(), name='rating-detail-view'),
path('tag/<int:pk>', views.TagDetailView.as_view(), name='tag-detail-view'),
]

View File

@ -0,0 +1,81 @@
from django.shortcuts import render
from django.views import generic
from .models import Authors, Books, Comments, Ratings, BooksAuthorsLink, Publishers, Tags, BooksTagsLink, BooksRatingsLink, Data
class AuthorListView(generic.ListView):
model = Authors
class BookListView(generic.ListView):
model = Books
class PublisherListView(generic.ListView):
model = Publishers
class RatingListView(generic.ListView):
model = Ratings
class TagListView(generic.ListView):
model = Tags
class AuthorDetailView(generic.DetailView):
model = Authors
def get_context_data(self, **kwargs):
# Call the base implementation first to get the context
context = super(AuthorDetailView, self).get_context_data(**kwargs)
# Create any data and add it to the context
books = BooksAuthorsLink.objects.filter(author=context["object"].id)
context['books'] = context['books'] = sorted([b.book for b in books.all()], key=lambda x: x.title)
return context
class BookDetailView(generic.DetailView):
model = Books
def get_context_data(self, **kwargs):
# Call the base implementation first to get the context
context = super(BookDetailView, self).get_context_data(**kwargs)
# Create any data and add it to the context
try:
context['comment'] = Comments.objects.get(
book=context["object"].id).text
except:
pass
context["imgpath"] = context["object"].path + "/cover.jpg"
download = Data.objects.get(book=context["object"].id)
context["download"] = f"{context['object'].path}/{download.name}.{download.format}"
return context
class PublisherDetailView(generic.DetailView):
model = Publishers
class RatingDetailView(generic.DetailView):
model = Ratings
def get_context_data(self, **kwargs):
# Call the base implementation first to get the context
context = super(RatingDetailView, self).get_context_data(**kwargs)
# Create any data and add it to the context
books = BooksRatingsLink.objects.filter(rating=context["object"].id)
context['books'] = sorted([b.book for b in books.all()], key=lambda x: x.title)
return context
class TagDetailView(generic.DetailView):
model = Tags
def get_context_data(self, **kwargs):
# Call the base implementation first to get the context
context = super(TagDetailView, self).get_context_data(**kwargs)
# Create any data and add it to the context
books = BooksTagsLink.objects.filter(tag=context["object"].id)
context['books'] = sorted([b.book for b in books.all()], key=lambda x: x.title)
return context

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'CalibreWebCompanion.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# requirements
Django 3.0
# how to use:
Edit `./CalibreWebCompanion/CalibreWebCompanion/settings`.
Set CALIBREPATH to the path of your library
`./CalibreWebCompanion`
run `./manage.py runserver`
this is in development mode. don't actually use it or release it like this. The debug info it shows is spicy.

BIN
models.vsdx Normal file

Binary file not shown.

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
django=3.0.8