X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/1d5395d0e29c482f1060be3d7def7aff15752014..9b533fdbfd08bd764744ae3a0d4717c87b61cf18:/src/documents/views.py diff --git a/src/documents/views.py b/src/documents/views.py index 0a9c0982..97d2ad6d 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -2,10 +2,12 @@ # 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 @@ -19,17 +21,21 @@ from django.http import Http404, HttpResponse, HttpResponseForbidden 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 # @@ -66,9 +72,17 @@ def my(request): 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, }) @@ -100,7 +114,7 @@ def activity(request, isodate=None): @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') @@ -224,7 +238,7 @@ def book_xml(request, slug): @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) @@ -263,6 +277,7 @@ def book_html(request, 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) @@ -273,12 +288,13 @@ def book_pdf(request, slug, mobile=False): 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) @@ -286,15 +302,50 @@ def book_epub(request, 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) @@ -302,9 +353,12 @@ def book_mobi(request, 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) @@ -345,13 +399,41 @@ def book(request, slug): 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, }) @@ -437,7 +519,7 @@ def chunk_edit(request, slug, chunk): 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) @@ -448,7 +530,7 @@ def chunk_edit(request, slug, chunk): if referer: parts = urlsplit(referer) parts = ['', ''] + list(parts[2:]) - go_next = urlquote_plus(urlunsplit(parts)) + go_next = quote_plus(urlunsplit(parts)) else: go_next = '' @@ -627,7 +709,7 @@ class GalleryView(UploadView): 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) @@ -652,10 +734,24 @@ def active_users_list(request): 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) @@ -672,3 +768,91 @@ def mark_final(request): 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())