Better management of manually-set members.
[wolnelektury.git] / src / isbn / management / commands / import_onix.py
1 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
3 #
4 from datetime import date
5 from lxml import etree
6 from django.core.management.base import BaseCommand
7
8 from isbn.models import ISBNPool, ONIXRecord
9 from librarian import XMLNamespace
10
11
12 ONIXNS = XMLNamespace('http://ns.editeur.org/onix/3.0/reference')
13
14 DIRECT_FIELDS = {
15     'product_form': 'ProductForm',
16     'product_form_detail': 'ProductFormDetail',
17     'title': 'TitleText',
18     'part_number': 'PartNumber',
19     'edition_type': 'EditionType',
20     'edition_number': 'EditionNumber',
21     'language': 'LanguageCode',
22     'imprint': 'ImprintName',
23 }
24
25 UNKNOWN = 'Autor nieznany'
26
27
28 def parse_date(date_str):
29     year = int(date_str[:4])
30     month = int(date_str[4:6])
31     day = int(date_str[6:])
32     return date(year, month, day)
33
34
35 def get_descendants(element, tags):
36     if isinstance(tags, str):
37         tags = [tags]
38     return element.findall('.//' + '/'.join(ONIXNS(tag) for tag in tags))
39
40
41 def get_field(element, tags, allow_multiple=False):
42     sub_elements = get_descendants(element, tags)
43     if not allow_multiple:
44         assert len(sub_elements) <= 1, 'multiple elements: %s' % tags
45     return sub_elements[0].text if sub_elements else None
46
47
48 class Command(BaseCommand):
49     help = "Import data from ONIX."
50
51     def add_arguments(self, parser):
52         parser.add_argument('filename')
53
54     def handle(self, **options):
55         filename = options['filename']
56         tree = etree.parse(open(filename))
57         for product in get_descendants(tree, 'Product'):
58             isbn = get_field(product, ['ProductIdentifier', 'IDValue'])
59             assert len(isbn) == 13
60             pool = ISBNPool.objects.get(prefix__in=[isbn[:i] for i in range(8, 11)])
61             contributors = [
62                 self.parse_contributor(contributor)
63                 for contributor in get_descendants(product, 'Contributor')]
64             record_data = {
65                 'isbn_pool': pool,
66                 'suffix': int(isbn[len(pool.prefix):-1]),
67                 'publishing_date': parse_date(
68                     get_field(product, ['PublishingDate', 'Date'], allow_multiple=True)),
69                 'contributors': contributors,
70             }
71             for field, tag in DIRECT_FIELDS.items():
72                 record_data[field] = get_field(product, tag) or ''
73             record = ONIXRecord.objects.create(**record_data)
74             ONIXRecord.objects.filter(pk=record.pk).update(datestamp=parse_date(product.attrib['datestamp']))
75
76     @staticmethod
77     def parse_contributor(contributor):
78         data = {
79             'isni': get_field(contributor, 'IDValue'),
80             'name': get_field(contributor, 'PersonNameInverted'),
81             'corporate_name': get_field(contributor, 'CorporateName'),
82             'unnamed': get_field(contributor, 'UnnamedPersons')
83         }
84         contributor_data = {
85             'role': get_field(contributor, 'ContributorRole'),
86         }
87         for key, value in data.items():
88             if value:
89                 contributor_data[key] = value
90         if contributor_data.get('name') == UNKNOWN:
91             del contributor_data['name']
92             contributor_data['unnamed'] = '01'
93         for date_elem in get_descendants(contributor, 'ContributorDate'):
94             date_role = get_field(date_elem, 'ContributorDateRole')
95             date = get_field(date_elem, 'Date')
96             if date_role == '50':
97                 contributor_data['birth_date'] = date
98             elif date_role == '51':
99                 contributor_data['death_date'] = date
100         return contributor_data