Compare commits
8 Commits
c00b2b0e52
...
cfe706b1f5
Author | SHA1 | Date | |
---|---|---|---|
|
cfe706b1f5 | ||
|
5d522da15e | ||
ee7a0bfc32 | |||
17be21ec00 | |||
|
76dd31c80c | ||
fce13c85e4 | |||
1c616d27d9 | |||
|
58d4f27c61 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -78,11 +78,11 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
"library",
|
"library",
|
||||||
# 'debug_toolbar', # DEBUG purposes
|
'debug_toolbar', # DEBUG purposes
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
# 'debug_toolbar.middleware.DebugToolbarMiddleware', # DEBUG purposes
|
'debug_toolbar.middleware.DebugToolbarMiddleware', # DEBUG purposes
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
@ -30,8 +30,8 @@ urlpatterns = [
|
|||||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|
||||||
|
|
||||||
# if settings.DEBUG: # DEBUG purposes
|
if settings.DEBUG: # DEBUG purposes
|
||||||
# import debug_toolbar
|
import debug_toolbar
|
||||||
# urlpatterns = [
|
urlpatterns = [
|
||||||
# path('__debug__/', include(debug_toolbar.urls)),
|
path('__debug__/', include(debug_toolbar.urls)),
|
||||||
# ] + urlpatterns
|
] + urlpatterns
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
CalibreWebCompanion/library/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
CalibreWebCompanion/library/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
CalibreWebCompanion/library/__pycache__/admin.cpython-38.pyc
Normal file
BIN
CalibreWebCompanion/library/__pycache__/admin.cpython-38.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
CalibreWebCompanion/library/__pycache__/forms.cpython-38.pyc
Normal file
BIN
CalibreWebCompanion/library/__pycache__/forms.cpython-38.pyc
Normal file
Binary file not shown.
BIN
CalibreWebCompanion/library/__pycache__/models.cpython-38.pyc
Normal file
BIN
CalibreWebCompanion/library/__pycache__/models.cpython-38.pyc
Normal file
Binary file not shown.
BIN
CalibreWebCompanion/library/__pycache__/urls.cpython-38.pyc
Normal file
BIN
CalibreWebCompanion/library/__pycache__/urls.cpython-38.pyc
Normal file
Binary file not shown.
BIN
CalibreWebCompanion/library/__pycache__/views.cpython-38.pyc
Normal file
BIN
CalibreWebCompanion/library/__pycache__/views.cpython-38.pyc
Normal file
Binary file not shown.
107
CalibreWebCompanion/library/calibredb.py
Normal file
107
CalibreWebCompanion/library/calibredb.py
Normal 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)
|
||||||
|
|
@ -1,13 +1,22 @@
|
|||||||
from .models import Author, Tag, Publisher, Language, Rating, Series
|
from .models import Author, Tag, Publisher, Language, Rating, Series
|
||||||
|
from django.db.models import Count
|
||||||
|
|
||||||
|
|
||||||
def filters(request):
|
def filters(request):
|
||||||
unique_authors = Author.objects.all().order_by('sort')
|
# unique_authors = Author.objects.all().order_by('sort')
|
||||||
unique_tags = Tag.objects.all().order_by('name')
|
# unique_tags = Tag.objects.all().order_by('name')
|
||||||
unique_publishers = Publisher.objects.all().order_by('name')
|
# unique_publishers = Publisher.objects.all().order_by('name')
|
||||||
unique_languages = Language.objects.all()
|
# unique_ratings = Rating.objects.all().order_by('rating')
|
||||||
unique_ratings = Rating.objects.all().order_by('rating')
|
# unique_languages = Language.objects.all()
|
||||||
unique_series = Series.objects.all().order_by('sort')
|
# unique_series = Series.objects.all().order_by('sort')
|
||||||
|
# unique_authors = Author.objects.annotate(num_books=Count('book')).order_by('sort')
|
||||||
|
unique_authors = Author.objects.only('name', "id").annotate(num_books=Count('book')).order_by('name')
|
||||||
|
unique_tags = Tag.objects.annotate(num_books=Count('book')).order_by('name')
|
||||||
|
unique_publishers = Publisher.objects.annotate(num_books=Count('book')).order_by('name')
|
||||||
|
unique_languages = Language.objects.annotate(num_books=Count('book')).order_by('rating')
|
||||||
|
unique_ratings = Rating.objects.annotate(num_books=Count('book'))
|
||||||
|
unique_series = Series.objects.annotate(num_books=Count('book')).order_by('sort')
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"unique_authors": unique_authors,
|
"unique_authors": unique_authors,
|
||||||
|
@ -0,0 +1,221 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-07-11 16:22
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('library', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Author',
|
||||||
|
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='Book',
|
||||||
|
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='BookAuthorLink',
|
||||||
|
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='BookLanguageLink',
|
||||||
|
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='BookPublisherLink',
|
||||||
|
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='BookRatingLink',
|
||||||
|
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='BookSeriesLink',
|
||||||
|
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='BookTagLink',
|
||||||
|
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='Comment',
|
||||||
|
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='Identifier',
|
||||||
|
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='Language',
|
||||||
|
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='Publisher',
|
||||||
|
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='Rating',
|
||||||
|
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='Tag',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.TextField()),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'tags',
|
||||||
|
'managed': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Authors',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Books',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='BooksAuthorsLink',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='BooksLanguagesLink',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='BooksPublishersLink',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='BooksRatingsLink',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='BooksSeriesLink',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='BooksTagsLink',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Comments',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Identifiers',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Languages',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Publishers',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Ratings',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Tags',
|
||||||
|
),
|
||||||
|
]
|
Binary file not shown.
Binary file not shown.
@ -17,7 +17,7 @@ class Author(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Returns the url to access a particular instance of MyModelName."""
|
"""Returns the url to access a particular instance of MyModelName."""
|
||||||
return reverse('author-detail-view', args=[str(self.id)])
|
return reverse('author-detail-view', args=[str(self.id)])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String for representing the MyModelName object (in Admin site etc.)."""
|
"""String for representing the MyModelName object (in Admin site etc.)."""
|
||||||
return self.name
|
return self.name
|
||||||
@ -27,12 +27,11 @@ class Author(models.Model):
|
|||||||
db_table = 'authors'
|
db_table = 'authors'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Comment(models.Model):
|
class Comment(models.Model):
|
||||||
book = models.ForeignKey("Book", db_column="book", on_delete=models.CASCADE)
|
book = models.ForeignKey("Book", db_column="book",
|
||||||
|
on_delete=models.CASCADE)
|
||||||
text = models.TextField()
|
text = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
managed = False
|
managed = False
|
||||||
db_table = 'comments'
|
db_table = 'comments'
|
||||||
@ -41,7 +40,6 @@ class Comment(models.Model):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Data(models.Model):
|
class Data(models.Model):
|
||||||
book = models.IntegerField()
|
book = models.IntegerField()
|
||||||
format = models.TextField()
|
format = models.TextField()
|
||||||
@ -61,7 +59,7 @@ class Identifier(models.Model):
|
|||||||
book = models.IntegerField()
|
book = models.IntegerField()
|
||||||
type = models.TextField()
|
type = models.TextField()
|
||||||
val = models.TextField()
|
val = models.TextField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String for representing the MyModelName object (in Admin site etc.)."""
|
"""String for representing the MyModelName object (in Admin site etc.)."""
|
||||||
return self.val
|
return self.val
|
||||||
@ -77,7 +75,7 @@ class Language(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Returns the url to access a particular instance of MyModelName."""
|
"""Returns the url to access a particular instance of MyModelName."""
|
||||||
return reverse('language-detail-view', args=[str(self.lang_code)])
|
return reverse('language-detail-view', args=[str(self.lang_code)])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String for representing the MyModelName object (in Admin site etc.)."""
|
"""String for representing the MyModelName object (in Admin site etc.)."""
|
||||||
return self.lang_code
|
return self.lang_code
|
||||||
@ -98,12 +96,12 @@ class Publisher(models.Model):
|
|||||||
through='BookPublisherLink',
|
through='BookPublisherLink',
|
||||||
through_fields=('publisher', 'book'),
|
through_fields=('publisher', 'book'),
|
||||||
related_name="released"
|
related_name="released"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Returns the url to access a particular instance of MyModelName."""
|
"""Returns the url to access a particular instance of MyModelName."""
|
||||||
return reverse('publisher-detail-view', args=[str(self.id)])
|
return reverse('publisher-detail-view', args=[str(self.id)])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String for representing the MyModelName object (in Admin site etc.)."""
|
"""String for representing the MyModelName object (in Admin site etc.)."""
|
||||||
return self.name
|
return self.name
|
||||||
@ -118,10 +116,11 @@ class Publisher(models.Model):
|
|||||||
|
|
||||||
class Rating(models.Model):
|
class Rating(models.Model):
|
||||||
rating = models.IntegerField(blank=True, null=True)
|
rating = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Returns the url to access a particular instance of MyModelName."""
|
"""Returns the url to access a particular instance of MyModelName."""
|
||||||
return reverse('rating-detail-view', args=[str(self.id)])
|
return reverse('rating-detail-view', args=[str(self.id)])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String for representing the MyModelName object (in Admin site etc.)."""
|
"""String for representing the MyModelName object (in Admin site etc.)."""
|
||||||
return str(self.rating)
|
return str(self.rating)
|
||||||
@ -138,7 +137,7 @@ class Series(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Returns the url to access a particular instance of MyModelName."""
|
"""Returns the url to access a particular instance of MyModelName."""
|
||||||
return reverse('series-detail-view', args=[str(self.id)])
|
return reverse('series-detail-view', args=[str(self.id)])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String for representing the MyModelName object (in Admin site etc.)."""
|
"""String for representing the MyModelName object (in Admin site etc.)."""
|
||||||
return self.name
|
return self.name
|
||||||
@ -153,10 +152,11 @@ class Series(models.Model):
|
|||||||
|
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
name = models.TextField()
|
name = models.TextField()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Returns the url to access a particular instance of MyModelName."""
|
"""Returns the url to access a particular instance of MyModelName."""
|
||||||
return reverse('tag-detail-view', args=[str(self.id)])
|
return reverse('tag-detail-view', args=[str(self.id)])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String for representing the MyModelName object (in Admin site etc.)."""
|
"""String for representing the MyModelName object (in Admin site etc.)."""
|
||||||
return self.name
|
return self.name
|
||||||
@ -193,14 +193,28 @@ class Book(models.Model):
|
|||||||
Language,
|
Language,
|
||||||
through='BookLanguageLink',
|
through='BookLanguageLink',
|
||||||
through_fields=('book', 'lang_code'))
|
through_fields=('book', 'lang_code'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def language(self):
|
||||||
|
return self.languages.first()
|
||||||
|
|
||||||
publishers = models.ManyToManyField(
|
publishers = models.ManyToManyField(
|
||||||
Publisher,
|
Publisher,
|
||||||
through='BookPublisherLink',
|
through='BookPublisherLink',
|
||||||
through_fields=('book', 'publisher'))
|
through_fields=('book', 'publisher'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def publisher(self):
|
||||||
|
return self.publishers.first()
|
||||||
|
|
||||||
series = models.ManyToManyField(
|
series = models.ManyToManyField(
|
||||||
Series,
|
Series,
|
||||||
through='BookSeriesLink',
|
through='BookSeriesLink',
|
||||||
through_fields=('book', 'series'))
|
through_fields=('book', 'series'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def serie(self):
|
||||||
|
return self.series.first()
|
||||||
tags = models.ManyToManyField(
|
tags = models.ManyToManyField(
|
||||||
Tag,
|
Tag,
|
||||||
through='BookTagLink',
|
through='BookTagLink',
|
||||||
@ -209,10 +223,15 @@ class Book(models.Model):
|
|||||||
Rating,
|
Rating,
|
||||||
through='BookRatingLink',
|
through='BookRatingLink',
|
||||||
through_fields=('book', 'rating'))
|
through_fields=('book', 'rating'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rating(self):
|
||||||
|
return self.rating.first()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Returns the url to access a particular instance of MyModelName."""
|
"""Returns the url to access a particular instance of MyModelName."""
|
||||||
return reverse('book-detail-view', args=[str(self.id)])
|
return reverse('book-detail-view', args=[str(self.id)])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""String for representing the MyModelName object (in Admin site etc.)."""
|
"""String for representing the MyModelName object (in Admin site etc.)."""
|
||||||
return self.title
|
return self.title
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
/* sidenav */
|
|
||||||
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ul.topnav {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topnav li {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topnav li a {
|
|
||||||
display: block;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
padding: 14px 16px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topnav li a:hover {
|
|
||||||
background-color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Table */
|
|
||||||
table {
|
|
||||||
border-spacing: 0;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
text-align: left;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(even) {
|
|
||||||
background-color: #f2f2f2
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
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";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@ -3,99 +3,126 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% block title %}<title>Local Library</title>{% endblock %}
|
{% block title %}<title>Local Library</title>{% endblock %}
|
||||||
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<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">
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
|
||||||
<!-- Add additional CSS in static file -->
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
{% load static %}
|
<!-- Compiled and minified CSS -->
|
||||||
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
|
||||||
|
<!-- Compiled and minified JavaScript -->
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating {
|
||||||
|
width: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
width: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.added {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{% block topnav%}
|
|
||||||
<ul class="topnav">
|
|
||||||
|
|
||||||
{% if user.is_authenticated %}
|
<div class="navbar-fixed">
|
||||||
<li class="active"><a href="{{user.get_absolute_url}}"> {{ user.get_username }}</a></li>
|
<nav>
|
||||||
<li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>
|
<div class="nav-wrapper row green darken-1">
|
||||||
{% else %}
|
|
||||||
<li><a href="{% url 'sign-up'%}?next={{request.path}}">Sign up</a></li>
|
|
||||||
<li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% if user.is_authenticated %}
|
<ul class="right">
|
||||||
<div class="container-fluid">
|
{% if user.is_authenticated %}
|
||||||
<div class="row">
|
<li class="active"><a href="{{user.get_absolute_url}}"> {{ user.get_username }}</a></li>
|
||||||
<div class="col-sm-2">
|
<li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>
|
||||||
{% block sidebar %}
|
</ul>
|
||||||
<div class="sidenav">
|
|
||||||
<a href="{% url 'search' %}">Search</a>
|
|
||||||
<a href="{% url 'books' %}">Books</a>
|
|
||||||
<button class="dropdown-btn">Authors
|
|
||||||
<i class="fa fa-caret-down"></i>
|
|
||||||
</button>
|
|
||||||
<div class="dropdown-container">
|
|
||||||
{% for author in unique_authors %}
|
|
||||||
<a href="{{author.get_absolute_url}}">{{author}}</a>
|
|
||||||
|
|
||||||
{% endfor %}
|
<ul class="left">
|
||||||
</div>
|
<li><a href="{% url 'search' %}">Search</a></li>
|
||||||
|
<li><a href="{% url 'books' %}">Books</a></li>
|
||||||
|
<li><a class="dropdown-trigger" href="#!" data-target="dropdown-authors">Authors<i
|
||||||
|
class="material-icons right">arrow_drop_down</i></a></li>
|
||||||
|
<li><a class="dropdown-trigger" href="#!" data-target="dropdown-ratings">Ratings<i
|
||||||
|
class="material-icons right">arrow_drop_down</i></a></li>
|
||||||
|
<li><a class="dropdown-trigger" href="#!" data-target="dropdown-tags">Tags<i
|
||||||
|
class="material-icons right">arrow_drop_down</i></a></li>
|
||||||
|
</ul>
|
||||||
|
<ul id="dropdown-authors" class="dropdown-content">
|
||||||
|
{% for author in unique_authors %}
|
||||||
|
<li><a href="{{author.get_absolute_url}}">{{author}}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<ul id="dropdown-ratings" class="dropdown-content">
|
||||||
|
{% for rating in unique_ratings %}
|
||||||
|
<li><a href="{{rating.get_absolute_url}}">{{rating}}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<ul id="dropdown-tags" class="dropdown-content">
|
||||||
|
{% for tag in unique_tags %}
|
||||||
|
<li><a href="{{tag.get_absolute_url}}">{{tag}}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
<button class="dropdown-btn">Publishers
|
{% else %}
|
||||||
<i class="fa fa-caret-down"></i>
|
<li><a href="{% url 'sign-up'%}?next={{request.path}}">Sign up</a></li>
|
||||||
</button>
|
<li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>
|
||||||
<div class="dropdown-container">
|
</ul>
|
||||||
{% for publisher in unique_publishers %}
|
{% endif %}
|
||||||
<a href="{{publisher.get_absolute_url}}">{{publisher}}</a>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="dropdown-btn">Ratings
|
|
||||||
<i class="fa fa-caret-down"></i>
|
|
||||||
</button>
|
|
||||||
<div class="dropdown-container">
|
|
||||||
{% for rating in unique_ratings %}
|
|
||||||
<a href="{{rating.get_absolute_url}}">{{rating}}</a>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<button class="dropdown-btn">Tags
|
|
||||||
<i class="fa fa-caret-down"></i>
|
|
||||||
</button>
|
|
||||||
<div class="dropdown-container">
|
|
||||||
{% for tag in unique_tags %}
|
|
||||||
<a href="{{tag.get_absolute_url}}">{{tag}}</a>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-10 ">{% block content %}{% endblock %}</div>
|
</nav>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var dropdown = document.getElementsByClassName("dropdown-btn");
|
$(".dropdown-trigger").dropdown({
|
||||||
var i;
|
hover: true,
|
||||||
|
constrainWidth: false,
|
||||||
for (i = 0; i < dropdown.length; i++) {
|
coverTrigger: false
|
||||||
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>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
{% block content %} {% endblock %}
|
||||||
|
{% else %}
|
||||||
|
<div class="valign-wrapper" style="width:100%;height:100%;position: absolute;">
|
||||||
|
<div class="valign" style="width:100%;">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m6 offset-m3">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content center">
|
||||||
|
<p>You don't have permission to view this.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-action center">
|
||||||
|
<a class="waves-effect waves-light btn-large green accent-4" href="{% url 'login'%}?next={{request.path}}">Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function sortTable(n) {
|
function sortTable(n) {
|
||||||
var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
||||||
@ -153,17 +180,6 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<h1>You don't have permission to view this.</h1>
|
|
||||||
<li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>
|
|
||||||
or
|
|
||||||
<li><a href="{% url 'sign-up'%}?next={{request.path}}">Sign up</a></li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -1,6 +1,22 @@
|
|||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<h1>You don't have permission to view this.</h1>
|
<div class="valign-wrapper" style="width:100%;height:100%;position: absolute;">
|
||||||
<li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>
|
<div class="valign" style="width:100%;">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m6 offset-m3">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content center">
|
||||||
|
<p>You don't have permission to view this.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-action center">
|
||||||
|
<a class="waves-effect waves-light btn-large green accent-4" href="{% url 'login'%}?next={{request.path}}">Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
@ -1,17 +1,17 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<h1>{{author}}</h1>
|
<h1>{{author}}</h1>
|
||||||
|
|
||||||
<table id="books">
|
<table id="books" class="highlight centered">
|
||||||
<tr>
|
<tr>
|
||||||
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
||||||
<th onclick="sortTable(0)">Title</th>
|
<th class="title" onclick="sortTable(0)">Title</th>
|
||||||
<th onclick="sortTable(1)">Author</th>
|
<th class="author" onclick="sortTable(1)">Author</th>
|
||||||
<th onclick="sortTable(2)">Rating</th>
|
<th class="rating" onclick="sortTable(2)">Rating</th>
|
||||||
<th onclick="sortTable(3)">Tags</th>
|
<th class="tags" onclick="sortTable(3)">Tags</th>
|
||||||
<th onclick="sortTable(4)">Added</th>
|
<th class="added" onclick="sortTable(4)">Added</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for book in books %}
|
{% for book in books %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<h1>Author List</h1>
|
<h1>Author List</h1>
|
||||||
{% if author_list %}
|
{% if author_list %}
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -2,49 +2,60 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<h1>{{book.title}} by
|
|
||||||
{% if book.authors %}
|
|
||||||
{% for author in book.authors.all %}
|
|
||||||
<a href="{{author.get_absolute_url}}">{{author.name}}</a>
|
|
||||||
{%endfor%}
|
|
||||||
{% else %}
|
|
||||||
|
|
||||||
{{book.author_sort}}
|
<div class="col s12 m7">
|
||||||
{%endif%}
|
<div class="card z-depth-0 horizontal">
|
||||||
|
<div class="card-image">
|
||||||
Published by
|
<a style="padding-top:15%" href="{{download}}"><img src=" {% static "" %}{{imgpath}}" alt="download" srcset=""></a>
|
||||||
{% if book.publishers %}
|
</div>
|
||||||
{% for pub in book.publishers.all %}
|
<div class="card-stacked">
|
||||||
<a href="{{pub.get_absolute_url}}">{{pub.name}}</a>
|
<div class="card-content">
|
||||||
{%endfor%}
|
<h1> {{book.title}}</h1>
|
||||||
{% else %}
|
<h4> by
|
||||||
Unknown
|
{% if book.authors %}
|
||||||
|
{% for author in book.authors.all %}
|
||||||
{%endif%}
|
<a href="{{author.get_absolute_url}}">{{author.name}}</a>
|
||||||
|
{%endfor%}
|
||||||
Tags:
|
{% else %}
|
||||||
{% if book.tags %}
|
{{book.author_sort}}
|
||||||
{% for tag in book.tags.all %}
|
{%endif%}
|
||||||
<a href="{{tag.get_absolute_url}}">{{tag.name}}</a>,
|
<br>
|
||||||
{%endfor%}
|
Published by
|
||||||
{% else %}
|
{% if book.publishers %}
|
||||||
{%endif%}
|
{% for pub in book.publishers.all %}
|
||||||
|
<a href="{{pub.get_absolute_url}}">{{pub.name}}</a>
|
||||||
Rating:
|
{%endfor%}
|
||||||
{% if book.ratings %}
|
{% else %}
|
||||||
{% for rating in book.ratings.all %}
|
Unknown
|
||||||
<a href="{{rating.get_absolute_url}}">{{rating}}</a>
|
{%endif%}
|
||||||
{%endfor%}
|
<br>
|
||||||
{% else %}
|
Tags:
|
||||||
{%endif%}
|
{% if book.tags %}
|
||||||
|
{% for tag in book.tags.all %}
|
||||||
<a href="{{book.publisher.get_absolute_url}}">{{book.publisher}}</a>
|
<a href="{{tag.get_absolute_url}}">{{tag.name}}</a>,
|
||||||
</h1>
|
{%endfor%}
|
||||||
|
{% else %}
|
||||||
<a href="{% static "" %}{{download}}"><img src="{% static "" %}{{imgpath}}" alt="download" srcset=""></a>
|
{%endif%}
|
||||||
|
<br>
|
||||||
|
Rating:
|
||||||
|
{% if book.ratings %}
|
||||||
|
{% for rating in book.ratings.all %}
|
||||||
|
<a href="{{rating.get_absolute_url}}">{{rating}}</a>
|
||||||
|
{%endfor%}
|
||||||
|
{% else %}
|
||||||
|
{%endif%}
|
||||||
|
<br>
|
||||||
|
<a href="{{book.publisher.get_absolute_url}}">{{book.publisher}}</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{{comment}}
|
{{comment}}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,17 +1,21 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<h1>Book List</h1>
|
|
||||||
|
|
||||||
<table id="books">
|
<h1 class="center">Book List</h1>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s1 m0">
|
||||||
|
</div>
|
||||||
|
<div class="col s10 m12">
|
||||||
|
<table id="books" class="highlight centered">
|
||||||
<tr>
|
<tr>
|
||||||
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
||||||
<th onclick="sortTable(0)">Title</th>
|
<th class="title" onclick="sortTable(0)">Title</th>
|
||||||
<th onclick="sortTable(1)">Author</th>
|
<th class="author" onclick="sortTable(1)">Author</th>
|
||||||
<th onclick="sortTable(2)">Rating</th>
|
<th class="rating" onclick="sortTable(2)">Rating</th>
|
||||||
<th onclick="sortTable(3)">Tags</th>
|
<th class="tags" onclick="sortTable(3)">Tags</th>
|
||||||
<th onclick="sortTable(4)">Added</th>
|
<th class="added" onclick="sortTable(4)">Added</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for book in book_list %}
|
{% for book in book_list %}
|
||||||
<tr>
|
<tr>
|
||||||
@ -30,11 +34,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
<div class="col s1 m0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,18 +1,18 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<h1>{{publisher}} </h1>
|
<h1>{{publisher}} </h1>
|
||||||
|
|
||||||
{% if publisher.released %}
|
{% if publisher.released %}
|
||||||
<table id="books">
|
<table id="books" class="highlight centered">
|
||||||
<tr>
|
<tr>
|
||||||
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
||||||
<th onclick="sortTable(0)">Title</th>
|
<th class="title" onclick="sortTable(0)">Title</th>
|
||||||
<th onclick="sortTable(1)">Author</th>
|
<th class="author" onclick="sortTable(1)">Author</th>
|
||||||
<th onclick="sortTable(2)">Rating</th>
|
<th class="rating" onclick="sortTable(2)">Rating</th>
|
||||||
<th onclick="sortTable(3)">Tags</th>
|
<th class="tags" onclick="sortTable(3)">Tags</th>
|
||||||
<th onclick="sortTable(4)">Added</th>
|
<th class="added" onclick="sortTable(4)">Added</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for book in books %}
|
{% for book in books %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<h1>Publishers List</h1>
|
<h1>Publishers List</h1>
|
||||||
{% if publisher_list %}
|
{% if publisher_list %}
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
<h1>{{rating}}</h1>
|
<h1 class="left">Books with rating: {{rating}}</h1>
|
||||||
{% if books %}
|
{% if books %}
|
||||||
<table id="books">
|
<table id="books" class="highlight centered">
|
||||||
<tr>
|
<tr>
|
||||||
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
||||||
<th onclick="sortTable(0)">Title</th>
|
<th class="title" onclick="sortTable(0)">Title</th>
|
||||||
<th onclick="sortTable(1)">Author</th>
|
<th class="author" onclick="sortTable(1)">Author</th>
|
||||||
<th onclick="sortTable(2)">Rating</th>
|
<th class="rating" onclick="sortTable(2)">Rating</th>
|
||||||
<th onclick="sortTable(3)">Tags</th>
|
<th class="tags" onclick="sortTable(3)">Tags</th>
|
||||||
<th onclick="sortTable(4)">Added</th>
|
<th class="added" onclick="sortTable(4)">Added</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for book in books %}
|
{% for book in books %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<h1>Ratings List</h1>
|
<h1>Ratings List</h1>
|
||||||
{% if rating_list %}
|
{% if rating_list %}
|
||||||
<ul>
|
<ul>
|
||||||
|
40
CalibreWebCompanion/library/templates/library/results.html
Normal file
40
CalibreWebCompanion/library/templates/library/results.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% load static %}
|
||||||
|
<h1>Results</h1>
|
||||||
|
|
||||||
|
<table id="books">
|
||||||
|
<tr>
|
||||||
|
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
||||||
|
<th onclick="sortTable(0)">Title</th>
|
||||||
|
<th onclick="sortTable(1)">Author</th>
|
||||||
|
<th onclick="sortTable(2)">Rating</th>
|
||||||
|
<th onclick="sortTable(3)">Tags</th>
|
||||||
|
<th onclick="sortTable(4)">Added</th>
|
||||||
|
</tr>
|
||||||
|
{% for book in book_list %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{{ book.get_absolute_url }}">{{ book.title }}</a></td>
|
||||||
|
<td>{{book.author_sort}}</td>
|
||||||
|
<td> {% for rating in book.ratings.all %}
|
||||||
|
{{rating}}
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% for tag in book.tags.all %}
|
||||||
|
{{tag}},
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
<td>{{book.timestamp}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -1,18 +1,17 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
<h1>{{tag}}</h1>
|
<h1>{{tag}}</h1>
|
||||||
{% if books %}
|
{% if books %}
|
||||||
<table id="books">
|
<table id="books" class="highlight centered">
|
||||||
<tr>
|
<tr>
|
||||||
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
||||||
<th onclick="sortTable(0)">Title</th>
|
<th class="title" onclick="sortTable(0)">Title</th>
|
||||||
<th onclick="sortTable(1)">Author</th>
|
<th class="author" onclick="sortTable(1)">Author</th>
|
||||||
<th onclick="sortTable(2)">Rating</th>
|
<th class="rating" onclick="sortTable(2)">Rating</th>
|
||||||
<th onclick="sortTable(3)">Tags</th>
|
<th class="tags" onclick="sortTable(3)">Tags</th>
|
||||||
<th onclick="sortTable(4)">Added</th>
|
<th class="added" onclick="sortTable(4)">Added</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for book in books %}
|
{% for book in books %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<h1>Tags List</h1>
|
<h1>Tags List</h1>
|
||||||
{% if tag_list %}
|
{% if tag_list %}
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -2,19 +2,6 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if form.errors %}
|
|
||||||
<p>Your username and password didn't match. Please try again.</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if next %}
|
|
||||||
{% if user.is_authenticated %}
|
|
||||||
<p>Your account doesn't have access to this page. To proceed,
|
|
||||||
please login with an account that has access.</p>
|
|
||||||
{% else %}
|
|
||||||
<p>Please login to see this page.</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form method="post" action="{% url 'login' %}">
|
<form method="post" action="{% url 'login' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<table>
|
<table>
|
||||||
@ -27,7 +14,23 @@
|
|||||||
<td>{{ form.password }}</td>
|
<td>{{ form.password }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<input type="submit" value="login" />
|
|
||||||
|
<div style="color:#f44336">
|
||||||
|
{% if form.errors %}
|
||||||
|
<p>Your username and password didn't match. Please try again.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if next %}
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<p>Your account doesn't have access to this page. To proceed,
|
||||||
|
please login with an account that has access.</p>
|
||||||
|
{% else %}
|
||||||
|
<p>Please login to see this page.</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="waves-effect waves-light btn green accent-4" type="submit">login</button>
|
||||||
<input type="hidden" name="next" value="{{ next }}" />
|
<input type="hidden" name="next" value="{{ next }}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %} {{form.as_p}} <input type="submit" value="Sign up"></form>
|
{% csrf_token %} {{form.as_p}} <button class="waves-effect waves-light btn green accent-4" type="submit">sign up</button></form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<h1>Results</h1>
|
<h1>Results</h1>
|
||||||
|
|
||||||
<table id="books">
|
<table id="books">
|
||||||
<tr>
|
<tr>
|
||||||
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
<!--When a header is clicked, run the sortTable function, with a parameter, 0 for sorting by names, 1 for sorting by country:-->
|
||||||
<th onclick="sortTable(0)">Title</th>
|
<th class="title" onclick="sortTable(0)">Title</th>
|
||||||
<th onclick="sortTable(1)">Author</th>
|
<th class="author" onclick="sortTable(1)">Author</th>
|
||||||
<th onclick="sortTable(2)">Rating</th>
|
<th class="rating" onclick="sortTable(2)">Rating</th>
|
||||||
<th onclick="sortTable(3)">Tags</th>
|
<th class="tags" onclick="sortTable(3)">Tags</th>
|
||||||
<th onclick="sortTable(4)">Added</th>
|
<th class="added" onclick="sortTable(4)">Added</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for book in book_list %}
|
{% for book in book_list %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
|
||||||
<form action="{% url 'results' %}" method="get">
|
<div class="container">
|
||||||
|
<form action="{% url 'results' %}" method="get" style="padding-top:2em">
|
||||||
<label for="title">Title: </label>
|
<label for="title">Title: </label>
|
||||||
<input id="title" type="text" name="title" value="">
|
<input id="title" type="text" name="title" value="">
|
||||||
<label for="author">Author: </label>
|
<label for="author">Author: </label>
|
||||||
<input id="author" type="text" name="author" value="">
|
<input id="author" type="text" name="author" value="">
|
||||||
<input type="submit" value="search">
|
<button class="waves-effect waves-light btn green accent-4" type="submit">search</button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -5,36 +5,39 @@
|
|||||||
{% block title %}<title>Local Library</title>{% endblock %}
|
{% block title %}<title>Local Library</title>{% endblock %}
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<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">
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
|
||||||
<!-- Add additional CSS in static file -->
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
{% load static %}
|
<!-- Compiled and minified CSS -->
|
||||||
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
|
||||||
|
<!-- Compiled and minified JavaScript -->
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{% block topnav%}
|
|
||||||
<ul class="topnav">
|
|
||||||
|
|
||||||
{% if user.is_authenticated %}
|
<div class="navbar-fixed">
|
||||||
<li class="active"><a href="{{user.get_absolute_url}}"></a> {{ user.get_username }}</a></li>
|
<nav>
|
||||||
<li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>
|
<div class="nav-wrapper row green darken-1">
|
||||||
{% else %}
|
|
||||||
<li><a href="{% url 'sign-up'%}?next={{request.path}}">Sign up</a></li>
|
|
||||||
<li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
<ul class="right">
|
||||||
|
{% if user.is_authenticated %}
|
||||||
<div class="container-fluid">
|
<li class="active"><a href="{{user.get_absolute_url}}"> {{ user.get_username }}</a></li>
|
||||||
<div class="row">
|
<li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>
|
||||||
|
</ul>
|
||||||
<div class="col-sm-10 ">{% block content %}{% endblock %}</div>
|
{% else %}
|
||||||
|
<li><a href="{% url 'sign-up'%}?next={{request.path}}">Sign up</a></li>
|
||||||
|
<li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s10 ">{% block content %}{% endblock %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
14
README.md
14
README.md
@ -7,4 +7,16 @@ Set CALIBREPATH to the path of your library
|
|||||||
`./CalibreWebCompanion`
|
`./CalibreWebCompanion`
|
||||||
run `./manage.py runserver`
|
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.
|
this is in development mode. don't actually use it or release it like this. The debug info it shows is spicy.
|
||||||
|
# Features
|
||||||
|
|
||||||
|
- [x] Books
|
||||||
|
- [x] navbar with tags, series, authors, etc
|
||||||
|
- [x] Search
|
||||||
|
- [x] authentication
|
||||||
|
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
- [ ] fix author_detail_view with annotate instead of current implementation
|
||||||
|
|
Loading…
Reference in New Issue
Block a user