Completed
Push — master ( c9f20b...bb045d )
by Dan Michael O.
14:32
created

Marc21Importer   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 213
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 7
Bugs 3 Features 0
Metric Value
wmc 31
c 7
b 3
f 0
lcom 1
cbo 10
dl 0
loc 213
rs 9.8

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 3
C parseRecord() 0 63 10
A fixVocabularyCode() 0 4 1
F importParsedRecord() 0 85 14
A import() 0 20 3
1
<?php
2
3
namespace Colligator;
4
5
use Colligator\Events\Marc21RecordImported;
6
use Danmichaelo\QuiteSimpleXMLElement\QuiteSimpleXMLElement;
7
use Event;
8
use Scriptotek\Marc\Record as MarcRecord;
9
use Scriptotek\SimpleMarcParser\BibliographicRecord;
10
use Scriptotek\SimpleMarcParser\HoldingsRecord;
11
use Scriptotek\SimpleMarcParser\Parser as MarcParser;
12
use Scriptotek\SimpleMarcParser\ParserException;
13
14
class Marc21Importer
15
{
16
    public $record;
17
    public $parser;
18
19
    /**
20
     * Create a new job instance.
21
     *
22
     * @param $record
23
     */
24
    public function __construct(MarcParser $parser = null, DescriptionScraper $scraper)
25
    {
26
        $this->parser = $parser ?: new MarcParser();
27
        $this->scraper = $scraper ?: new DescriptionScraper();
28
    }
29
30
    /**
31
     * Parse using SimpleMarcParser and separate bibliographic and holdings.
32
     *
33
     * @param QuiteSimpleXMLElement $data
34
     *
35
     * @return array
36
     */
37
    public function parseRecord(QuiteSimpleXMLElement $data)
38
    {
39
        $data->registerXPathNamespaces([
40
            'marc' => 'http://www.loc.gov/MARC21/slim'
41
        ]);
42
        $biblio = null;
43
        $holdings = [];
44
        foreach ($data->xpath('.//marc:record') as $rec) {
45
            $parsed = $this->parser->parse($rec);
46
            if ($parsed instanceof BibliographicRecord) {
47
                $biblio = $parsed->toArray();
48
            } elseif ($parsed instanceof HoldingsRecord) {
49
                $holdings[] = $parsed->toArray();
50
            }
51
        }
52
53
        if (!count($holdings)) {
54
            // Oh, hello Alma...
55
            $q = $data->first('.//marc:record')->asXML();
56
57
            $rec = MarcRecord::fromString($q);
58
59
            $itemMap = [
60
                'x' => 'location',  // OBS: 1030310
61
                'y' => 'shelvinglocation',  // OBS: k00475
62
                'c' => 'barcode',
63
                'z' => 'callcode',
64
                'a' => 'id',
65
                'd' => 'due_back_date',
66
                'p' => 'process_type',
67
                's' => 'item_status',
68
                'n' => 'public_note',
69
            ];
70
71
            foreach ($rec->getFields('976') as $field) {
72
                $holding = [];
73
74
                foreach ($itemMap as $c => $f) {
75
                    $sf = $field->getSubfield($c);
76
                    if ($sf) {
77
                        $holding[$f] = $sf->getData();
78
                    }
79
                }
80
81
                $sf = $field->getSubfield('s');
82
                if ($sf) {
83
                    $sft = $sf->getData();
84
85
                    # TODO: Er dette faktisk circulation status, eller bare om den er missing eller ei?
86
87
                    if ($sft) {
88
                        $holding['circulation_status'] = 'Available';
89
                    } else {
90
                        $holding['circulation_status'] = 'Unavailable';
91
                    }
92
                }
93
94
                $holdings[] = $holding;
95
            }
96
        }
97
98
        return [$biblio, $holdings];
99
    }
100
101
    /**
102
     * Fixes wrong vocabulary codes returned by the Bibsys SRU service.
103
     * Bibsys strips dashes in 648, 650, 655, but not in 651, so we have
104
     * to do that ourselves. This affects 'no-ubo-mn' and 'no-ubo-mr'.
105
     */
106
    public function fixVocabularyCode($code)
107
    {
108
        return str_replace('-', '', $code);
109
    }
110
111
    /**
112
     * @param array $biblio
113
     * @param array $holdings
114
     *
115
     * @return null|Document
116
     */
117
    public function importParsedRecord(array $biblio, array $holdings = [])
118
    {
119
        // Convert Carbon date objects to ISO8601 strings
120
        if (isset($biblio['created'])) {
121
            $biblio['created'] = $biblio['created']->toIso8601String();
122
        }
123
        if (isset($biblio['modified'])) {
124
            $biblio['modified'] = $biblio['modified']->toIso8601String();
125
        }
126
127
        // Find existing Document or create a new one
128
        $doc = Document::firstOrNew(['bibsys_id' => $biblio['id']]);
129
130
        // Update Document
131
        $doc->bibliographic = $biblio;
132
        $doc->holdings = $holdings;
133
134
        // Extract description from bibliographic record if no description exists
135
        if (isset($biblio['description']) && is_null($doc->description)) {
136
            $this->scraper->updateDocument($doc, $biblio['description']);
137
        }
138
139
        if (!$doc->save()) {
140
            $this->error("Document $biblio->id could not be saved!");
141
142
            return;
143
        }
144
145
        // Check other form
146
        $other_id = array_get($biblio, 'other_form.id');
147
        if (!empty($other_id)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
148
149
            // @TODO: https://github.com/scriptotek/colligator-backend/issues/34
150
            // Alma uses 776, but the IDs can't be looked up. Need to investigate!
151
            // Example: 990824196984702204 (NZ: 990824196984702201), having
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
152
            // 776    0_ $t The Earth after us : what legacy will humans leave in the rocks? $w 991234552274702201
153
154
            // $doc2 = Document::where('bibsys_id', '=', $other_id)->first();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
155
            // if (is_null($doc2)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
156
            //     $record = \SruClient::first('bs.objektid=' . $other_id);
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
157
            //     \Log::debug('Importing related record ' . $other_id);
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
158
            //     if (is_null($record)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
159
            //         die('uh oh');
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
160
            //     } else {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
161
            //         $this->import($record->data);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
162
            //     }
163
            // }
164
165
            // @TODO: Add a separate jobb that updates e-books weekly or so..
166
        }
167
168
        // Sync subjects
169
        $subject_ids = [];
170
        foreach ($biblio['subjects'] as $value) {
171
            $value['vocabulary'] = $this->fixVocabularyCode($value['vocabulary']);
172
            $subject = Subject::lookup($value['vocabulary'], $value['term'], $value['type']);
173
            if (is_null($subject)) {
174
                $subject = Subject::create($value);
175
            }
176
            $subject_ids[] = $subject->id;
177
        }
178
        $doc->subjects()->sync($subject_ids);
179
180
        // Sync genres
181
        $genre_ids = [];
182
        foreach ($biblio['genres'] as $value) {
183
            $genre = Genre::lookup($value['vocabulary'], $value['term']);
184
            if (is_null($genre)) {
185
                $genre = Genre::create($value);
186
            }
187
            $genre_ids[] = $genre->id;
188
        }
189
        $doc->genres()->sync($genre_ids);
190
191
        // Extract cover from bibliographic record if no local cover exists
192
        if (isset($biblio['cover_image']) && is_null($doc->cover)) {
193
            try {
194
                $doc->storeCover($biblio['cover_image']);
195
            } catch (\ErrorException $e) {
196
                \Log::error('Failed to store cover: ' . $biblio['cover_image']);
197
            }
198
        }
199
200
        return $doc;
201
    }
202
203
    /**
204
     * Execute the job.
205
     */
206
    public function import(QuiteSimpleXMLElement $record)
207
    {
208
        try {
209
            list($biblio, $holdings) = $this->parseRecord($record);
210
        } catch (ParserException $e) {
211
            $this->error('Failed to parse MARC record. Error "' . $e->getMessage() . '" in: ' . $e->getFile() . ':' . $e->getLine() . "\nStack trace:\n" . $e->getTraceAsString());
212
213
            return;
214
        }
215
216
        $doc = $this->importParsedRecord($biblio, $holdings);
217
218
        \Log::debug('[Marc21Importer] Imported ' . $doc->bibsys_id . ' as ' . $doc->id);
219
220
        if (!is_null($doc)) {
221
            Event::fire(new Marc21RecordImported($doc->id));
222
        }
223
224
        return $doc->id;
225
    }
226
}
227