Importing covers from WikiCommons, MNW.
authorRadek Czajka <rczajka@rczajka.pl>
Fri, 25 Feb 2022 13:20:55 +0000 (14:20 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Fri, 25 Feb 2022 13:20:55 +0000 (14:20 +0100)
src/cover/forms.py
src/cover/locale/pl/LC_MESSAGES/django.mo
src/cover/locale/pl/LC_MESSAGES/django.po
src/cover/templates/cover/add_image.html
src/cover/utils.py
src/cover/views.py

index ea51e46..c4f06fa 100644 (file)
@@ -10,7 +10,7 @@ from cover.models import Image
 from django.utils.safestring import mark_safe
 from PIL import Image as PILImage
 
 from django.utils.safestring import mark_safe
 from PIL import Image as PILImage
 
-from cover.utils import get_flickr_data, FlickrError, URLOpener
+from cover.utils import get_import_data, FlickrError, URLOpener
 
 
 class ImageAddForm(forms.ModelForm):
 
 
 class ImageAddForm(forms.ModelForm):
@@ -41,8 +41,8 @@ class ImageAddForm(forms.ModelForm):
             same_source = Image.objects.filter(source_url=source_url)
             if same_source:
                 raise forms.ValidationError(mark_safe(
             same_source = Image.objects.filter(source_url=source_url)
             if same_source:
                 raise forms.ValidationError(mark_safe(
-                    ugettext('Image <a href="%s">already in repository</a>'
-                             % same_source.first().get_absolute_url())))
+                    ugettext('Image <a href="%(url)s">already in repository</a>.')
+                    % {'url': same_source.first().get_absolute_url()}))
         return source_url
 
     def clean(self):
         return source_url
 
     def clean(self):
@@ -91,15 +91,15 @@ class ReadonlyImageEditForm(ImageEditForm):
         raise AssertionError("ReadonlyImageEditForm should not be saved.")
 
 
         raise AssertionError("ReadonlyImageEditForm should not be saved.")
 
 
-class FlickrForm(forms.Form):
-    source_url = forms.URLField(label=_('Flickr URL'))
+class ImportForm(forms.Form):
+    source_url = forms.URLField(label=_('WikiCommons, MNW or Flickr URL'))
 
     def clean_source_url(self):
         url = self.cleaned_data['source_url']
         try:
 
     def clean_source_url(self):
         url = self.cleaned_data['source_url']
         try:
-            flickr_data = get_flickr_data(url)
+            import_data = get_import_data(url)
         except FlickrError as e:
             raise forms.ValidationError(e)
         for field_name in ('license_url', 'license_name', 'author', 'title', 'download_url'):
         except FlickrError as e:
             raise forms.ValidationError(e)
         for field_name in ('license_url', 'license_name', 'author', 'title', 'download_url'):
-            self.cleaned_data[field_name] = flickr_data[field_name]
-        return flickr_data['source_url']
+            self.cleaned_data[field_name] = import_data[field_name]
+        return import_data['source_url']
index 1c297fe..756253c 100644 (file)
Binary files a/src/cover/locale/pl/LC_MESSAGES/django.mo and b/src/cover/locale/pl/LC_MESSAGES/django.mo differ
index d40bd54..5418ccf 100644 (file)
@@ -5,42 +5,36 @@
 #
 msgid ""
 msgstr ""
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
+"Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
 "Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2014-02-25 13:30+0100\n"
+"PO-Revision-Date: 2022-02-25 14:10+0100\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
+"Language-Team: \n"
+"Language: pl\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
 "|| n%100>=20) ? 1 : 2);\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
 "|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 1.5.4\n"
+"X-Generator: Poedit 3.0\n"
 
 
-#: cover/forms.py:34
+#: cover/forms.py:34 cover/forms.py:44
 #, python-format
 msgid "Image <a href=\"%(url)s\">already in repository</a>."
 msgstr "Obraz <a href=\"%(url)s\">jest już w repozytorium</a>."
 
 #, python-format
 msgid "Image <a href=\"%(url)s\">already in repository</a>."
 msgstr "Obraz <a href=\"%(url)s\">jest już w repozytorium</a>."
 
-#: cover/forms.py:44
-#, fuzzy, python-format
-#| msgid "Image <a href=\"%(url)s\">already in repository</a>."
-msgid "Image <a href=\"%s\">already in repository</a>"
-msgstr "Obraz <a href=\"%(url)s\">jest już w repozytorium</a>."
-
 #: cover/forms.py:53
 msgid "No image specified"
 #: cover/forms.py:53
 msgid "No image specified"
-msgstr ""
+msgstr "Nie wskazano obrazu"
 
 #: cover/forms.py:61 cover/forms.py:78
 #, python-format
 msgid "Image too small: %sx%s, minimal dimensions %sx%s"
 
 #: cover/forms.py:61 cover/forms.py:78
 #, python-format
 msgid "Image too small: %sx%s, minimal dimensions %sx%s"
-msgstr ""
+msgstr "Obraz za mały: %sx%s, minimalny rozmiar %sx%s"
 
 #: cover/forms.py:95
 
 #: cover/forms.py:95
-msgid "Flickr URL"
-msgstr "URL z Flickra"
+msgid "WikiCommons, MNW or Flickr URL"
+msgstr "URL z WikiCommons, MNW albo Flickra"
 
 #: cover/models.py:23
 msgid "title"
 
 #: cover/models.py:23
 msgid "title"
@@ -85,8 +79,8 @@ msgid "Add image"
 msgstr "Dodaj obrazek"
 
 #: cover/templates/cover/add_image.html:19
 msgstr "Dodaj obrazek"
 
 #: cover/templates/cover/add_image.html:19
-msgid "Load from Flickr"
-msgstr "Pobierz z Flickra"
+msgid "Load from WikiCommons, MNW, Flickr"
+msgstr "Pobierz z WikiCommons, MNW albo Flickra"
 
 #: cover/templates/cover/image_detail.html:6
 #: cover/templates/cover/image_detail.html:9
 
 #: cover/templates/cover/image_detail.html:6
 #: cover/templates/cover/image_detail.html:9
@@ -117,3 +111,15 @@ msgstr "Obrazki na okładki"
 #: cover/templates/cover/image_list.html:15
 msgid "Add new"
 msgstr "Dodaj nowy"
 #: cover/templates/cover/image_list.html:15
 msgid "Add new"
 msgstr "Dodaj nowy"
+
+#, fuzzy, python-format
+#~| msgid "Image <a href=\"%(url)s\">already in repository</a>."
+#~ msgid "Image <a href=\"%(url)s\">already in repository</a>"
+#~ msgstr "Obraz <a href=\"%(url)s\">jest już w repozytorium</a>."
+
+#, python-format
+#~ msgid "Image <a href=\"%s\">already in repository</a>"
+#~ msgstr "Obraz <a href=\"%s\">jest już w repozytorium</a>."
+
+#~ msgid "Flickr URL"
+#~ msgstr "URL z Flickra"
index 56f5665..092933e 100644 (file)
        <div class="card-body">
 
 <form method="post">{% csrf_token %}
        <div class="card-body">
 
 <form method="post">{% csrf_token %}
-<input type="hidden" name='form_id' value="flickr" />
+<input type="hidden" name='form_id' value="import" />
 <table class='editable'><tbody>
     {% bootstrap_form ff %}
     {% buttons %}
 <table class='editable'><tbody>
     {% bootstrap_form ff %}
     {% buttons %}
-    <button class="btn btn-primary" type="submit">{% trans "Load from Flickr" %}</button>
+    <button class="btn btn-primary" type="submit">{% trans "Load from WikiCommons, MNW, Flickr" %}</button>
     {% endbuttons %}
 </tbody></table>
 </form>
     {% endbuttons %}
 </tbody></table>
 </form>
index d13554f..3bf3e9f 100644 (file)
@@ -1,17 +1,16 @@
 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+import csv
+from io import StringIO
 import json
 import re
 from urllib.request import FancyURLopener
 
 import json
 import re
 from urllib.request import FancyURLopener
 
-from django.contrib.sites.models import Site
-
-
 class URLOpener(FancyURLopener):
     @property
     def version(self):
 class URLOpener(FancyURLopener):
     @property
     def version(self):
-        return 'FNP Redakcja (http://%s)' % Site.objects.get_current()
+        return 'FNP Redakcja'
 
 
 class FlickrError(Exception):
 
 
 class FlickrError(Exception):
@@ -69,3 +68,77 @@ def get_flickr_data(url):
         'title': title,
         'download_url': download_url,
     }
         'title': title,
         'download_url': download_url,
     }
+
+
+def get_wikimedia_data(url):
+    """
+    >>> get_wikimedia_data('https://commons.wikimedia.org/wiki/File:Valdai_IverskyMon_asv2018_img47.jpg')
+    {'title': 'Valdai IverskyMon asv2018 img47', 'author': 'A.Savin', 'source_url': 'https://commons.wikimedia.org/wiki/File:Valdai_IverskyMon_asv2018_img47.jpg', 'download_url': 'https://upload.wikimedia.org/wikipedia/commons/4/43/Valdai_IverskyMon_asv2018_img47.jpg', 'license_url': 'http://artlibre.org/licence/lal/en', 'license_name': 'FAL'}
+
+    """
+    file_name = url.rsplit('/', 1)[-1].rsplit(':', 1)[-1]
+    d = json.loads(URLOpener().open('https://commons.wikimedia.org/w/api.php?action=query&titles=File:{}&prop=imageinfo&iiprop=url|user|extmetadata&iimetadataversion=latest&format=json'.format(file_name)).read().decode('utf-8'))
+    d = list(d['query']['pages'].values())[0]['imageinfo'][0]
+    ext = d['extmetadata']
+
+    meta = {
+        'title': ext['ObjectName']['value'],
+        'author': d['user'],
+        'source_url': d['descriptionurl'],
+        'download_url': d['url'],
+        'license_url': ext['LicenseUrl']['value'],
+        'license_name': ext['LicenseShortName']['value'],
+    }
+    
+    return meta
+
+
+def get_mnw_data(url):
+    """
+    >>> get_mnw_data('https://cyfrowe.mnw.art.pl/pl/katalog/794032')
+    {'title': 'Pejzaż z podwójnym świerkiem', 'author': 'nieznany, Altdorfer, Albrecht (ca 1480-1538)', 'source_url': 'https://cyfrowe.mnw.art.pl/pl/katalog/794032', 'download_url': 'https://cyfrowe-cdn.mnw.art.pl/upload/multimedia/49/58/49583b3e9b23e2d25f372fe6021ae220.jpg', 'license_url': 'https://pl.wikipedia.org/wiki/Domena_publiczna', 'license_name': 'DOMENA PUBLICZNA'}
+
+    """
+    nr = url.rsplit('/', 1)[-1]
+    d = list(
+        csv.DictReader(
+            StringIO(
+                URLOpener().open(
+                    'https://cyfrowe-api.mnw.art.pl/api/object/{}/csv'.format(nr)
+                ).read().decode('utf-8')
+            )
+        )
+    )[0]
+
+    authors = []
+    i = 0
+    while f'authors.{i}.name' in d:
+        authors.append(d[f'authors.{i}.name'])
+        i += 1
+
+    license_url = d['copyrights.0.link']
+    license_name = d['copyrights.0.name']
+    if license_name == 'DOMENA PUBLICZNA':
+        license_name = 'domena publiczna'
+        license_url = ''
+        
+    return {
+        'title': d['title'],
+        'author': ', '.join(authors),
+        'source_url': url,
+        'download_url': 'https://cyfrowe-cdn.mnw.art.pl/upload/multimedia/{}.{}'.format(
+            d['image.filePath'],
+            d['image.extension'],
+        ),
+        'license_url': license_url,
+        'license_name': license_name,
+    }
+
+
+def get_import_data(url):
+    if re.match(r'(https?://)?(www\.|secure\.)?flickr\.com/', url):
+        return get_flickr_data(url)
+    if re.match(r'(https?://)?(commons|upload)\.wikimedia\.org/', url):
+        return get_wikimedia_data(url)
+    if re.match(r'(https?://)?cyfrowe\.mnw\.art\.pl/', url):
+        return get_mnw_data(url)
index bfc3ee3..a7b96fb 100644 (file)
@@ -133,8 +133,8 @@ def image_list(request):
 def add_image(request):
     form = ff = None
     if request.method == 'POST':
 def add_image(request):
     form = ff = None
     if request.method == 'POST':
-        if request.POST.get('form_id') == 'flickr':
-            ff = forms.FlickrForm(request.POST)
+        if request.POST.get('form_id') == 'import':
+            ff = forms.ImportForm(request.POST)
             if ff.is_valid():
                 form = forms.ImageAddForm(ff.cleaned_data)
         else:
             if ff.is_valid():
                 form = forms.ImageAddForm(ff.cleaned_data)
         else:
@@ -145,7 +145,7 @@ def add_image(request):
     if form is None:
         form = forms.ImageAddForm()
     if ff is None:
     if form is None:
         form = forms.ImageAddForm()
     if ff is None:
-        ff = forms.FlickrForm()
+        ff = forms.ImportForm()
     return render(request, 'cover/add_image.html', {
             'form': form,
             'ff': ff,
     return render(request, 'cover/add_image.html', {
             'form': form,
             'ff': ff,