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)