from admin_ordering.models import OrderableModel
from wikidata.client import Client
from .constants import WIKIDATA
-from .wikidata import WikidataMixin
+from .wikidata import WikidataModel
-class Author(WikidataMixin, models.Model):
+class Author(WikidataModel):
slug = models.SlugField(max_length=255, null=True, blank=True, unique=True)
first_name = models.CharField(_("first name"), max_length=255, blank=True)
last_name = models.CharField(_("last name"), max_length=255, blank=True)
book = models.ForeignKey('Book', models.CASCADE)
-class Category(WikidataMixin, models.Model):
+class Category(WikidataModel):
name = models.CharField(_("name"), max_length=255)
slug = models.SlugField(max_length=255, unique=True)
verbose_name_plural = _('kinds')
-class Book(WikidataMixin, models.Model):
+class Book(WikidataModel):
slug = models.SlugField(max_length=255, blank=True, null=True, unique=True)
authors = models.ManyToManyField(Author, blank=True, verbose_name=_("authors"))
translators = models.ManyToManyField(
return (decimal.Decimal(book.estimated_chars) / 1800 * self.per_normpage).quantize(decimal.Decimal('1.00'), rounding=decimal.ROUND_HALF_UP)
-class Place(WikidataMixin, models.Model):
+class Place(WikidataModel):
name = models.CharField(_('name'), max_length=255, blank=True)
locative = models.CharField(_('locative'), max_length=255, blank=True, help_text=_('in…'))
# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
+from django.apps import apps
from django.db.models import Prefetch
+from django.http import Http404
+from django.utils.formats import localize_input
from django.contrib.auth.models import User
from django.views.generic import DetailView, TemplateView
from . import models
import documents.models
from rest_framework.generics import ListAPIView
from rest_framework.filters import SearchFilter
+from rest_framework.permissions import IsAdminUser
+from rest_framework.response import Response
+from rest_framework.views import APIView
from rest_framework import serializers
class serializer_class(serializers.Serializer):
label = serializers.CharField(source='wluri')
+
+class WikidataView(APIView):
+ permission_classes = [IsAdminUser]
+
+ def get_object(self, model, qid, save):
+ try:
+ Model = apps.get_model('catalogue', model)
+ except LookupError:
+ raise Http404
+ if not issubclass(Model, models.WikidataModel):
+ raise Http404
+
+ obj = Model.objects.filter(wikidata=qid).first()
+ if obj is None:
+ obj = Model(wikidata=qid)
+ if not obj.pk and save:
+ obj.save()
+ else:
+ obj.wikidata_populate(save=False)
+ d = {
+ "id": obj.pk,
+ }
+ for attname in dir(Model.Wikidata):
+ if attname.startswith("_"):
+ continue
+ for fieldname, lang in obj.wikidata_fields_for_attribute(attname):
+ d[fieldname] = getattr(obj, fieldname)
+
+ if isinstance(d[fieldname], models.WikidataModel):
+ d[attname] = {
+ "model": type(d[fieldname])._meta.model_name,
+ "wd": d[fieldname].wikidata,
+ "label": str(d[fieldname]) or d[fieldname]._wikidata_label,
+ }
+ else:
+ d[fieldname] = localize_input(d[fieldname])
+ return Response(d)
+
+ def get(self, request, model, qid):
+ return self.get_object(model, qid, save=False)
+
+ def post(self, request, model, qid):
+ return self.get_object(model, qid, save=True)
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from datetime import date
+from django.conf import settings
from django.db import models
from django.db.models.signals import m2m_changed
from django.utils.html import format_html
from django.dispatch import receiver
from wikidata.client import Client
from wikidata.datavalue import DatavalueError
+from modeltranslation.translator import translator
+from modeltranslation.settings import AVAILABLE_LANGUAGES
-class WikidataMixin(models.Model):
+class WikidataModel(models.Model):
wikidata = models.CharField(
max_length=255,
blank=True,
class Meta:
abstract = True
-
- def wikidata_populate(self, client, entity, attname, wd):
+
+ def wikidata_populate_field(self, client, entity, attname, wd, save, lang):
model_field = self._meta.get_field(attname)
if isinstance(model_field, models.ManyToManyField):
if getattr(self, attname).all().exists():
wdvalue = None
if wd == "description":
- wdvalue = entity.description.get("pl", str(entity.description))
+ wdvalue = entity.description.get(lang, str(entity.description))
elif wd == "label":
- wdvalue = entity.label.get("pl", str(entity.label))
+ wdvalue = entity.label.get(lang, str(entity.label))
else:
try:
+ # TODO: lang?
wdvalue = entity.get(client.get(wd))
except DatavalueError:
pass
- self.set_field_from_wikidata(attname, wdvalue)
-
+ self.set_field_from_wikidata(attname, wdvalue, save=save)
+
+ def wikidata_populate(self, save=True):
+ Wikidata = type(self).Wikidata
+ client = Client()
+ # Probably should getlist
+ entity = client.get(self.wikidata)
+ for attname in dir(Wikidata):
+ if attname.startswith("_"):
+ continue
+ wd = getattr(Wikidata, attname)
+
+ self.wikidata_populate_attribute(client, entity, attname, wd, save=save)
+ if hasattr(Wikidata, '_supplement'):
+ for attname, wd in Wikidata._supplement(self):
+ self.wikidata_populate_attribute(client, entity, attname, wd, save=save)
+
+ def wikidata_fields_for_attribute(self, attname):
+ field = getattr(type(self), attname)
+ if type(self) in translator._registry:
+ opts = translator.get_options_for_model(type(self))
+ if attname in opts.fields:
+ tfields = opts.fields[attname]
+ for tf in tfields:
+ yield tf.name, tf.language
+ return
+
+ yield attname, settings.LANGUAGE_CODE
+
+ def wikidata_populate_attribute(self, client, entity, attname, wd, save):
+ for fieldname, lang in self.wikidata_fields_for_attribute(attname):
+ self.wikidata_populate_field(client, entity, fieldname, wd, save, lang)
+
def save(self, **kwargs):
- super().save()
- if self.wikidata and hasattr(self, "Wikidata"):
- Wikidata = type(self).Wikidata
- client = Client()
- # Probably should getlist
- entity = client.get(self.wikidata)
- for attname in dir(Wikidata):
- if attname.startswith("_"):
- continue
- wd = getattr(Wikidata, attname)
-
- self.wikidata_populate(client, entity, attname, wd)
- if hasattr(Wikidata, '_supplement'):
- for attname, wd in Wikidata._supplement(self):
- self.wikidata_populate(client, entity, attname, wd)
+ am_new = self.pk is None
+ super().save()
+ if am_new and self.wikidata and hasattr(self, "Wikidata"):
+ self.wikidata_populate()
kwargs.update(force_insert=False, force_update=True)
super().save(**kwargs)
- def set_field_from_wikidata(self, attname, wdvalue):
+ def set_field_from_wikidata(self, attname, wdvalue, save, language='pl'):
if not wdvalue:
return
# Find out what this model field is
model_field = self._meta.get_field(attname)
if isinstance(model_field, models.ForeignKey):
rel_model = model_field.related_model
- if issubclass(rel_model, WikidataMixin):
- # welp, we can try and find by WD identifier.
- wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
+ if issubclass(rel_model, WikidataModel):
+ label = wdvalue.label.get(language, str(wdvalue.label))
+ try:
+ wdvalue = rel_model.objects.get(wikidata=wdvalue.id)
+ except rel_model.DoesNotExist:
+ wdvalue = rel_model(wikidata=wdvalue.id)
+ if save:
+ wdvalue.save()
+ wdvalue._wikidata_label = label
setattr(self, attname, wdvalue)
elif isinstance(model_field, models.ManyToManyField):
rel_model = model_field.related_model
- if issubclass(rel_model, WikidataMixin):
- wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
+ if issubclass(rel_model, WikidataModel):
+ label = wdvalue.label.get(language, str(wdvalue.label))
+ try:
+ wdvalue = rel_model.objects.get(wikidata=wdvalue.id)
+ except rel_model.DoesNotExist:
+ wdvalue = rel_model(wikidata=wdvalue.id)
+ if save:
+ wdvalue.save()
+ wdvalue._wikidata_label = label
getattr(self, attname).set([wdvalue])
else:
# How to get original title?
if isinstance(model_field, models.IntegerField):
wdvalue = wdvalue.year
elif not isinstance(wdvalue, str):
- wdvalue = wdvalue.label.get("pl", str(wdvalue.label))
+ wdvalue = wdvalue.label.get(language, str(wdvalue.label))
setattr(self, attname, wdvalue)
def wikidata_link(self):
class WikidataAdminMixin:
+ class Media:
+ css = {"screen": ("catalogue/wikidata_admin.css",)}
+ js = ("catalogue/wikidata_admin.js",)
+
def save_related(self, request, form, formsets, change):
super().save_related(request, form, formsets, change)
form.instance.save()