sorl-thumbnail==12.5.0
# home-brewed & dependencies
-librarian==1.8.3
+librarian==1.10
# celery tasks
celery[redis]==4.4.7
msgstr ""
"Project-Id-Version: WolneLektury\n"
"Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2020-10-16 09:51+0200\n"
+"PO-Revision-Date: 2020-11-09 00:53+0100\n"
"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org."
"pl>\n"
msgid "Book %s already exists"
msgstr "Książka %s już istnieje"
-#: catalogue/models/book.py:813
+#: catalogue/models/book.py:853
msgid "This work needs modernisation"
msgstr "Ten utwór wymaga uwspółcześnienia"
-#: catalogue/models/book.py:892 catalogue/models/bookmedia.py:27
+#: catalogue/models/book.py:932 catalogue/models/bookmedia.py:27
#, python-format
msgid "%s file"
msgstr "plik %s"
msgstr "tagi"
#: catalogue/templates/catalogue/book_detail.html:24
-#: catalogue/templates/catalogue/book_text.html:25
+#: catalogue/templates/catalogue/book_text.html:34
msgid "Other versions"
msgstr "Inne wersje"
msgstr "Zobacz też"
#: catalogue/templates/catalogue/book_detail.html:41
-#: catalogue/templates/catalogue/book_text.html:38
-#: catalogue/templates/catalogue/book_text.html:57
+#: catalogue/templates/catalogue/book_text.html:47
+#: catalogue/templates/catalogue/book_text.html:66
#: catalogue/templates/catalogue/tagged_object_list.html:20
msgid "Themes"
msgstr "Motywy"
msgid "For now this work is only available for our subscribers."
msgstr "Jak na razie ten utwór jest dostępny wyłącznie dla naszych Przyjaciół."
-#: catalogue/templates/catalogue/book_text.html:32
+#: catalogue/templates/catalogue/book_text.html:41
msgid "Table of contents"
msgstr "Spis treści"
-#: catalogue/templates/catalogue/book_text.html:44
+#: catalogue/templates/catalogue/book_text.html:53
msgid "Edit. note"
msgstr "Nota red."
-#: catalogue/templates/catalogue/book_text.html:50
+#: catalogue/templates/catalogue/book_text.html:59
msgid "Infobox"
msgstr "Informacje"
-#: catalogue/templates/catalogue/book_text.html:55
+#: catalogue/templates/catalogue/book_text.html:64
msgid "Numbering"
msgstr "Numeracja"
-#: catalogue/templates/catalogue/book_text.html:59
+#: catalogue/templates/catalogue/book_text.html:68
msgid "Footnotes"
msgstr "Przypisy"
-#: catalogue/templates/catalogue/book_text.html:82
+#: catalogue/templates/catalogue/book_text.html:70
+msgid "References"
+msgstr "Odniesienia"
+
+#: catalogue/templates/catalogue/book_text.html:93
#: catalogue/templates/catalogue/viewer_base.html:54
msgid "Close"
msgstr "Zamknij"
-#: catalogue/templates/catalogue/book_text.html:83
+#: catalogue/templates/catalogue/book_text.html:94
msgid "Please wait..."
msgstr "Proszę czekać…"
-#: catalogue/templates/catalogue/book_text.html:127
+#: catalogue/templates/catalogue/book_text.html:149
msgid "Other versions of the book"
msgstr "Inne wersje utworu"
-#: catalogue/templates/catalogue/book_text.html:128
+#: catalogue/templates/catalogue/book_text.html:150
msgid "Close the other version"
msgstr "Zamknij drugą wersję"
child.parent_cover_changed()
book.update_popularity()
+ tasks.update_references.delay(book.id)
+
cls.published.send(sender=cls, instance=book)
return book
+ def get_master(self):
+ master_tags = [
+ 'opowiadanie',
+ 'powiesc',
+ 'dramat_wierszowany_l',
+ 'dramat_wierszowany_lp',
+ 'dramat_wspolczesny', 'liryka_l', 'liryka_lp',
+ 'wywiad',
+ ]
+ from librarian.parser import WLDocument
+ wld = WLDocument.from_file(self.xml_file.path, parse_dublincore=False)
+ root = wld.edoc.getroot()
+ for master in root.iter():
+ if master.tag in master_tags:
+ return master
+
+ def update_references(self):
+ from references.models import Entity, Reference
+ master = self.get_master()
+ found = set()
+ for i, sec in enumerate(master):
+ for ref in sec.findall('.//ref'):
+ href = ref.attrib.get('href', '')
+ if not href or href in found:
+ continue
+ found.add(href)
+ entity, created = Entity.objects.get_or_create(
+ uri=href
+ )
+ ref, created = Reference.objects.get_or_create(
+ book=self,
+ entity=entity
+ )
+ ref.first_section = 'sec%d' % (i + 1)
+ entity.populate()
+ entity.save()
+ Reference.objects.filter(book=self).exclude(entity__uri__in=found).delete()
+
@classmethod
@transaction.atomic
def repopulate_ancestors(cls):
def update_counters():
from .helpers import update_counters
update_counters()
+
+
+@task(ignore_result=True)
+def update_references(book_id):
+ from catalogue.models import Book
+ Book.objects.get(id=book_id).update_references()
+
{% extends "catalogue/viewer_base.html" %}
-{% load i18n %}
+{% load i18n l10n %}
{% load catalogue_tags %}
{% load chunks %}
{% load thumbnail %}
{% block title %}{{ book.pretty_title }}{% endblock %}
+{% block extrahead %}
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
+ integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
+ crossorigin=""/>
+ <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
+ integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
+ crossorigin=""></script>
+{% endblock %}
+
{% block menu %}
<li>
<a href="{{ book.get_absolute_url }}" id="menu-book" data-box="book-short">
data-setting="always-hide-line-numbers"><span>{% trans "Numbering" %}</span></a>
<a href="#" class="settings-switch" id="settings-themes"
data-setting="always-hide-themes"><span>{% trans "Themes" %}</span></a>
- <a href="#" class="settings-switch" id="settings-annotations"
- data-setting="no-annotations"><span>{% trans "Footnotes" %}</span></a>
+ <a href="#" class="settings-switch" id="settings-annotations"
+ data-setting="no-annotations"><span>{% trans "Footnotes" %}</span></a>
+ <a href="#" class="settings-switch" id="settings-references"
+ data-setting="no-references"><span>{% trans "References" %}</span></a>
{% endblock menu %}
<div id="other-text-waiter">{% trans "Please wait..." %}</div>
<div id="other-text-body" style="display: none;"></div>
</article>
+
+ <div id="reference-box">
+ <div id="reference-map"></div>
+ <a id="reference-close" href="#">x</a>
+ <div id="reference-images">
+ </div>
+ <a id="reference-link" target="_blank"></a>
+ </div>
+
+
+
{% endblock big-pane %}
{% block footer %}
{% include 'annoy/dynamic_insert.html' %}
{% endfor %}
</div>
+
+ {% localize off %}
+ <script type="application/json" id="interesting-references">
+ {
+ {% for ref in book.reference_set.all %}
+ {% if ref.entity.is_interesting %}
+ "{{ ref.entity.uri }}": {
+ {% if ref.entity.lat and ref.entity.lon %}
+ "location": [{{ ref.entity.lat }}, {{ ref.entity.lon }}],
+ {% endif %}
+ "images": {{ ref.entity.images|safe }},
+ "label": "{{ ref.entity.label }}",
+ "description": "{{ ref.entity.description }}",
+ "wikipedia_link": "{{ ref.entity.wikipedia_link }}"
+ }{% if not forloop.last %},{% endif %}
+ {% endif %}
+ {% endfor %}
+ }
+ </script>
+ {% endlocalize %}
{% endblock footer %}
_("An error occurred: %(exception)s\n\n%(tb)s") % {
'exception': exception, 'tb': tb
},
- mimetype='text/plain'
+ content_type='text/plain'
)
return HttpResponse(_("Book imported successfully"))
return HttpResponse(_("Error importing file: %r") % book_import_form.errors)
--- /dev/null
+from django.contrib import admin
+from . import models
+
+
+admin.site.register(models.Entity)
--- /dev/null
+from django.apps import AppConfig
+
+
+class ReferencesConfig(AppConfig):
+ name = 'references'
--- /dev/null
+# Generated by Django 2.2.16 on 2020-11-08 15:38
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('catalogue', '0030_collection_role'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Entity',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uri', models.CharField(db_index=True, max_length=255, unique=True)),
+ ('label', models.CharField(blank=True, max_length=1024)),
+ ('description', models.CharField(blank=True, max_length=2048)),
+ ('wikipedia_link', models.CharField(blank=True, max_length=1024)),
+ ('lat', models.FloatField(blank=True, null=True)),
+ ('lon', models.FloatField(blank=True, null=True)),
+ ('images', models.TextField(blank=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Reference',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('first_section', models.CharField(max_length=255)),
+ ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='catalogue.Book')),
+ ('entity', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='references.Entity')),
+ ],
+ options={
+ 'unique_together': {('book', 'entity')},
+ },
+ ),
+ ]
--- /dev/null
+import json
+from django.db import models
+from wikidata.client import Client
+
+
+class Entity(models.Model):
+ WIKIDATA_PREFIX = 'https://www.wikidata.org/wiki/'
+ WIKIDATA_IMAGE = 'P18'
+ WIKIDATA_COORDINATE_LOCATION = 'P625'
+ WIKIDATA_EARTH = 'Q2'
+
+ uri = models.CharField(max_length=255, unique=True, db_index=True)
+ label = models.CharField(max_length=1024, blank=True)
+ description = models.CharField(max_length=2048, blank=True)
+ wikipedia_link = models.CharField(max_length=1024, blank=True)
+ lat = models.FloatField(null=True, blank=True)
+ lon = models.FloatField(null=True, blank=True)
+ images = models.TextField(blank=True)
+
+ @property
+ def is_interesting(self):
+ return self.wikipedia_link or (self.lat and self.lon) or len(self.images)>2
+
+ def populate(self):
+ if self.uri.startswith(self.WIKIDATA_PREFIX):
+ self.populate_from_wikidata(
+ self.uri[len(self.WIKIDATA_PREFIX):]
+ )
+
+ def populate_from_wikidata(self, wikidata_id):
+ client = Client()
+ entity = client.get(wikidata_id)
+
+ self.label = entity.label.get('pl', entity.label) or ''
+ self.description = entity.description.get('pl', entity.description) or ''
+ sitelinks = entity.attributes.get('sitelinks', {})
+ self.wikipedia_link = sitelinks.get('plwiki', sitelinks.get('enwiki', {})).get('url') or ''
+
+ location_prop = client.get(self.WIKIDATA_COORDINATE_LOCATION)
+ location = entity.get(location_prop)
+ if location and location.globe.id == self.WIKIDATA_EARTH:
+ self.lat = location.latitude
+ self.lon = location.longitude
+
+ images = entity.getlist(client.get(self.WIKIDATA_IMAGE))
+ self.images = json.dumps([
+ {
+ "url": image.image_url,
+ "page": image.page_url,
+ "resolution": image.image_resolution,
+ } for image in images
+ ])
+
+
+class Reference(models.Model):
+ book = models.ForeignKey('catalogue.Book', models.CASCADE)
+ entity = models.ForeignKey(Entity, models.CASCADE)
+ first_section = models.CharField(max_length=255)
+
+ class Meta:
+ unique_together = (('book', 'entity'),)
+
--- /dev/null
+from django.test import TestCase
+
+# Create your tests here.
--- /dev/null
+from django.shortcuts import render
+
+# Create your views here.
'newtagging',
'opds',
'pdcounter',
+ 'references',
'reporting',
'sponsors',
'stats',
'js/book_text/info.js',
'js/book_text/menu.js',
'js/book_text/note.js',
+ 'js/book_text/references.js',
'js/book_text/settings.js',
'js/book_text/toc.js',
'js/locale.js',
--- /dev/null
+(function($){$(function(){
+
+ var interestingReferences = $("#interesting-references").text();
+ if (interestingReferences) {
+ interestingReferences = $.parseJSON(interestingReferences);
+ }
+ if (interestingReferences) {
+ $("settings-references").show();
+ }
+
+
+
+ var map_enabled = false;
+ var marker = L.marker([0,0]);
+ var map = null;
+
+ function enable_map() {
+ if (map_enabled) return;
+
+ map = L.map('reference-map').setView([0, 0], 11);
+ L.tileLayer('https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png?lang=pl', {
+ attribution: 'Map data © <a href="http://openstreetmap.org/copyright">OpenStreetMap contributors</a>'
+ }).addTo(map);
+
+ map_enabled = true;
+ }
+ function disable_map() {
+ $("#reference-map").hide('slow');
+ }
+
+
+ $("#reference-close").on("click", function() {
+ $("#reference-box").hide();
+ });
+
+ $('a.reference').each(function() {
+ $this = $(this);
+ uri = $this.attr('data-uri');
+ console.log('check ' + uri);
+ if (interestingReferences.hasOwnProperty(uri)) {
+ $this.addClass('interesting');
+ ref = interestingReferences[uri];
+
+ $this.attr('href', ref.wikipedia_link);
+ $this.attr('target', '_blank');
+ }
+ });
+
+
+ $('a.reference.interesting').on('click', function(event) {
+ event.preventDefault();
+
+ $("#reference-box").show();
+
+ $this = $(this);
+ uri = $this.attr('data-uri');
+ ref = interestingReferences[uri];
+
+ if (ref.location) {
+ enable_map();
+
+ marker.setLatLng(ref.location);
+ //marker.setContent(ref.label);
+ marker.bindTooltip(ref.label).openTooltip();
+ map.addLayer(marker);
+ map.panTo(ref.location, {
+ animate: true,
+ duration: 1,
+ });
+ } else {
+ disable_map();
+ if (map) {
+ map.removeLayer(marker);
+ }
+ }
+
+ $("#reference-images img").remove();
+ if (ref.images) {
+ $.each(ref.images, function(i, e) {
+ $i = $("<a target='_blank'><img></a>");
+ $i.attr('href', e.page);
+ $('img', $i).attr('src', e.url);
+ $("#reference-images").append($i);
+ })
+ }
+
+ $("#reference-link").text(ref.label);
+ $("#reference-link").attr('href', ref.wikipedia_link);
+ });
+})})(jQuery);
@import "book_text/note";
@import "book_text/numbering";
@import "book_text/other";
+@import "book_text/references";
@import "book_text/settings";
@import "book_text/themes";
@import "book_text/toc";
--- /dev/null
+a.reference.interesting::after {
+ content: "📌";
+}
+.no-references .reference.interesting:after {
+ display: none;
+}
+
+#reference-box {
+ display: none;
+ width: 300px;
+ position: fixed;
+ top: 0;
+ right: 0;
+ z-index:100;
+ background: #eee;
+ #reference-map {
+ height:400px;
+ width: 300px;
+ }
+ #reference-images {
+ padding: 10px;
+ white-space: nowrap;
+ overflow-x: auto;
+ a {
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0 10px 0 0;
+ img {
+ margin: 0;
+ height: 100px;
+ }
+ }
+ }
+ #reference-link {
+ display: block;
+ font-size: 1.5em;
+ padding: 10px;
+ }
+
+ #reference-close {
+ font-size: 30px;
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ z-index: 1000;
+ font-family: sans-serif;
+ color: black;
+ }
+}
#settings-line-numbers span {@include switch-off;}
}
+#settings-references span {@include switch-on;}
+.no-references {
+ #settings-references span {@include switch-off;}
+}