change the comic list view to use a datatable.

This commit is contained in:
ajurna@gmail.com
2016-04-04 16:16:00 +01:00
parent d995c88f47
commit 56e055e9f4
9 changed files with 190 additions and 11 deletions

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-04-04 11:28
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('comic', '0012_auto_20160401_0949'),
]
operations = [
migrations.AddField(
model_name='comicstatus',
name='finished',
field=models.BooleanField(default=False),
),
]

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-04-04 13:02
from __future__ import unicode_literals
from django.db import migrations
from django.db.models import Max
def set_finished(apps, schema_editor):
comicstatus = apps.get_model('comic', 'comicstatus')
comicpage = apps.get_model('comic', 'ComicPage')
for row in comicstatus.objects.all():
last_page = comicpage.objects.filter(Comic=row.comic).aggregate(Max('index'))
if row.last_read_page == last_page['index__max']:
row.finished = True
row.save()
class Migration(migrations.Migration):
dependencies = [
('comic', '0013_comicstatus_finished'),
]
operations = [
migrations.RunPython(set_finished, reverse_code=migrations.RunPython.noop),
]

View File

@@ -353,5 +353,12 @@ class ComicStatus(models.Model):
comic = models.ForeignKey(ComicBook, unique=False, null=False) comic = models.ForeignKey(ComicBook, unique=False, null=False)
last_read_page = models.IntegerField(default=0) last_read_page = models.IntegerField(default=0)
unread = models.BooleanField(default=True) unread = models.BooleanField(default=True)
finished = models.BooleanField(default=False)
@property
def read(self):
return self.last_read_page
def __str__(self):
return 'C:{0} P:{1}'.format(self.comic.file_name, self.last_read_page)
# TODO: add support to reference items last being read # TODO: add support to reference items last being read

View File

@@ -31,4 +31,12 @@ body {
max-height: 300px; max-height: 300px;
overflow: auto; overflow: auto;
box-shadow: none; box-shadow: none;
}
td a {
display:block;
width:100%;
}
tr.clickable-row {
cursor: pointer;
} }

View File

@@ -73,6 +73,7 @@
<script src="/static/js/bootstrap.min.js"></script> <script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/jasny-bootstrap.min.js"></script> <script src="/static/js/jasny-bootstrap.min.js"></script>
<script src="/static/js/jQuery-2.2.2.min.js"></script> <script src="/static/js/jQuery-2.2.2.min.js"></script>
<script src="/static/js/js.cookie.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/t/bs/dt-1.10.11,b-colvis-1.1.2,r-2.0.2/datatables.min.js"></script> <script type="text/javascript" src="https://cdn.datatables.net/t/bs/dt-1.10.11,b-colvis-1.1.2,r-2.0.2/datatables.min.js"></script>
{% block script %} {% block script %}
{% endblock %} {% endblock %}

View File

@@ -3,6 +3,24 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<table class="table table-bordered table-striped table-hover" id="comic_list">
<caption><h2>Comics</h2></caption>
<thead>
<tr>
<th><center><span class="glyphicon glyphicon-file"></span></center></th>
<th width="100%">File/Folder</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr class="clickable-row" data-href="/comic/">
<td><center><span class="glyphicon glyphicon-file"></span></center></td>
<td>loading data</td>
<td><span class="label label-primary pull-right">1/23</span></td>
</tr>
</tbody>
</table>
<!--/.nav-collapse
<h2 class="center">Comics</h2> <h2 class="center">Comics</h2>
<div class="list-group"> <div class="list-group">
{% if file_list %} {% if file_list %}
@@ -17,12 +35,42 @@
{% else %} {% else %}
<p class="list-group-item">No comics.</p> <p class="list-group-item">No comics.</p>
{% endif %} {% endif %}
</div> </div>-->
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$(document).ready(function() {
$('#comic_list').DataTable({
"processing": true,
"ajax": {
"type": "POST",
"url": "{{ json_url }}",
"data": function ( d ) {
d.csrfmiddlewaretoken = Cookies.get('csrftoken');
},
},
"rowCallback": function( row, data, index ) {
var r = $(row)
r.attr('data-href', data['url']);
r.attr('style', 'cursor: pointer;')
r.click(function() {
window.document.location = $(this).data("href");
});
},
"columns": [
{ "data" : "icon", "orderable": false },
{ "data" : "name" },
{ "data" : "label" },
],
"order": [[ 1, 'asc' ]],
});
$(".clickable-row").click(function() {
window.document.location = $(this).data("href");
});
} );
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -11,5 +11,7 @@ urlpatterns = [
url(r'^account/$', views.account_page, name='account'), url(r'^account/$', views.account_page, name='account'),
url(r'^read/(?P<comic_selector>[\w-]+)/(?P<page>[0-9]+)/$', views.read_comic, name='read_comic'), url(r'^read/(?P<comic_selector>[\w-]+)/(?P<page>[0-9]+)/$', views.read_comic, name='read_comic'),
url(r'^read/(?P<comic_selector>[\w-]+)/(?P<page>[0-9]+)/img$', views.get_image, name='get_image'), url(r'^read/(?P<comic_selector>[\w-]+)/(?P<page>[0-9]+)/img$', views.get_image, name='get_image'),
url(r'^list_json/$', views.comic_list_json, name='comic_list_json1'),
url(r'^list_json/(?P<directory_selector>[\w-]+)/$', views.comic_list_json, name='comic_list_json2'),
url(r'^(?P<directory_selector>[\w-]+)/$', views.comic_list, name='comic_list'), url(r'^(?P<directory_selector>[\w-]+)/$', views.comic_list, name='comic_list'),
] ]

View File

@@ -75,6 +75,20 @@ def generate_breadcrumbs_from_menu(paths):
return output return output
class DirFile:
def __init__(self):
self.name = ''
self.isdir = False
self.icon = ''
self.iscb = False
self.location = ''
self.label = ''
self.cur_page = 0
def __str__(self):
return self.name
def generate_directory(user, directory=False): def generate_directory(user, directory=False):
""" """
@@ -90,7 +104,7 @@ def generate_directory(user, directory=False):
dir_path = '' dir_path = ''
ordered_dir_list = get_ordered_dir_list(base_dir) ordered_dir_list = get_ordered_dir_list(base_dir)
for file_name in ordered_dir_list: for file_name in ordered_dir_list:
df = ComicBook.DirFile() df = DirFile()
df.name = file_name df.name = file_name
if path.isdir(path.join(base_dir, dir_path, file_name)): if path.isdir(path.join(base_dir, dir_path, file_name)):
if directory: if directory:
@@ -101,7 +115,8 @@ def generate_directory(user, directory=False):
parent__isnull=True) parent__isnull=True)
df.isdir = True df.isdir = True
df.icon = 'glyphicon-folder-open' df.icon = 'glyphicon-folder-open'
df.location = urlsafe_base64_encode(d.selector.bytes) df.location = '/comic/{0}/'.format(urlsafe_base64_encode(d.selector.bytes).decode())
df.label = generate_dir_status(user, d)
elif file_name.lower()[-4:] in ['.rar', '.zip', '.cbr', '.cbz']: elif file_name.lower()[-4:] in ['.rar', '.zip', '.cbr', '.cbz']:
df.iscb = True df.iscb = True
df.icon = 'glyphicon-book' df.icon = 'glyphicon-book'
@@ -114,16 +129,18 @@ def generate_directory(user, directory=False):
directory__isnull=True) directory__isnull=True)
except ComicBook.DoesNotExist: except ComicBook.DoesNotExist:
book = ComicBook.process_comic_book(file_name, directory) book = ComicBook.process_comic_book(file_name, directory)
df.location = urlsafe_base64_encode(book.selector.bytes)
status, _ = ComicStatus.objects.get_or_create(comic=book, user=user) status, _ = ComicStatus.objects.get_or_create(comic=book, user=user)
last_page = status.last_read_page last_page = status.last_read_page
df.location = '/comic/read/{0}/{1}/'.format(urlsafe_base64_encode(book.selector.bytes).decode(),
last_page)
if status.unread: if status.unread:
df.label = '<span class="label label-default pull-right">Unread</span>' df.label = '<center><span class="label label-default">Unread</span></center>'
elif (last_page + 1) == book.page_count: elif (last_page + 1) == book.page_count:
df.label = '<span class="label label-success pull-right">Read</span>' df.label = '<center><span class="label label-success">Read</span></center>'
df.cur_page = last_page df.cur_page = last_page
else: else:
label_text = '<span class="label label-primary pull-right">%s/%s</span>' % \ label_text = '<center><span class="label label-primary">%s/%s</span></center>' % \
(last_page + 1, book.page_count) (last_page + 1, book.page_count)
df.label = label_text df.label = label_text
df.cur_page = last_page df.cur_page = last_page
@@ -133,6 +150,21 @@ def generate_directory(user, directory=False):
return files return files
def generate_dir_status(user, directory):
cb_list = ComicBook.objects.filter(directory=directory)
total = cb_list.count()
total_read = ComicStatus.objects.filter(user=user,
comic__in=cb_list,
finished=True).count()
if total == 0:
return '<center><span class="label label-default">Empty</span></center>'
elif total == total_read:
return '<center><span class="label label-success">All Read</span></center>'
elif total_read == 0:
return '<center><span class="label label-default">Unread</span></center>'
return '<center><span class="label label-primary">{0}/{1}</span></center>'.format(total_read, total)
def get_ordered_dir_list(folder): def get_ordered_dir_list(folder):
directories = [] directories = []
files = [] files = []

View File

@@ -1,5 +1,6 @@
import uuid import uuid
from os import path from os import path
import json
from django.contrib.auth import login, authenticate from django.contrib.auth import login, authenticate
from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.decorators import login_required, user_passes_test
@@ -7,13 +8,16 @@ from django.contrib.auth.models import User
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.utils.http import urlsafe_base64_decode from django.utils.http import urlsafe_base64_decode
from django.views.decorators.csrf import ensure_csrf_cookie
from django.db.models import Max
from .forms import SettingsForm, AccountForm, EditUserForm, AddUserForm, InitialSetupForm from .forms import SettingsForm, AccountForm, EditUserForm, AddUserForm, InitialSetupForm
from .models import Setting, ComicBook, ComicStatus, Directory from .models import Setting, ComicBook, ComicStatus, Directory, ComicPage
from .util import generate_breadcrumbs_from_path, generate_breadcrumbs_from_menu, \ from .util import generate_breadcrumbs_from_path, generate_breadcrumbs_from_menu, \
generate_title_from_path, Menu, generate_directory, scan_directory generate_title_from_path, Menu, generate_directory, scan_directory
@ensure_csrf_cookie
@login_required @login_required
def comic_list(request, directory_selector=False): def comic_list(request, directory_selector=False):
try: try:
@@ -24,8 +28,8 @@ def comic_list(request, directory_selector=False):
return redirect('/comic/settings/') return redirect('/comic/settings/')
if directory_selector: if directory_selector:
directory_selector = uuid.UUID(bytes=urlsafe_base64_decode(directory_selector)) selector = uuid.UUID(bytes=urlsafe_base64_decode(directory_selector))
directory = Directory.objects.get(selector=directory_selector) directory = Directory.objects.get(selector=selector)
else: else:
directory = False directory = False
@@ -34,19 +38,46 @@ def comic_list(request, directory_selector=False):
if directory: if directory:
title = generate_title_from_path(directory.path) title = generate_title_from_path(directory.path)
breadcrumbs = generate_breadcrumbs_from_path(directory) breadcrumbs = generate_breadcrumbs_from_path(directory)
json_url = '/comic/list_json/{0}/'.format(directory_selector)
else: else:
title = generate_title_from_path('Home') title = generate_title_from_path('Home')
breadcrumbs = generate_breadcrumbs_from_path() breadcrumbs = generate_breadcrumbs_from_path()
files = generate_directory(request.user, directory) json_url = '/comic/list_json/'
files = generate_directory(request.user)
return render(request, 'comic/comic_list.html', { return render(request, 'comic/comic_list.html', {
'file_list': files, 'file_list': files,
'breadcrumbs': breadcrumbs, 'breadcrumbs': breadcrumbs,
'menu': Menu(request.user, 'Browse'), 'menu': Menu(request.user, 'Browse'),
'title': title, 'title': title,
'json_url': json_url
}) })
@login_required
def comic_list_json(request, directory_selector=False):
icon_str = '<span class="glyphicon {0}"></span>'
if directory_selector:
directory_selector = uuid.UUID(bytes=urlsafe_base64_decode(directory_selector))
directory = Directory.objects.get(selector=directory_selector)
else:
directory = False
files = generate_directory(request.user, directory)
response_data = dict()
response_data['data'] = []
for file in files:
response_data['data'].append({
'icon': icon_str.format(file.icon),
'name': file.name,
'label': file.label,
'url': file.location,
})
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
@login_required @login_required
def account_page(request): def account_page(request):
success_message = [] success_message = []
@@ -208,6 +239,10 @@ def read_comic(request, comic_selector, page):
status, _ = ComicStatus.objects.get_or_create(comic=book, user=request.user) status, _ = ComicStatus.objects.get_or_create(comic=book, user=request.user)
status.unread = False status.unread = False
status.last_read_page = page status.last_read_page = page
if ComicPage.objects.filter(Comic=book).aggregate(Max('index'))['index__max'] == status.last_read_page:
status.finished = True
else:
status.finished = False
status.save() status.save()
title = 'CBWebReader - ' + book.file_name + ' - Page: ' + str(page) title = 'CBWebReader - ' + book.file_name + ' - Page: ' + str(page)
context = { context = {