File Coverage

blib/lib/MARC/Convert/Wikidata/Transform.pm
Criterion Covered Total %
statement 246 269 91.4
branch 44 72 61.1
condition 4 9 44.4
subroutine 43 43 100.0
pod 0 2 0.0
total 337 395 85.3


line stmt bran cond sub pod time code
1             package MARC::Convert::Wikidata::Transform;
2              
3 7     7   160493 use strict;
  7         47  
  7         217  
4 7     7   35 use warnings;
  7         14  
  7         203  
5              
6 7     7   1009 use Class::Utils qw(set_params);
  7         28591  
  7         315  
7 7     7   3422 use Data::Kramerius;
  7         32861  
  7         221  
8 7     7   46 use Error::Pure qw(err);
  7         14  
  7         304  
9 7     7   59 use List::Util qw(any);
  7         13  
  7         343  
10 7     7   3781 use MARC::Convert::Wikidata::Object;
  7         95111  
  7         275  
11 7     7   3482 use MARC::Convert::Wikidata::Object::ISBN;
  7         397857  
  7         281  
12 7     7   3388 use MARC::Convert::Wikidata::Object::Kramerius;
  7         2601  
  7         218  
13 7     7   3383 use MARC::Convert::Wikidata::Object::People;
  7         2188891  
  7         244  
14 7     7   3740 use MARC::Convert::Wikidata::Object::Publisher;
  7         3847  
  7         213  
15 7     7   3417 use MARC::Convert::Wikidata::Object::Series;
  7         4013  
  7         312  
16 7         178 use MARC::Convert::Wikidata::Utils qw(clean_cover clean_date clean_edition_number
17             clean_number_of_pages clean_oclc clean_publication_date clean_publisher_name
18             clean_publisher_place clean_series_name clean_series_ordinal clean_subtitle
19 7     7   3664 clean_title);
  7         28  
20 7     7   991 use Readonly;
  7         18  
  7         305  
21 7     7   56 use Scalar::Util qw(blessed);
  7         15  
  7         289  
22 7     7   4345 use URI;
  7         33594  
  7         303  
23 7     7   54 use Unicode::UTF8 qw(decode_utf8 encode_utf8);
  7         18  
  7         20953  
24              
25             Readonly::Array our @COVERS => qw(hardback paperback);
26             Readonly::Hash our %PEOPLE_TYPE => {
27             'aft' => 'authors_of_afterword',
28             'aui' => 'authors_of_introduction',
29             'aut' => 'authors',
30             'com' => 'compilers',
31             'drt' => 'directors',
32             'edt' => 'editors',
33             'ill' => 'illustrators',
34             'nrt' => 'narrators',
35             'pht' => 'photographers',
36             'trl' => 'translators',
37             };
38              
39             our $VERSION = 0.02;
40              
41             # Constructor.
42             sub new {
43 5     5 0 82094 my ($class, @params) = @_;
44              
45             # Create object.
46 5         16 my $self = bless {}, $class;
47              
48             # MARC::Record object.
49 5         26 $self->{'marc_record'} = undef;
50              
51             # Process parameters.
52 5         33 set_params($self, @params);
53              
54 5 50       90 if (! defined $self->{'marc_record'}) {
55 0         0 err "Parameter 'marc_record' is required.";
56             }
57 5 50 33     70 if (! blessed($self->{'marc_record'})
58             || ! $self->{'marc_record'}->isa('MARC::Record')) {
59              
60 0         0 err "Parameter 'marc_record' must be a MARC::Record object.";
61             }
62              
63 5         47 $self->{'_kramerius'} = Data::Kramerius->new;
64              
65             # Process people in 100, 700.
66 5         11913 $self->{'_people'} = {
67             'authors' => [],
68             'authors_of_afterword' => [],
69             'authors_of_introduction' => [],
70             'compilers' => [],
71             'directors' => [],
72             'editors' => [],
73             'illustrators' => [],
74             'narrators' => [],
75             'photographers' => [],
76             'translators' => [],
77             };
78 5         47 $self->_process_people_100;
79 5         20 $self->_process_people_700;
80              
81 5         15 $self->{'_object'} = undef;
82 5         30 $self->_process_object;
83              
84 5         119 return $self;
85             }
86              
87             sub object {
88 4     4 0 540 my $self = shift;
89              
90 4         47 return $self->{'_object'};
91             }
92              
93             sub _ccnb {
94 5     5   15 my $self = shift;
95              
96 5         16 my $ccnb = $self->_subfield('015', 'a');
97 5 50       115 if (! defined $ccnb) {
98 0         0 $ccnb = $self->_subfield('015', 'z');
99             }
100              
101 5         26 return $ccnb;
102             }
103              
104             sub _construct_kramerius {
105 4     4   13 my ($self, $kramerius_uri) = @_;
106              
107             # XXX krameriusndk.nkp.cz is virtual project domain.
108 4         23 $kramerius_uri =~ s/krameriusndk\.nkp\.cz/kramerius.mzk.cz/ms;
109              
110 4         29 my $u = URI->new($kramerius_uri);
111 4         23075 my $authority = $u->authority;
112 4         345 foreach my $k ($self->{'_kramerius'}->list) {
113 42 100       432 if ($k->url =~ m/$authority\/$/ms) {
114 3         51 my @path_seg = $u->path_segments;
115 3         216 my $uuid = $path_seg[-1];
116 3         18 $uuid =~ s/^uuid://ms;
117 3         19 return MARC::Convert::Wikidata::Object::Kramerius->new(
118             'kramerius_id' => $k->id,
119             'object_id' => $uuid,
120             'url' => $kramerius_uri,
121             );
122             }
123             }
124              
125 1         10 return;
126             }
127              
128             sub _cover {
129 5     5   12 my $self = shift;
130              
131 5         25 my @cover = $self->_subfield('020', 'q');
132 5         59 my @ret_cover;
133 5         13 foreach my $cover (@cover) {
134 2         19 $cover = clean_cover($cover);
135 2 50       7 if (! defined $cover) {
136 0         0 next;
137             }
138 2 50 66     12 if ($cover ne 'hardback' && $cover ne 'paperback') {
139 0         0 warn encode_utf8("Book cover '$cover' doesn't exist.\n");
140             } else {
141 2         6 push @ret_cover, $cover;
142             }
143             }
144              
145 5 50       21 if (@ret_cover > 1) {
146 0         0 err 'Multiple book covers.',
147             'List', (join ',', @ret_cover),
148             ;
149             }
150              
151 5         24 return $ret_cover[0];
152             }
153              
154             sub _dml {
155 5     5   11 my $self = shift;
156              
157 5         18 my @fields_856 = $self->{'marc_record'}->field('856');
158 5         624 foreach my $field_856 (@fields_856) {
159 4         25 my $uri = $field_856->subfield('u');
160 4 50       118 if ($uri =~ m/https:\/\/dml\.cz\/handle\/10338\.dmlcz\/(\d+)$/ms) {
161 0         0 return $1;
162             }
163             }
164              
165 5         27 return;
166             }
167              
168             sub _edition_number {
169 7     7   22 my $self = shift;
170              
171 7         20 my $edition_number = $self->_subfield('250', 'a');
172 7 50       149 if (! defined $edition_number) {
173 0         0 return;
174             }
175 7         27 my $orig_edition_number = $edition_number;
176 7         27 $edition_number = clean_edition_number($edition_number);
177              
178 7 100       53 if (! defined $edition_number) {
    50          
179 3         69 warn encode_utf8("Edition number '$orig_edition_number' cannot clean.\n");
180             } elsif ($edition_number !~ m/^\d+$/ms) {
181 0         0 warn encode_utf8("Edition number '$edition_number' isn't number.\n");
182             }
183              
184 7         319 return $edition_number;
185             }
186              
187             sub _isbns {
188 5     5   11 my $self = shift;
189              
190 5         30 my @isbn_fields = $self->{'marc_record'}->field('020');
191 5         623 my @ret_isbns;
192 5         15 foreach my $isbn_field (@isbn_fields) {
193 2         7 my $isbn = $isbn_field->subfield('a');
194 2 100       46 if (! defined $isbn) {
195 1         3 next;
196             }
197 1         5 my @publishers = $isbn_field->subfield('q');
198 1         24 my ($publisher, $cover);
199 1         8 foreach my $pub (@publishers) {
200 1         4 $pub = clean_cover($pub);
201 1 50       3 if (! defined $pub) {
202 0         0 next;
203             }
204 1 50   1   5 if (any { $pub eq $_ } @COVERS) {
  1         19  
205 1         6 $cover = $pub;
206             } else {
207 0         0 $publisher = $pub;
208             }
209             }
210 1 50       17 my $isbn_o = MARC::Convert::Wikidata::Object::ISBN->new(
    50          
211             defined $cover ? (
212             'cover' => $cover,
213             ) : (),
214             'isbn' => $isbn,
215             defined $publisher ? (
216             'publisher' => MARC::Convert::Wikidata::Object::Publisher->new(
217             'name' => clean_publisher_name($publisher),
218             ),
219             ) : (),
220             );
221 1 50       706 if (defined $isbn_o) {
222 1         5 push @ret_isbns, $isbn_o;
223             }
224             }
225              
226 5         21 return (@ret_isbns);
227             }
228              
229             sub _issn {
230 5     5   10 my $self = shift;
231              
232 5         18 my $issn = $self->_subfield('022', 'a');
233              
234 5         25 return $issn;
235             }
236              
237             sub _krameriuses {
238 5     5   20 my $self = shift;
239              
240             return map {
241 5         23 $self->_construct_kramerius($_);
  4         105  
242             } $self->_subfield('856', 'u');
243             }
244              
245             sub _languages {
246 5     5   169 my $self = shift;
247              
248 5         18 my @lang = $self->_subfield('041', 'a');
249 5 100       74 if (! @lang) {
250 4         14 push @lang, $self->_subfield('040', 'b');
251             }
252              
253 5         148 return @lang;
254             }
255              
256              
257             sub _number_of_pages {
258 5     5   10 my $self = shift;
259              
260 5         16 my $number_of_pages = $self->_subfield('300', 'a');
261 5         147 $number_of_pages = clean_number_of_pages($number_of_pages);
262              
263 5         28 return $number_of_pages;
264             }
265              
266             sub _oclc {
267 5     5   11 my $self = shift;
268              
269 5         16 my @oclc = $self->_subfield('035', 'a');
270 5         103 foreach my $oclc (@oclc) {
271 4         17 $oclc = clean_oclc($oclc);
272             }
273 5 50       23 if (@oclc > 1) {
274 0         0 err 'Multiple OCLC control number.';
275             }
276              
277 5         27 return $oclc[0];
278             }
279              
280             sub _process_object {
281 5     5   13 my $self = shift;
282              
283 5         20 my ($publication_date, $publication_date_option) = $self->_publication_date;
284 5         27 my ($start_time, $end_time);
285 5 50       23 if ($publication_date =~ m/^(\d+)\-(\d*)$/ms) {
286 0         0 $start_time = $1;
287 0 0       0 if ($2) {
288 0         0 $end_time = $2;
289             }
290 0         0 undef $publication_date;
291             }
292              
293             # TODO $publication_date_option; end_time; start_time
294             $self->{'_object'} = MARC::Convert::Wikidata::Object->new(
295             'authors' => $self->{'_people'}->{'authors'},
296             'authors_of_afterword' => $self->{'_people'}->{'authors_of_afterword'},
297             'authors_of_introduction' => $self->{'_people'}->{'authors_of_introduction'},
298             'ccnb' => $self->_ccnb,
299             'compilers' => $self->{'_people'}->{'compilers'},
300             'cover' => $self->_cover,
301             'directors' => $self->{'_people'}->{'directors'},
302             $self->_dml ? ('dml' => $self->_dml) : (),
303             $self->_edition_number ? ('edition_number' => $self->_edition_number) : (),
304             'editors' => $self->{'_people'}->{'editors'},
305             'end_time' => $end_time,
306             'isbns' => [$self->_isbns],
307             'issn' => $self->_issn,
308             'illustrators' => $self->{'_people'}->{'illustrators'},
309             'krameriuses' => [$self->_krameriuses],
310             'languages' => [$self->_languages],
311             'narrators' => $self->{'_people'}->{'narrators'},
312             'number_of_pages' => $self->_number_of_pages,
313             'oclc' => $self->_oclc,
314             'photographers' => $self->{'_people'}->{'photographers'},
315             'publication_date' => $publication_date,
316             'publishers' => [$self->_publishers],
317             'series' => [$self->_series],
318             'start_time' => $start_time,
319             'subtitles' => [$self->_subtitles],
320             'title' => $self->_title,
321 5 50       34 'translators' => $self->{'_people'}->{'translators'},
    100          
322             );
323              
324 5         1838 return;
325             }
326              
327             sub _process_people {
328 7     7   22 my ($self, $field) = @_;
329              
330 7         28 my @types = $field->subfield('4');
331 7         291 my @type_keys;
332 7         22 foreach my $type (@types) {
333 6         21 my $type_key = $self->_process_people_type($type);
334 6 50       62 if (defined $type_key) {
335 6         19 push @type_keys, $type_key;
336             }
337             }
338 7 100       23 if (! @type_keys) {
339 1         3 return;
340             }
341              
342 6         21 my $full_name = $field->subfield('a');
343             # TODO Only if type 1. Fix for type 0 and 2.
344 6         231 my ($surname, $name) = split m/,\s*/ms, $full_name;
345              
346 6         22 my $nkcr_aut = $field->subfield('7');
347              
348 6         182 my $dates = $field->subfield('d');
349 6         160 my ($date_of_birth, $date_of_death);
350 6 50       20 if (defined $dates) {
351 6         23 ($date_of_birth, $date_of_death) = split m/-/ms, $dates;
352 6         41 $date_of_birth = clean_date($date_of_birth);
353 6         25 $date_of_death = clean_date($date_of_death);
354             }
355              
356 6         24 foreach my $type_key (@type_keys) {
357 6         12 push @{$self->{'_people'}->{$type_key}},
  6         85  
358             MARC::Convert::Wikidata::Object::People->new(
359             'date_of_birth' => $date_of_birth,
360             'date_of_death' => $date_of_death,
361             'name' => $name,
362             'nkcr_aut' => $nkcr_aut,
363             'surname' => $surname,
364             );
365             }
366              
367 6         8820 return;
368             }
369              
370             sub _process_people_100 {
371 5     5   13 my $self = shift;
372              
373 5         38 my @field_100 = $self->{'marc_record'}->field('100');
374 5         751 foreach my $field (@field_100) {
375 5         28 $self->_process_people($field);
376             }
377              
378 5         13 return;
379             }
380              
381             sub _process_people_700 {
382 5     5   10 my $self = shift;
383              
384 5         20 my @field_700 = $self->{'marc_record'}->field('700');
385 5         636 foreach my $field (@field_700) {
386 2         7 $self->_process_people($field);
387             }
388              
389 5         13 return;
390             }
391              
392             sub _process_people_type {
393 6     6   18 my ($self, $type) = @_;
394              
395 6 50 33     65 if (! defined $type || $type eq '') {
396 0         0 warn "People type set to 'aut'.\n";
397 0         0 $type = 'aut';
398             }
399              
400 6 50       48 if (exists $PEOPLE_TYPE{$type}) {
401 6         99 return $PEOPLE_TYPE{$type};
402             } else {
403 0         0 warn "People type '$type' doesn't exist.\n";
404 0         0 return;
405             }
406             }
407              
408             sub _process_publisher_field {
409 18     18   42 my ($self, $field_num) = @_;
410              
411 18         55 my $field = $self->{'marc_record'}->field($field_num);
412 18 100       1776 if (! defined $field) {
413 9         25 return ();
414             }
415 9         36 my @publisher_names = $field->subfield('b');
416 9         272 my @publishers;
417 9         66 for (my $i = 0; $i < @publisher_names; $i++) {
418 9         36 my $publisher_name = clean_publisher_name($publisher_names[$i]);
419              
420 9         27 my @places = $field->subfield('a');
421 9         255 my $place;
422 9 50       33 if (defined $places[$i]) {
423 9         21 $place = $places[$i];
424             } else {
425 0         0 $place = $places[0];
426             }
427 9         27 $place = clean_publisher_place($place);
428              
429 9         81 push @publishers, MARC::Convert::Wikidata::Object::Publisher->new(
430             'name' => $publisher_name,
431             'place' => $place,
432             );
433             }
434              
435 9         612 return @publishers;
436             }
437              
438             sub _publication_date {
439 5     5   24 my $self = shift;
440              
441 5         20 my $publication_date = $self->_subfield('264', 'c');
442 5 50       27 if (! $publication_date) {
443 5         15 $publication_date = $self->_subfield('260', 'c');
444             }
445              
446 5         154 my $option;
447 5         40 ($publication_date, $option) = clean_publication_date($publication_date);
448              
449 5 50       38 return wantarray ? ($publication_date, $option) : $publication_date;
450             }
451              
452             sub _publishers {
453 9     9   19 my $self = shift;
454              
455 9         37 my @publishers = $self->_process_publisher_field('260');
456 9         27 push @publishers, $self->_process_publisher_field('264');
457              
458 9         50 return @publishers;
459             }
460              
461             sub _series {
462 5     5   10 my $self = shift;
463              
464 5         15 my @series_490 = $self->{'marc_record'}->field('490');
465 5         640 my @series;
466 5         13 foreach my $series_490 (@series_490) {
467 4         14 my $series_name = $series_490->subfield('a');
468 4         118 $series_name = clean_series_name($series_name);
469 4         13 my $series_ordinal = $series_490->subfield('v');
470 4         113 $series_ordinal = clean_series_ordinal($series_ordinal);
471              
472             # XXX Over all publishers.
473 4         16 foreach my $publisher ($self->_publishers) {
474 4         36 push @series, MARC::Convert::Wikidata::Object::Series->new(
475             'name' => $series_name,
476             'publisher' => $publisher,
477             'series_ordinal' => $series_ordinal,
478             );
479             }
480             }
481              
482 5         282 return @series;
483             }
484              
485             sub _subfield {
486 76     76   173 my ($self, $field, $subfield) = @_;
487              
488 76         211 my $field_value = $self->{'marc_record'}->field($field);
489 76 100       6594 if (! defined $field_value) {
490 19         53 return;
491             }
492              
493 57         140 return $field_value->subfield($subfield);
494             }
495              
496             sub _subtitles {
497 5     5   54 my $self = shift;
498              
499 5         10 my @ret;
500 5         40 my $subtitle = $self->_subfield('245', 'b');
501 5         158 $subtitle = clean_subtitle($subtitle);
502 5 100       37 if (defined $subtitle) {
503 4         17 push @ret, $subtitle;
504             }
505 5         22 my $number_of_part = $self->_subfield('245', 'n');
506 5         137 $number_of_part = clean_subtitle($number_of_part);
507 5 50       21 if (defined $number_of_part) {
508 0         0 push @ret, $number_of_part;
509             }
510 5         17 my $name_of_part = $self->_subfield('245', 'p');
511 5         120 $name_of_part = clean_subtitle($name_of_part);
512 5 50       17 if (defined $name_of_part) {
513 0         0 push @ret, $name_of_part;
514             }
515              
516 5         21 return @ret;
517             }
518              
519             sub _title {
520 5     5   10 my $self = shift;
521              
522 5         14 my $title = $self->_subfield('245', 'a');
523 5         145 $title = clean_title($title);
524              
525 5         93 return $title;
526             }
527              
528             1;
529              
530             __END__