c6885705d0f3fba049c46f61162168a9ed835858
[redakcja.git] / src / catalogue / wikidata.py
1 from datetime import date
2 from django.db import models
3 from django.db.models.signals import m2m_changed
4 from django.utils.html import format_html
5 from django.utils.translation import gettext_lazy as _
6 from django.dispatch import receiver
7 from wikidata.client import Client
8 from wikidata.datavalue import DatavalueError
9
10
11 class WikidataMixin(models.Model):
12     wikidata = models.CharField(
13         max_length=255,
14         null=True,
15         blank=True,
16         unique=True,
17         help_text=_('If you have a Wikidata ID, like "Q1337", enter it and save.'),
18     )
19
20     class Meta:
21         abstract = True
22
23     def save(self, **kwargs):
24         super().save()
25         if self.wikidata and hasattr(self, "Wikidata"):
26             Wikidata = type(self).Wikidata
27             client = Client()
28             # Probably should getlist
29             entity = client.get(self.wikidata)
30             for attname in dir(Wikidata):
31                 if attname.startswith("_"):
32                     continue
33                 wd = getattr(Wikidata, attname)
34
35                 model_field = self._meta.get_field(attname)
36                 if isinstance(model_field, models.ManyToManyField):
37                     if getattr(self, attname).all().exists():
38                         continue
39                 else:
40                     if getattr(self, attname):
41                         continue
42
43                 wdvalue = None
44                 if wd == "description":
45                     wdvalue = entity.description.get("pl", str(entity.description))
46                 elif wd == "label":
47                     wdvalue = entity.label.get("pl", str(entity.label))
48                 else:
49                     try:
50                         wdvalue = entity.get(client.get(wd))
51                     except DatavalueError:
52                         pass
53
54                 self.set_field_from_wikidata(attname, wdvalue)
55
56         kwargs.update(force_insert=False, force_update=True)
57         super().save(**kwargs)
58
59     def set_field_from_wikidata(self, attname, wdvalue):
60         if not wdvalue:
61             return
62         # Find out what this model field is
63         model_field = self._meta.get_field(attname)
64         if isinstance(model_field, models.ForeignKey):
65             rel_model = model_field.related_model
66             if issubclass(rel_model, WikidataMixin):
67                 # welp, we can try and find by WD identifier.
68                 wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
69                 setattr(self, attname, wdvalue)
70         elif isinstance(model_field, models.ManyToManyField):
71             rel_model = model_field.related_model
72             if issubclass(rel_model, WikidataMixin):
73                 wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
74                 getattr(self, attname).set([wdvalue])
75         else:
76             # How to get original title?
77             if isinstance(wdvalue, date):
78                 if isinstance(model_field, models.IntegerField):
79                     wdvalue = wdvalue.year
80             elif not isinstance(wdvalue, str):
81                 wdvalue = wdvalue.label.get("pl", str(wdvalue.label))
82             setattr(self, attname, wdvalue)
83
84
85 class WikidataAdminMixin:
86     def save_related(self, request, form, formsets, change):
87         super().save_related(request, form, formsets, change)
88         form.instance.save()
89
90     def wikidata_link(self, obj):
91         if obj.wikidata:
92             return format_html('<a href="https://www.wikidata.org/wiki/{wd}" target="_blank">{wd}</a>', wd=obj.wikidata)
93         else:
94             return ''
95     wikidata_link.admin_order_field = 'wikidata'