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