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.db import models
6 from django.db.models.signals import m2m_changed
7 from django.utils.html import format_html
8 from django.utils.translation import gettext_lazy as _
9 from django.dispatch import receiver
10 from wikidata.client import Client
11 from wikidata.datavalue import DatavalueError
14 class WikidataMixin(models.Model):
15 wikidata = models.CharField(
18 help_text=_('If you have a Wikidata ID, like "Q1337", enter it and save.'),
24 def wikidata_populate(self, client, entity, attname, wd):
25 model_field = self._meta.get_field(attname)
26 if isinstance(model_field, models.ManyToManyField):
27 if getattr(self, attname).all().exists():
30 if getattr(self, attname):
34 if wd == "description":
35 wdvalue = entity.description.get("pl", str(entity.description))
37 wdvalue = entity.label.get("pl", str(entity.label))
40 wdvalue = entity.get(client.get(wd))
41 except DatavalueError:
44 self.set_field_from_wikidata(attname, wdvalue)
46 def save(self, **kwargs):
48 if self.wikidata and hasattr(self, "Wikidata"):
49 Wikidata = type(self).Wikidata
51 # Probably should getlist
52 entity = client.get(self.wikidata)
53 for attname in dir(Wikidata):
54 if attname.startswith("_"):
56 wd = getattr(Wikidata, attname)
58 self.wikidata_populate(client, entity, attname, wd)
59 if hasattr(Wikidata, '_supplement'):
60 for attname, wd in Wikidata._supplement(self):
61 self.wikidata_populate(client, entity, attname, wd)
64 kwargs.update(force_insert=False, force_update=True)
65 super().save(**kwargs)
67 def set_field_from_wikidata(self, attname, wdvalue):
70 # Find out what this model field is
71 model_field = self._meta.get_field(attname)
72 if isinstance(model_field, models.ForeignKey):
73 rel_model = model_field.related_model
74 if issubclass(rel_model, WikidataMixin):
75 # welp, we can try and find by WD identifier.
76 wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
77 setattr(self, attname, wdvalue)
78 elif isinstance(model_field, models.ManyToManyField):
79 rel_model = model_field.related_model
80 if issubclass(rel_model, WikidataMixin):
81 wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
82 getattr(self, attname).set([wdvalue])
84 # How to get original title?
85 if isinstance(wdvalue, date):
86 if isinstance(model_field, models.IntegerField):
87 wdvalue = wdvalue.year
88 elif not isinstance(wdvalue, str):
89 wdvalue = wdvalue.label.get("pl", str(wdvalue.label))
90 setattr(self, attname, wdvalue)
92 def wikidata_link(self):
95 '<a href="https://www.wikidata.org/wiki/{wd}" target="_blank">{wd}</a>',
101 wikidata_link.admin_order_field = "wikidata"
104 class WikidataAdminMixin:
105 def save_related(self, request, form, formsets, change):
106 super().save_related(request, form, formsets, change)