2 # Extract MARC fields from XML
3 # get_marc_attrs( { marc_xml => doc } )
7 # Map item types to schema.org types; impedance mismatch :(
8 args.schema.itemtype = {};
10 schema_typemap.a = 'http://schema.org/Book';
11 schema_typemap.j = 'http://schema.org/MusicRecording';
14 FOR isbn IN xml.findnodes('//*[@tag="020"]/*[@code="a"]');
15 args.isbns.push(isbn.textContent);
18 FOR upc IN xml.findnodes('//*[@tag="024"]/*[@code="a"]');
19 args.upcs.push(upc.textContent);
21 args.upc = args.upcs.0; # use first UPC as the default
22 args.issn = xml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
23 args.author = xml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
25 # Include subfields 'abnp' to generate a more comprehensive title display in search results
26 titresults = xml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b" or @code="n" or @code="p"]');
27 titresults_content = [];
28 FOR sub IN titresults; titresults_content.push(sub.textContent); END;
29 args.title = titresults_content.join(" ");
30 # Avoid ugly trailing syntax on brief titles
31 args.title = args.title | replace('[:;/]$', '');
33 # Provide correct spacing between the subfields
34 titsubs = xml.findnodes('//*[@tag="245"]/*[@code]');
36 FOR sub IN titsubs; titsubs_content.push(sub.textContent); END;
37 args.title_extended = titsubs_content.join(" ");
39 args.publisher = xml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
40 args.pubdate = xml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
41 args.summary = xml.findnodes('//*[@tag="520"]/*[@code="a"]').textContent;
42 args.edition = xml.findnodes('//*[@tag="250"]/*[@code="a"]').textContent ||
43 xml.findnodes('//*[@tag="534"]/*[@code="b"]').textContent ||
44 xml.findnodes('//*[@tag="775"]/*[@code="b"]').textContent;
46 '//*[@tag="300"]/*[@code="a" or @code="b" or @code="c" or @code="e"]'
49 FOR p IN phys; phys_content.push(p.textContent); END;
50 args.phys_desc = phys_content.join("");
52 args.contents = xml.findnodes('//*[@tag="505"]').textContent;
55 args.marc_cn = xml.findnodes('//*[@tag="092" or @tag="099"]/*').textContent;
58 args.isbn_clean = args.isbns.0.replace('\ .*', '');
60 # Extract the 856 URLs that are not otherwise represented by asset.uri's
62 FOR node IN xml.findnodes('//*[@tag="856" and @ind1="4" and (@ind2="0" or @ind2="1")]');
63 IF node.findnodes('./*[@code="9" or @code="w" or @code="n"]'); NEXT; END; # asset.uri's
64 label = node.findnodes('./*[@code="y"]');
65 notes = node.findnodes('./*[@code="z" or @code="3"]');
66 FOR href IN node.findnodes('./*[@code="u"]');
68 # it's possible for multiple $u's to exist within 1 856 tag.
69 # in that case, honor the label/notes data for the first $u, but
70 # leave any subsequent $u's as unadorned href's.
71 # use href/link/note keys to be consistent with args.uri's
72 args.online_res.push({
73 href => href.textContent,
74 link => (loop.first AND label) ? label.textContent : href.textContent,
75 note => (loop.first) ? notes.textContent : ''
83 args.resolver_isbns = [];
84 args.resolver_issns = [];
86 # we use $9 of ISBN and ISSN as a flag for e-version
87 FOR resolver_isbn IN xml.findnodes('//*[@tag="020"]/*[@code="9"]');
88 IF resolver_isbn.textContent == "SFX" || resolver_isbn.textContent == "CUFTS";
89 my_parent = resolver_isbn.parentNode();
90 FOR resolver_isbn_val IN my_parent.findnodes('./*[@code="a"]');
91 args.resolver_isbns.push(
92 resolver_isbn_val.textContent.replace('-', '').replace('\ .*', '')
98 FOR resolver_issn IN xml.findnodes('//*[@tag="022"]/*[@code="9"]');
99 IF resolver_issn.textContent == "SFX" || resolver_issn.textContent == "CUFTS";
100 my_parent = resolver_issn.parentNode();
101 FOR resolver_issn_val IN my_parent.findnodes('./*[@code="a"]');
102 args.resolver_issns.push(
103 resolver_issn_val.textContent.replace('[^\d\-X]', '')
110 FOR rawissn IN xml.findnodes('//*[@tag="022"]/*[@code="a"]');
112 rawissn.textContent.replace('[^\d\-X]', '')
116 FOR volume IN xml.findnodes('//*[local-name()="volumes"]/*[local-name()="volume"]');
118 # Check volume visibility - could push this into XPath
119 vol.label = volume.getAttribute('label');
121 # Prepend prefix, if any
122 prefix = volume.findnodes('./*[local-name()="call_number_prefix"][@ident!="-1"]');
123 IF prefix.getAttribute('label') != '';
124 vol.label = prefix.getAttribute('label') _ " " _ vol.label;
127 # Append prefix, if any
128 suffix = volume.findnodes('./*[local-name()="call_number_suffix"][@ident!="-1"]');
129 IF suffix.getAttribute('label') != '';
130 vol.label = vol.label _ " " _ suffix.getAttribute('label');
133 vol.id = volume.getAttribute('id');
134 NEXT IF volume.getAttribute('opac_visible') == 'false';
135 NEXT IF volume.getAttribute('deleted') == 'true';
137 IF vol.label == '##URI##';
138 FOR uri IN volume.findnodes('./*[local-name()="uris"]/*[local-name()="uri"]');
140 res.href = uri.getAttribute('href');
141 res.link = uri.getAttribute('label');
142 res.note = uri.getAttribute('use_restriction');
147 copies = volume.findnodes('./*[local-name()="copies"]/*[local-name()="copy"]');
149 parts = copy.findnodes('./*[local-name()="monograph_parts"]/*[local-name()="monograph_part"]');
150 FOREACH part IN parts;
151 part_label = part.getAttribute('label');
152 LAST IF part_label != '';
154 # Check copy visibility
155 cp.deleted = copy.getAttribute('deleted');
156 cp.visible = copy.getAttribute('opac_visible');
157 NEXT IF (cp.deleted == 'true' OR cp.visible == 'false');
159 # Iterate through all of the children to determine visibility
160 FOR node IN cp.childNodes;
161 NEXT IF cp.visible == 'false';
162 vis = node.getAttribute('opac_visible');
163 del = node.getAttribute('deleted');
164 IF vis == 'false' or del == 'true';
165 cp.visible = 'false';
169 NEXT IF cp.visible == 'false';
171 loc = copy.findnodes('./*[local-name()="location"]');
172 circlib = copy.findnodes('./*[local-name()="circlib"]');
173 status = copy.findnodes('./*[local-name()="status"]');
177 part_label => part_label,
178 location => loc.textContent,
179 library => circlib.textContent,
180 status => status.textContent
181 barcode => copy.getAttribute('barcode')
183 args.holdings.push(holding);
189 # Extract the copy count summary
190 count_type = (ctx.is_staff) ? 'staff' : 'public';
192 # Consortial copy count summary first
193 xpath = '//*[local-name()="counts"]/*[local-name()="count"][@type="' _ count_type _ '"]';
194 args.copy_counts = {};
195 FOR node IN xml.findnodes(xpath);
196 FOR attr IN ['count', 'available', 'unshadow', 'transcendant', 'org_unit'];
197 depth = node.getAttribute('depth');
198 args.copy_counts.$depth.$attr = node.getAttribute(attr);
202 # Get preferred library copy count
203 args.plib_copy_counts = {};
204 count_type = 'pref_lib';
205 xpath = '//*[local-name()="counts"]/*[local-name()="count"][@type="' _ count_type _ '"]';
206 FOR node IN xml.findnodes(xpath);
207 FOR attr IN ['count', 'available', 'unshadow', 'transcendant', 'org_unit'];
208 depth = node.getAttribute('depth');
209 args.plib_copy_counts.$depth.$attr = node.getAttribute(attr);
213 # "mattype" == "custom marc format specifier"
214 FOR icon_style IN ['mattype', 'item_type'];
215 node = xml.findnodes(
216 '//*[local-name()="attributes"]/*[local-name()="field"][@name="' _ icon_style _ '"]');
217 IF node AND node.textContent;
218 type = node.textContent;
219 args.format_label = node.getAttribute('coded-value')
220 args.schema.itemtype = schema_typemap.$type;
221 args.format_icon = ctx.media_prefix _ '/images/format_icons/' _ icon_style _ '/' _ type _ '.png';