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:
69 opts = translator.get_options_for_model(type(self))
73 if attname in opts.fields:
74 tfields = opts.fields[attname]
76 yield tf.name, tf.language
79 yield attname, settings.LANGUAGE_CODE
81 def wikidata_populate_attribute(self, client, entity, attname, wd, save):
82 for fieldname, lang in self.wikidata_fields_for_attribute(attname):
83 self.wikidata_populate_field(client, entity, fieldname, wd, save, lang)
85 def save(self, **kwargs):
86 am_new = self.pk is None
89 if am_new and self.wikidata and hasattr(self, "Wikidata"):
90 self.wikidata_populate()
92 kwargs.update(force_insert=False, force_update=True)
93 super().save(**kwargs)
95 def set_field_from_wikidata(self, attname, wdvalue, save, language='pl'):
98 # Find out what this model field is
99 model_field = self._meta.get_field(attname)
100 if isinstance(model_field, models.ForeignKey):
101 rel_model = model_field.related_model
102 if issubclass(rel_model, WikidataModel):
103 label = wdvalue.label.get(language, str(wdvalue.label))
105 wdvalue = rel_model.objects.get(wikidata=wdvalue.id)
106 except rel_model.DoesNotExist:
107 wdvalue = rel_model(wikidata=wdvalue.id)
110 wdvalue._wikidata_label = label
111 setattr(self, attname, wdvalue)
112 elif isinstance(model_field, models.ManyToManyField):
113 rel_model = model_field.related_model
114 if issubclass(rel_model, WikidataModel):
115 label = wdvalue.label.get(language, str(wdvalue.label))
117 wdvalue = rel_model.objects.get(wikidata=wdvalue.id)
118 except rel_model.DoesNotExist:
119 wdvalue = rel_model(wikidata=wdvalue.id)
122 wdvalue._wikidata_label = label
123 getattr(self, attname).set([wdvalue])
125 # How to get original title?
126 if isinstance(wdvalue, date):
127 if isinstance(model_field, models.IntegerField):
128 wdvalue = wdvalue.year
129 elif not isinstance(wdvalue, str):
130 wdvalue = wdvalue.label.get(language, str(wdvalue.label))
131 setattr(self, attname, wdvalue)
133 def wikidata_link(self):
136 '<a href="https://www.wikidata.org/wiki/{wd}" target="_blank">{wd}</a>',
142 wikidata_link.admin_order_field = "wikidata"
145 class WikidataAdminMixin:
147 css = {"screen": ("catalogue/wikidata_admin.css",)}
148 js = ("catalogue/wikidata_admin.js",)
150 def save_related(self, request, form, formsets, change):
151 super().save_related(request, form, formsets, change)