plwiki for books
[redakcja.git] / src / isbn / models.py
1 from datetime import date
2 from django.apps import apps
3 from django.db import models
4 from lxml import etree
5 import requests
6 from .product_forms import FORMS
7
8
9 class IsbnPool(models.Model):
10     PURPOSE_GENERAL = 'GENERAL'
11     PURPOSE_WL = 'WL'
12     PURPOSE_CHOICES = (
13         (PURPOSE_WL, 'Wolne Lektury'),
14         (PURPOSE_GENERAL, 'Ogólne'),
15     )
16
17     prefix = models.CharField(max_length=10)
18     suffix_from = models.IntegerField()
19     suffix_to = models.IntegerField()
20     ref_from = models.IntegerField()
21     purpose = models.CharField(max_length=8, choices=PURPOSE_CHOICES)
22
23     def __str__(self):
24         return '-'.join((
25             self.prefix[:3],
26             self.prefix[3:5],
27             self.prefix[5:],
28             'X' * (12 - len(self.prefix)),
29             'X'
30         ))
31
32     @staticmethod
33     def check_digit(prefix12):
34         digits = [int(d) for d in prefix12]
35         return str((-sum(digits[0::2]) + 7 * sum(digits[1::2])) % 10)
36     
37     def get_code(self, suffix, dashes=False):
38         suffix_length = 12 - len(self.prefix)
39         suffix_str = f'{suffix:0{suffix_length}d}'
40         prefix12 = self.prefix + suffix_str
41         check_digit = self.check_digit(prefix12)
42         if dashes:
43             isbn = '-'.join((
44                 self.prefix[:3],
45                 self.prefix[3:5],
46                 self.prefix[5:],
47                 suffix_str,
48                 check_digit
49             ))
50         else:
51             isbn = ''.join((
52                 prefix12, check_digit
53             ))
54         return isbn
55
56     
57     @property
58     def size(self):
59         return self.suffix_to - self.suffix_from + 1
60
61     @property
62     def entries(self):
63         return self.isbn_set.count()
64     
65     @property
66     def fill_percentage(self):
67         return 100 * self.entries / self.size
68
69     def bn_record_id_for(self, suffix):
70         return self.ref_from + suffix
71     
72     def import_all_bn_data(self):
73         for suffix in range(self.suffix_from, self.suffix_to + 1):
74             print(suffix)
75             self.import_bn_data_for(suffix)
76     
77     def import_bn_data_for(self, suffix):
78         record_id = self.bn_record_id_for(suffix)
79         content = requests.get(
80             f'https://e-isbn.pl/IsbnWeb/record/export_onix.xml?record_id={record_id}').content
81         elem = etree.fromstring(content)
82         product = elem.find('{http://ns.editeur.org/onix/3.0/reference}Product')
83         if product is not None:
84             isbn, created = self.isbn_set.get_or_create(
85                 suffix=suffix
86             )
87             isbn.bn_data = etree.tostring(product, pretty_print=True, encoding='unicode')
88             isbn.save(update_fields=['bn_data'])
89
90
91 class Isbn(models.Model):
92     pool = models.ForeignKey(IsbnPool, models.PROTECT)
93     suffix = models.IntegerField()
94     datestamp = models.DateField(blank=True, null=True)
95     book = models.ForeignKey(
96         'catalogue.Book', models.PROTECT, null=True, blank=True
97     )
98     form = models.CharField(
99         max_length=32, choices=[
100             (form, form)
101             for form, config in FORMS
102         ], blank=True
103     )
104     bn_data = models.TextField(blank=True)
105     wl_data = models.TextField(blank=True)
106     notes = models.TextField(blank=True)
107
108     class Meta:
109         ordering = ['pool', 'suffix']
110         unique_together = ['pool', 'suffix']
111
112     def __str__(self):
113         return self.get_code(True)
114         
115     def get_code(self, dashes=True):
116         return self.pool.get_code(self.suffix, dashes=dashes)
117
118     @classmethod
119     def get_for_book(cls, book, form):
120         isbn = cls.objects.filter(book=book, form=form).first()
121         if isbn is None:
122             return cls.assign(book, form)
123         return isbn
124
125     @classmethod
126     def assign(cls, book, form):
127         pool = IsbnPool.objects.filter(purpose=IsbnPool.PURPOSE_WL).first()
128         suffix = pool.isbn_set.aggregate(s=models.Max('suffix'))['s'] + 1
129         assert suffix <= pool.suffix_to
130         return pool.isbn_set.create(
131             book=book, form=form, suffix=suffix, datestamp=date.today()
132         )
133
134     @classmethod
135     def formats_from_document(cls, document):
136         # This is a document
137         meta = document.wldocument(librarian2=True).meta
138         is_parent = len(meta.parts)
139         formats = []
140         for form, config in FORMS:
141             if config.book and (not is_parent or config.parent):
142                 formats.append((
143                     form,
144                     getattr(meta, f'isbn_{form}')
145                 ))
146         return formats
147
148     @classmethod
149     def import_from_documents(cls):
150         Book = apps.get_model('documents', 'Book')
151         for book in Book.objects.all():
152             try:
153                 catalogue_book = book.catalogue_book
154                 if catalogue_book is None:
155                     continue
156             except:
157                 continue
158             try:
159                 meta = book.wldocument(publishable=False, librarian2=True).meta
160             except:
161                 continue
162             for form in ('html', 'txt', 'pdf', 'epub', 'mobi'):
163                 isbn = getattr(meta, f'isbn_{form}')
164                 if isbn is not None:
165                     parts = isbn.split('-')
166                     assert parts[0] == 'ISBN'
167                     suffix = int(parts[-2])
168                     prefix = ''.join(parts[1:-2])
169                     pool = IsbnPool.objects.get(prefix=prefix)
170                     isbn, created = pool.isbn_set.get_or_create(
171                         suffix=suffix,
172                     )
173                     add_note = False
174                     if isbn.book is None:
175                         isbn.book = catalogue_book
176                     elif isbn.book != catalogue_book:
177                         add_note = True
178                     if not isbn.form:
179                         isbn.form = form
180                     elif isbn.form != form:
181                         add_note = True
182                     if add_note:
183                         isbn.notes += '\n\n' + catalogue_book.slug + ' ' + form
184                     isbn.save(update_fields=['book', 'form', 'notes'])