Fix popup map panning
[wolnelektury.git] / src / references / models.py
1 import json
2 import urllib.parse
3 from django.db import models
4 from wikidata.client import Client
5
6
7 class Entity(models.Model):
8     WIKIDATA_PREFIX = 'https://www.wikidata.org/wiki/'
9     WIKIDATA_IMAGE = 'P18'
10     WIKIDATA_COORDINATE_LOCATION = 'P625'
11     WIKIDATA_EARTH = 'Q2'
12     WIKIDATA_IMAGE_QUERY = './w/api.php?action=query&titles={}&format=json&prop=imageinfo&iiprop=url&iiurlwidth=240&iiurlheight=200'
13
14     uri = models.CharField(max_length=255, unique=True, db_index=True)
15     label = models.CharField(max_length=1024, blank=True)
16     description = models.CharField(max_length=2048, blank=True)
17     wikipedia_link = models.CharField(max_length=1024, blank=True)
18     lat = models.FloatField(null=True, blank=True)
19     lon = models.FloatField(null=True, blank=True)
20     images = models.TextField(blank=True)
21
22     @property
23     def is_interesting(self):
24         return self.wikipedia_link or (self.lat and self.lon) or len(self.images)>2
25     
26     def populate(self):
27         if self.uri.startswith(self.WIKIDATA_PREFIX):
28             self.populate_from_wikidata(
29                 self.uri[len(self.WIKIDATA_PREFIX):]
30             )
31
32     def populate_from_wikidata(self, wikidata_id):
33         client = Client()
34         client.opener.addheaders = [(
35             'User-Agent', 'Wolne Lektury / https://wolnelektury.pl / Python-wikidata'
36         )]
37         entity = client.get(wikidata_id)
38
39         self.label = entity.label.get('pl', entity.label.get('mul', entity.label)) or ''
40         self.description = entity.description.get('pl', entity.description.get('mul', entity.description)) or ''
41         sitelinks = entity.attributes.get('sitelinks', {})
42         self.wikipedia_link = sitelinks.get('plwiki', sitelinks.get('enwiki', {})).get('url') or ''
43
44         location_prop = client.get(self.WIKIDATA_COORDINATE_LOCATION)
45         location = entity.get(location_prop)
46         if location and location.globe.id == self.WIKIDATA_EARTH:
47             self.lat = location.latitude
48             self.lon = location.longitude
49
50         images = entity.getlist(client.get(self.WIKIDATA_IMAGE))
51         image_data_list = []
52         for image in images:
53             image_data = {
54                 "url": image.image_url,
55                 "page": image.page_url,
56                 "resolution": image.image_resolution,
57             }
58
59             result = client.request(
60                 self.WIKIDATA_IMAGE_QUERY.format(
61                     urllib.parse.quote(image.title)
62                 )
63             )
64
65             result_data = next(iter(result['query']['pages'].values()))['imageinfo'][0]
66             image_data['thumburl'] = result_data['thumburl']
67             image_data['thumbresolution'] = [
68                 result_data['thumbwidth'],
69                 result_data['thumbheight']
70             ]
71             if 'responsiveUrls' in result_data:
72                 image_data['responsiveUrls'] = result_data['responsiveUrls']
73
74             image_data_list.append(image_data)
75
76         self.images = json.dumps(image_data_list)
77
78     def get_images(self):
79         return json.loads(self.images)
80
81     def get_pretty_coords(self):
82         def to_minutes(n):
83             coo = []
84             coo.append(int(n))
85             n -= coo[-1]
86             n *= 60
87             coo.append(int(n))
88             n -= coo[-1]
89             n *= 60
90             coo.append(n)
91             return coo
92
93         if not self.lat or not self.lon:
94             return ''
95
96         lat, latm, lon, lonm = self.lat, 'N', self.lon, 'E'
97         if lat < 0:
98             latm = 'S'
99             lat = -lat
100         if lon < 0:
101             lonm = 'W'
102             lon = -lon
103         
104         return '%d°%d\'%d"%s %d°%d\'%d"%s' % (
105             *to_minutes(lat),
106             latm,
107             *to_minutes(lon),
108             lonm,
109         )
110
111     def get_json(self):
112         data = {
113             "id": self.id,
114             "images": self.get_images(),
115             "label": self.label,
116             "description": self.description,
117             "wikipedia_link": self.wikipedia_link,
118         }
119         if self.lat and self.lon:
120             data['location'] = [self.lat, self.lon]
121         return json.dumps(data)
122     
123
124 class Reference(models.Model):
125     book = models.ForeignKey('catalogue.Book', models.CASCADE)
126     entity = models.ForeignKey(Entity, models.CASCADE)
127
128     class Meta:
129         unique_together = (('book', 'entity'),)
130
131
132 class Occurence(models.Model):
133     reference = models.ForeignKey(Reference, models.CASCADE)
134     section = models.IntegerField()
135     html = models.TextField()
136
137     class Meta:
138         ordering = ('section',)