mirror of
https://github.com/ajurna/cbwebreader.git
synced 2025-12-06 06:17:17 +00:00
change the comic list view to use a datatable.
This commit is contained in:
20
comic/migrations/0013_comicstatus_finished.py
Normal file
20
comic/migrations/0013_comicstatus_finished.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
||||||
26
comic/migrations/0014_auto_20160404_1402.py
Normal file
26
comic/migrations/0014_auto_20160404_1402.py
Normal 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),
|
||||||
|
]
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -32,3 +32,11 @@ body {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td a {
|
||||||
|
display:block;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
tr.clickable-row {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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 %}
|
||||||
@@ -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'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
Reference in New Issue
Block a user