# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from collections import defaultdict
+from copy import deepcopy
from datetime import datetime, date, timedelta
+from itertools import zip_longest
import logging
import os
-from urllib.parse import unquote, urlsplit, urlunsplit
+from urllib.parse import quote_plus, unquote, urlsplit, urlunsplit
from django.conf import settings
from django.contrib import auth
from django.http.response import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.utils.encoding import iri_to_uri
-from django.utils.http import urlquote_plus
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_POST
from django_cas_ng.decorators import user_passes_test
+import requests
-from apiclient import NotAuthorizedError
+from librarian import epubcheck
+from librarian.html import raw_printable_text
+
+from apiclient import api_call, NotAuthorizedError
from . import forms
from . import helpers
from .helpers import active_tab
from .models import (Book, Chunk, Image, BookPublishRecord,
ChunkPublishRecord, ImagePublishRecord, Project)
+import catalogue.models
from fileupload.views import UploadView
#
key=lambda x: x[1]['time'], reverse=True)
for k, v in last_books:
v['time'] = datetime.fromtimestamp(v['time'])
+ try:
+ resp = api_call(request.user, 'username/')
+ except NotAuthorizedError:
+ wllogin = None
+ else:
+ wllogin = resp['username']
+
return render(request, 'documents/my_page.html', {
'last_books': last_books,
"logout_to": '/',
+ "wllogin": wllogin,
})
@never_cache
def logout_then_redirect(request):
auth.logout(request)
- return http.HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?='))
+ return http.HttpResponseRedirect(quote_plus(request.GET.get('next', '/'), safe='/?='))
@permission_required('documents.add_book')
@never_cache
def book_xml_dc(request, slug):
- book = get_object_or_404(Book, catalogue_book_id=slug)
+ book = get_object_or_404(Book, dc_slug=slug)
return serve_xml(request, book, slug)
return render(request, 'documents/book_text.html', locals())
+@login_required
@never_cache
def book_pdf(request, slug, mobile=False):
book = get_object_or_404(Book, slug=slug)
doc = book.wldocument()
# TODO: error handling
customizations = ['26pt', 'nothemes', 'nomargins', 'notoc'] if mobile else None
- pdf_file = doc.as_pdf(cover=True, ilustr_path=book.gallery_path(), customizations=customizations)
+ pdf_file = doc.as_pdf(cover=True, base_url=request.build_absolute_uri(book.gallery_path()), customizations=customizations)
from .ebook_utils import serve_file
return serve_file(pdf_file.get_filename(),
book.slug + '.pdf', 'application/pdf')
+@login_required
@never_cache
def book_epub(request, slug):
book = get_object_or_404(Book, slug=slug)
return HttpResponseForbidden("Not authorized.")
# TODO: move to celery
- doc = book.wldocument()
+ doc = book.wldocument(librarian2=True)
# TODO: error handling
- epub = doc.as_epub(ilustr_path=book.gallery_path()).get_bytes()
+
+ from librarian.builders import EpubBuilder
+ epub = EpubBuilder(
+ base_url='file://' + book.gallery_path() + '/',
+ debug=True
+ ).build(doc).get_bytes()
response = HttpResponse(content_type='application/epub+zip')
response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.epub'
response.write(epub)
return response
+@login_required
+@never_cache
+def book_epubcheck(request, slug):
+ book = get_object_or_404(Book, slug=slug)
+ if not book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+
+ # TODO: move to celery
+ doc = book.wldocument(librarian2=True)
+ # TODO: error handling
+
+ from librarian.builders import EpubBuilder
+ epub = EpubBuilder(
+ base_url='file://' + book.gallery_path() + '/',
+ debug=True
+ ).build(doc)
+ fname = epub.get_filename()
+
+ messages = epubcheck.epubcheck(fname)
+ for message in messages:
+ for location in message.get('locations', []):
+ if 'wl_chunk' in location:
+ location['wl_chunk'] = book[location['wl_chunk']]
+ return render(request, 'documents/book_epubcheck.html', {
+ 'messages': messages,
+ 'book': book,
+ })
+
+
+@login_required
@never_cache
def book_mobi(request, slug):
book = get_object_or_404(Book, slug=slug)
return HttpResponseForbidden("Not authorized.")
# TODO: move to celery
- doc = book.wldocument()
+ doc = book.wldocument(librarian2=True)
# TODO: error handling
- mobi = doc.as_mobi(ilustr_path=book.gallery_path()).get_bytes()
+ from librarian.builders import MobiBuilder
+ mobi = MobiBuilder(
+ base_url='file://' + book.gallery_path() + '/'
+ ).build(doc).get_bytes()
response = HttpResponse(content_type='application/x-mobipocket-ebook')
response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.mobi'
response.write(mobi)
publish_error = book.publishable_error()
publishable = publish_error is None
+ stats = None
+ try:
+ doc = book.wldocument(librarian2=True)
+ except:
+ doc = None
+ else:
+ try:
+ stats = doc.get_statistics()
+ except:
+ pass
+
+ cbook_by_slug = None
+ if book.dc_slug:
+ audio_items = requests.get(f'https://audio.wolnelektury.pl/archive/book/{book.dc_slug}.json').json()['items']
+ has_audio = bool(audio_items)
+ can_sell_audio = has_audio and all(x['project']['can_sell'] for x in audio_items)
+
+ if book.catalogue_book is None or book.dc_slug != book.catalogue_book.slug:
+ cbook_by_slug = catalogue.models.Book.objects.filter(slug=book.dc_slug).first()
+ else:
+ has_audio = None
+ can_sell_audio = None
+
return render(request, "documents/book_detail.html", {
"book": book,
+ "doc": doc,
+ "stats": stats,
"publishable": publishable,
"publishable_error": publish_error,
"form": form,
"publish_options_form": publish_options_form,
"editable": editable,
+ "has_audio": has_audio,
+ "can_sell_audio": can_sell_audio,
+ "cbook_by_slug": cbook_by_slug,
})
form.save()
go_next = request.GET.get('next', None)
if go_next:
- go_next = urlquote_plus(unquote(iri_to_uri(go_next)), safe='/?=&')
+ go_next = quote_plus(unquote(iri_to_uri(go_next)), safe='/?=&')
else:
go_next = doc.book.get_absolute_url()
return http.HttpResponseRedirect(go_next)
if referer:
parts = urlsplit(referer)
parts = ['', ''] + list(parts[2:])
- go_next = urlquote_plus(urlunsplit(parts))
+ go_next = quote_plus(urlunsplit(parts))
else:
go_next = ''
if form.is_valid():
days = form.cleaned_data['days']
beta = form.cleaned_data['beta']
+ hidden = form.cleaned_data['hidden']
else:
days = 0
beta = False
+ hidden = False
book = get_object_or_404(Book, slug=slug)
if not book.accessible(request):
return HttpResponseForbidden("Not authorized.")
try:
protocol = 'https://' if request.is_secure() else 'http://'
- book.publish(request.user, host=protocol + request.get_host(), days=days, beta=beta)
+ book.publish(request.user, host=protocol + request.get_host(), days=days, beta=beta, hidden=hidden)
except NotAuthorizedError:
return http.HttpResponseRedirect(reverse('apiclient_oauth' if not beta else 'apiclient_beta_oauth'))
except BaseException as e:
return "%s%s/" % (settings.IMAGE_DIR, self.object.gallery)
-def active_users_list(request):
+def active_users_list(request, csv=False):
year = int(request.GET.get('y', date.today().year))
by_user = defaultdict(lambda: 0)
by_email = defaultdict(lambda: 0)
for email, count in by_email.items():
active_users.append((email, names_by_email[email], count))
active_users.sort(key=lambda x: -x[2])
- return render(request, 'documents/active_users_list.html', {
- 'users': active_users,
- 'year': year,
- })
+ if csv:
+ return http.HttpResponse(
+ '\n'.join((
+ ','.join(
+ (str(x[2]), x[0], ','.join(x[1]))
+ )
+ for x in active_users
+ )),
+ content_type='text/csv',
+ headers={
+ 'Content-Disposition': f'attachment; filename=redakcja-{year}.csv',
+ }
+ )
+ else:
+ return render(request, 'documents/active_users_list.html', {
+ 'users': active_users,
+ 'year': year,
+ })
@user_passes_test(lambda u: u.is_superuser)
def mark_final_completed(request):
return render(request, 'documents/mark_final_completed.html')
+
+
+def synchro(request, slug):
+ book = get_object_or_404(Book, slug=slug)
+ if not book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+
+ document = book.wldocument(librarian2=True)
+ slug = document.meta.url.slug
+ print(f'https://audio.wolnelektury.pl/archive/book/{slug}.json')
+ error = None
+ try:
+ items = requests.get(f'https://audio.wolnelektury.pl/archive/book/{slug}.json').json()['items']
+ except:
+ error = 'Błąd połączenia z repozytorium audio.'
+ items = []
+ else:
+ mp3 = [
+ item['part'] for item in items
+ ]
+
+ split_on = (
+ 'naglowek_rozdzial',
+ 'naglowek_scena',
+ )
+
+ if split_on:
+ documents = []
+ headers = [('Początek', 0, 0)]
+ present = True
+ n = 0
+ while present:
+ present = False
+ n += 1
+ newdoc = deepcopy(document)
+ newdoc.tree.getroot().document = newdoc
+
+ master = newdoc.tree.getroot()[-1]
+ i = 0
+ for item in list(master):
+ #chunkno, sourceline = 0, self.sourceline
+ #if builder.splits:
+ # chunkno, sourceline = len(builder.splits), sourceline - builder.splits[-1]
+
+ if 'forcesplit' in item.attrib or (item.tag in split_on and 'nosplit' not in item.attrib):
+ # TODO: clear
+ i += 1
+ if n > 1 and i == n:
+ headers.append((
+ raw_printable_text(item),
+ 0,
+ item.sourceline,
+ ))
+ if i != n and not (n == 1 and not i):
+ master.remove(item)
+ else:
+ present = True
+ if present:
+ documents.append(newdoc)
+ else:
+ documents = [document]
+ headers = [(
+ document.meta.title, 0 ,0
+ )]
+
+ length_ok = len(headers) == len(mp3)
+ table = zip_longest(headers, mp3)
+
+
+ return render(request, 'documents/synchro.html', {
+ 'book': book,
+ 'documents': documents,
+ 'headers': headers,
+ 'mp3': mp3,
+ 'length_ok': length_ok,
+ 'table': table,
+ 'error': error,
+ })
+
+
+@permission_required('documents.change_book')
+def attach_book_to_catalogue(request, pk):
+ dbook = get_object_or_404(Book, pk=pk)
+ if dbook.dc_slug:
+ cbook = get_object_or_404(catalogue.models.Book, slug=dbook.dc_slug)
+ dbook.catalogue_book = cbook
+ dbook.save()
+ return http.HttpResponseRedirect(dbook.get_absolute_url())