1 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
 
   2 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 
   4 from datetime import date
 
   5 from django.conf import settings
 
   6 from django.db import models
 
   7 from django.db.models.signals import m2m_changed
 
   8 from django.utils.html import format_html
 
   9 from django.utils.translation import gettext_lazy as _
 
  10 from django.dispatch import receiver
 
  11 from wikidata.client import Client
 
  12 from wikidata.datavalue import DatavalueError
 
  13 from modeltranslation.translator import translator
 
  14 from modeltranslation.settings import AVAILABLE_LANGUAGES
 
  17 class WikidataModel(models.Model):
 
  18     wikidata = models.CharField(
 
  21         help_text=_('If you have a Wikidata ID, like "Q1337", enter it and save.'),
 
  27     def wikidata_populate_field(self, client, entity, attname, wd, save, lang):
 
  28         model_field = self._meta.get_field(attname)
 
  29         if isinstance(model_field, models.ManyToManyField):
 
  30             if getattr(self, attname).all().exists():
 
  33             if getattr(self, attname):
 
  37         if wd == "description":
 
  38             wdvalue = entity.description.get(lang, str(entity.description))
 
  40             wdvalue = entity.label.get(lang, str(entity.label))
 
  44                 wdvalue = entity.get(client.get(wd))
 
  45             except DatavalueError:
 
  48         self.set_field_from_wikidata(attname, wdvalue, save=save)
 
  50     def wikidata_populate(self, save=True):
 
  51         Wikidata = type(self).Wikidata
 
  53         # Probably should getlist
 
  54         entity = client.get(self.wikidata)
 
  55         for attname in dir(Wikidata):
 
  56             if attname.startswith("_"):
 
  58             wd = getattr(Wikidata, attname)
 
  60             self.wikidata_populate_attribute(client, entity, attname, wd, save=save)
 
  61         if hasattr(Wikidata, '_supplement'):
 
  62             for attname, wd in Wikidata._supplement(self):
 
  63                 self.wikidata_populate_attribute(client, entity, attname, wd, save=save)
 
  65     def wikidata_fields_for_attribute(self, attname):
 
  66         field = getattr(type(self), attname)
 
  67         if type(self) in translator._registry:
 
  68             opts = translator.get_options_for_model(type(self))
 
  69             if attname in opts.fields:
 
  70                 tfields = opts.fields[attname]
 
  72                     yield tf.name, tf.language
 
  75         yield attname, settings.LANGUAGE_CODE
 
  77     def wikidata_populate_attribute(self, client, entity, attname, wd, save):
 
  78         for fieldname, lang in self.wikidata_fields_for_attribute(attname):
 
  79             self.wikidata_populate_field(client, entity, fieldname, wd, save, lang)
 
  81     def save(self, **kwargs):
 
  82         am_new = self.pk is None
 
  85         if am_new and self.wikidata and hasattr(self, "Wikidata"):
 
  86             self.wikidata_populate()
 
  88         kwargs.update(force_insert=False, force_update=True)
 
  89         super().save(**kwargs)
 
  91     def set_field_from_wikidata(self, attname, wdvalue, save, language='pl'):
 
  94         # Find out what this model field is
 
  95         model_field = self._meta.get_field(attname)
 
  96         if isinstance(model_field, models.ForeignKey):
 
  97             rel_model = model_field.related_model
 
  98             if issubclass(rel_model, WikidataModel):
 
  99                 label = wdvalue.label.get(language, str(wdvalue.label))
 
 101                     wdvalue = rel_model.objects.get(wikidata=wdvalue.id)
 
 102                 except rel_model.DoesNotExist:
 
 103                     wdvalue = rel_model(wikidata=wdvalue.id)
 
 106                 wdvalue._wikidata_label = label
 
 107                 setattr(self, attname, wdvalue)
 
 108         elif isinstance(model_field, models.ManyToManyField):
 
 109             rel_model = model_field.related_model
 
 110             if issubclass(rel_model, WikidataModel):
 
 111                 label = wdvalue.label.get(language, str(wdvalue.label))
 
 113                     wdvalue = rel_model.objects.get(wikidata=wdvalue.id)
 
 114                 except rel_model.DoesNotExist:
 
 115                     wdvalue = rel_model(wikidata=wdvalue.id)
 
 118                 wdvalue._wikidata_label = label
 
 119                 getattr(self, attname).set([wdvalue])
 
 121             # How to get original title?
 
 122             if isinstance(wdvalue, date):
 
 123                 if isinstance(model_field, models.IntegerField):
 
 124                     wdvalue = wdvalue.year
 
 125             elif not isinstance(wdvalue, str):
 
 126                 wdvalue = wdvalue.label.get(language, str(wdvalue.label))
 
 127             setattr(self, attname, wdvalue)
 
 129     def wikidata_link(self):
 
 132                 '<a href="https://www.wikidata.org/wiki/{wd}" target="_blank">{wd}</a>',
 
 138     wikidata_link.admin_order_field = "wikidata"
 
 141 class WikidataAdminMixin:
 
 143         css = {"screen": ("catalogue/wikidata_admin.css",)}
 
 144         js = ("catalogue/wikidata_admin.js",)
 
 146     def save_related(self, request, form, formsets, change):
 
 147         super().save_related(request, form, formsets, change)