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