commit 8ec8c686c681192dc29c06ebb102f2488b00520b Author: MassiveAtoms Date: Tue Jul 7 23:11:21 2020 -0300 reasonably working calibreweb thing diff --git a/CalibreWebCompanion/CalibreWebCompanion/__init__.py b/CalibreWebCompanion/CalibreWebCompanion/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/CalibreWebCompanion/CalibreWebCompanion/asgi.py b/CalibreWebCompanion/CalibreWebCompanion/asgi.py new file mode 100644 index 0000000..41b4f52 --- /dev/null +++ b/CalibreWebCompanion/CalibreWebCompanion/asgi.py @@ -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() diff --git a/CalibreWebCompanion/CalibreWebCompanion/settings.py b/CalibreWebCompanion/CalibreWebCompanion/settings.py new file mode 100644 index 0000000..d7fef39 --- /dev/null +++ b/CalibreWebCompanion/CalibreWebCompanion/settings.py @@ -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/' diff --git a/CalibreWebCompanion/CalibreWebCompanion/urls.py b/CalibreWebCompanion/CalibreWebCompanion/urls.py new file mode 100644 index 0000000..64829d1 --- /dev/null +++ b/CalibreWebCompanion/CalibreWebCompanion/urls.py @@ -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) \ No newline at end of file diff --git a/CalibreWebCompanion/CalibreWebCompanion/wsgi.py b/CalibreWebCompanion/CalibreWebCompanion/wsgi.py new file mode 100644 index 0000000..46b629e --- /dev/null +++ b/CalibreWebCompanion/CalibreWebCompanion/wsgi.py @@ -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() diff --git a/CalibreWebCompanion/db.sqlite3 b/CalibreWebCompanion/db.sqlite3 new file mode 100644 index 0000000..b92ca61 Binary files /dev/null and b/CalibreWebCompanion/db.sqlite3 differ diff --git a/CalibreWebCompanion/db_routers/__init__.py b/CalibreWebCompanion/db_routers/__init__.py new file mode 100644 index 0000000..ace8f6b --- /dev/null +++ b/CalibreWebCompanion/db_routers/__init__.py @@ -0,0 +1,2 @@ +from .routers import DjangoRouter, CalibreRouter +__name__ = "db_routers" \ No newline at end of file diff --git a/CalibreWebCompanion/db_routers/routers.py b/CalibreWebCompanion/db_routers/routers.py new file mode 100644 index 0000000..672dcc9 --- /dev/null +++ b/CalibreWebCompanion/db_routers/routers.py @@ -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 \ No newline at end of file diff --git a/CalibreWebCompanion/library/__init__.py b/CalibreWebCompanion/library/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/CalibreWebCompanion/library/_models.py b/CalibreWebCompanion/library/_models.py new file mode 100644 index 0000000..f9320a5 --- /dev/null +++ b/CalibreWebCompanion/library/_models.py @@ -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' diff --git a/CalibreWebCompanion/library/admin.py b/CalibreWebCompanion/library/admin.py new file mode 100644 index 0000000..69776b3 --- /dev/null +++ b/CalibreWebCompanion/library/admin.py @@ -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"]) \ No newline at end of file diff --git a/CalibreWebCompanion/library/apps.py b/CalibreWebCompanion/library/apps.py new file mode 100644 index 0000000..e01db0a --- /dev/null +++ b/CalibreWebCompanion/library/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class LibraryConfig(AppConfig): + name = 'library' diff --git a/CalibreWebCompanion/library/calibredb.py b/CalibreWebCompanion/library/calibredb.py new file mode 100644 index 0000000..a0cdb7c --- /dev/null +++ b/CalibreWebCompanion/library/calibredb.py @@ -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) + diff --git a/CalibreWebCompanion/library/migrations/0001_initial.py b/CalibreWebCompanion/library/migrations/0001_initial.py new file mode 100644 index 0000000..cd7a498 --- /dev/null +++ b/CalibreWebCompanion/library/migrations/0001_initial.py @@ -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, + }, + ), + ] diff --git a/CalibreWebCompanion/library/migrations/__init__.py b/CalibreWebCompanion/library/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/CalibreWebCompanion/library/models.py b/CalibreWebCompanion/library/models.py new file mode 100644 index 0000000..a9177c9 --- /dev/null +++ b/CalibreWebCompanion/library/models.py @@ -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' diff --git a/CalibreWebCompanion/library/static/css/styles.css b/CalibreWebCompanion/library/static/css/styles.css new file mode 100644 index 0000000..b49e6a5 --- /dev/null +++ b/CalibreWebCompanion/library/static/css/styles.css @@ -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; + } \ No newline at end of file diff --git a/CalibreWebCompanion/library/static/js/nav.js b/CalibreWebCompanion/library/static/js/nav.js new file mode 100644 index 0000000..9503bdc --- /dev/null +++ b/CalibreWebCompanion/library/static/js/nav.js @@ -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"; + } + }); +} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/base.html b/CalibreWebCompanion/library/templates/base.html new file mode 100644 index 0000000..0b47aae --- /dev/null +++ b/CalibreWebCompanion/library/templates/base.html @@ -0,0 +1,54 @@ + + + + {% block title %}Local Library{% endblock %} + + + + + {% load static %} + + + +
+
+
+ {% block sidebar %} +
+ About + Services + Clients + Contact + + + {{counter}} books +
+ {% endblock %} +
+
{% block content %}{% endblock %}
+
+
+ + + \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/authors_detail.html b/CalibreWebCompanion/library/templates/library/authors_detail.html new file mode 100644 index 0000000..d3e832e --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/authors_detail.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} +

{{authors}}

+ {% if books %} + + {% else %} + {%endif%} + + + + +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/authors_list.html b/CalibreWebCompanion/library/templates/library/authors_list.html new file mode 100644 index 0000000..2da171f --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/authors_list.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} +

Author List

+ {% if authors_list %} + + {% else %} +

There are no authors in the library.

+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/books_detail.html b/CalibreWebCompanion/library/templates/library/books_detail.html new file mode 100644 index 0000000..9265b46 --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/books_detail.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} +

{{books.title}} by + {% if books.authors %} + {% for author in books.authors.all %} + {{author.name}} + {%endfor%} + {% else %} + + {{books.author_sort}} + {%endif%} + + Published by + {% if books.publishers %} + {% for pub in books.publishers.all %} + {{pub.name}} + {%endfor%} + {% else %} + Unknown + + {%endif%} + + Tags: + {% if books.tags %} + {% for tag in books.tags.all %} + {{tag.name}}, + {%endfor%} + {% else %} + {%endif%} + + Rating: + {% if books.ratings %} + {% for rating in books.ratings.all %} + {{rating}} + {%endfor%} + {% else %} + {%endif%} + + {{book.publisher}} +

+{{comment}} + + + +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/books_list.html b/CalibreWebCompanion/library/templates/library/books_list.html new file mode 100644 index 0000000..81c97f8 --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/books_list.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} +

Book List

+ {% if books_list %} + + {% else %} +

There are no books in the library.

+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/publishers_detail.html b/CalibreWebCompanion/library/templates/library/publishers_detail.html new file mode 100644 index 0000000..834da20 --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/publishers_detail.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} +

{{authors}}

+ + {% if publishers.released %} + + {% else %} + {%endif%} + + +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/publishers_list.html b/CalibreWebCompanion/library/templates/library/publishers_list.html new file mode 100644 index 0000000..a9557be --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/publishers_list.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} +

Publishers List

+ {% if publishers_list %} + + {% else %} +

There are no publishers in the library.

+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/ratings_detail.html b/CalibreWebCompanion/library/templates/library/ratings_detail.html new file mode 100644 index 0000000..4cfcb26 --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/ratings_detail.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} + +

{{ratings}}

+ {% if books %} + + {% else %} + {%endif%} + + + + +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/ratings_list.html b/CalibreWebCompanion/library/templates/library/ratings_list.html new file mode 100644 index 0000000..b4d09f4 --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/ratings_list.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} +

Ratings List

+ {% if ratings_list %} + + {% else %} +

There are no ratings in the library.

+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/tags_detail.html b/CalibreWebCompanion/library/templates/library/tags_detail.html new file mode 100644 index 0000000..8b8edbc --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/tags_detail.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} + +

{{tags}}

+ {% if books %} + + {% else %} + {%endif%} + + + + +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templates/library/tags_list.html b/CalibreWebCompanion/library/templates/library/tags_list.html new file mode 100644 index 0000000..7d26ee9 --- /dev/null +++ b/CalibreWebCompanion/library/templates/library/tags_list.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} +{% load static %} +

Tags List

+ {% if tags_list %} + + {% else %} +

There are no tags in the library.

+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/CalibreWebCompanion/library/templatetags/__init__.py b/CalibreWebCompanion/library/templatetags/__init__.py new file mode 100644 index 0000000..3eaef43 --- /dev/null +++ b/CalibreWebCompanion/library/templatetags/__init__.py @@ -0,0 +1 @@ +from . import custom \ No newline at end of file diff --git a/CalibreWebCompanion/library/templatetags/custom.py b/CalibreWebCompanion/library/templatetags/custom.py new file mode 100644 index 0000000..3662f74 --- /dev/null +++ b/CalibreWebCompanion/library/templatetags/custom.py @@ -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() \ No newline at end of file diff --git a/CalibreWebCompanion/library/tests.py b/CalibreWebCompanion/library/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/CalibreWebCompanion/library/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/CalibreWebCompanion/library/urls.py b/CalibreWebCompanion/library/urls.py new file mode 100644 index 0000000..8e03baf --- /dev/null +++ b/CalibreWebCompanion/library/urls.py @@ -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/', views.AuthorDetailView.as_view(), name='author-detail-view'), + path('book/', views.BookDetailView.as_view(), name='book-detail-view'), + path('publisher/', views.PublisherDetailView.as_view(), name='publisher-detail-view'), + path('rating/', views.RatingDetailView.as_view(), name='rating-detail-view'), + path('tag/', views.TagDetailView.as_view(), name='tag-detail-view'), + + +] \ No newline at end of file diff --git a/CalibreWebCompanion/library/views.py b/CalibreWebCompanion/library/views.py new file mode 100644 index 0000000..d4d724e --- /dev/null +++ b/CalibreWebCompanion/library/views.py @@ -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 diff --git a/CalibreWebCompanion/manage.py b/CalibreWebCompanion/manage.py new file mode 100644 index 0000000..bd30ba8 --- /dev/null +++ b/CalibreWebCompanion/manage.py @@ -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() diff --git a/README.md b/README.md new file mode 100644 index 0000000..9fc27c8 --- /dev/null +++ b/README.md @@ -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. \ No newline at end of file diff --git a/models.vsdx b/models.vsdx new file mode 100644 index 0000000..8ccda43 Binary files /dev/null and b/models.vsdx differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e9b1726 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +django=3.0.8 \ No newline at end of file