]> git.evergreen-ils.org Git - working/Evergreen.git/blob - docs/RELEASE_NOTES_NEXT/OAI2/install.adoc
LP#1729620 New optional feature: an OAI2 provider service.
[working/Evergreen.git] / docs / RELEASE_NOTES_NEXT / OAI2 / install.adoc
1 = oai-openils is an openSRF service
2
3 This module is an optional service that exposes your catalog through the [OAI2 protocol](http://www.openarchives.org/OAI/openarchivesprotocol.html).
4
5 == 1. Intended behaviour
6
7 === 1.1 Entry points
8 There are two: one for bibliographic records and one for authority records:
9
10     http://your-domain/opac/extras/oai/authority
11     http://your-domain/opac/extras/oai/biblio
12  
13 === 1.2 Setspec are not implemented
14
15 This is a work in progress and not enabled. The aim is to have the owning library determine the set hierarchy. The Concerto
16 test database for example has a record with tcn #1. This record is so popular it has copies attached to library units
17 "Example Branch 1", "Example Branch 2", "Example Branch 3", "Example Bookmobile 1" which is a child of Branch 3 and
18 "Example Branch 4". This entire kinship is expressed as sets like so: 
19
20 ```xml
21 <header>
22     ...
23     <setSpec>CONS</setSpec>
24     <setSpec>CONS:SYS1</setSpec>
25     <setSpec>CONS:SYS2</setSpec>
26     <setSpec>CONS:SYS1:BR1</setSpec>
27     <setSpec>CONS:SYS1:BR2</setSpec>
28     <setSpec>CONS:SYS2:BR3</setSpec>
29     <setSpec>CONS:SYS2:BR4</setSpec>
30     <setSpec>CONS:SYS2:BR3:BM1</setSpec>
31 </header>
32 ```
33 Likewise the setSpecs of authority records are derived from their browse axis ( Title, Author, Subject and Topic ).
34
35 === 1.3 OAI2 datestamp
36
37 The edit date of the bibliographic and authority record is used as datestamp. If you want the date for editorial updates
38 of bibliographic assets ( copies, call numbers ) reflected in the datestamp, then add the triggers shown below.
39
40 === 1.4 Bibliographic mapping of assets to 852 subfields
41
42 Certain attributes asset are placed into 852 subfields so:
43
44 | subfield code | asset resource |
45 | --- | --- |
46 | a | location |
47 | b | owning_lib |
48 | c | callnumber |
49 | d | circlib |
50 | g | barcode |
51 | n | status |
52  
53 Thus the Concerto with tcn #1 will have it's 852 subfields expressed as:
54 ```xml
55 <marc:datafield ind1="4" ind2=" " tag="852">
56     <marc:subfield code="a">Stacks</marc:subfield>
57     <marc:subfield code="b">BR4</marc:subfield>
58     <marc:subfield code="c">ML 60 R100</marc:subfield>
59     <marc:subfield code="d">BR4</marc:subfield>
60     <marc:subfield code="g">CONC70000435</marc:subfield>
61     <marc:subfield code="n">Checked out</marc:subfield>
62 </marc:datafield>
63 ```
64 This mapping can be customized and extended with static subfields:
65 ```xml
66     <marc:subfield code="q">A constant value</marc:subfield>
67 ```
68
69 === 1.5 Default configuration
70
71 All default configuration is commented in the open-ils.oai app_settings element. See below for details on how to
72 override defaults by removing the comments and substitute the values.
73
74 == 2. Installation
75
76 === 2.1 Perl modules
77
78 Lookup the Perl handler and the associated openils module:
79
80  - [Open-ILS/src/perlmods/lib/OpenILS/WWW/OAI.pm](Open-ILS/src/perlmods/lib/OpenILS/WWW/OAI.pm)
81  - [Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm](Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm)
82
83 Place them in your codebase next to the other openils modules and let them thus become part of the build:
84
85     Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm
86     Open-ILS/src/perlmods/lib/OpenILS/WWW/OAI.pm
87
88 or copy the files (owned by the opensrf user) on your servers that host the openils services in the Perl library path:
89
90     /the perl library path/OpenILS/Application/OAI.pm
91     /the perl library path/OpenILS/WWW/OAI.pm
92
93 === 2.2 Declare the perl handler
94
95 Declare the Perl handler in the Apache eg_startup file:
96
97 ```perl
98 use OpenILS::WWW::OAI qw( <openils sysdir>conf/opensrf_core.xml );
99 ```
100     
101 And reference it in the Apache eg_vhost.conf file, apache 2.2:
102
103     <Location /opac/extras/oai>
104         SetHandler perl-script
105         PerlHandler OpenILS::WWW::OAI
106         Options +ExecCGI
107         PerlSendHeader On
108         allow from all
109     </Location>
110
111 or apache 2.4
112
113     <Location /opac/extras/oai>
114         SetHandler perl-script
115         PerlHandler OpenILS::WWW::OAI
116         Options +ExecCGI
117         PerlSendHeader On
118         Require all granted
119     </Location>
120
121 In the eg.conf file under 'PerlRequire /etc/apache2/eg_startup' add:
122 ```apache
123 PerlChildInitHandler OpenILS::WWW::OAI::child_init
124
125 ```
126
127 === 2.3 The database and fieldmapper
128
129 ==== 2.3.1 Database
130
131 The service requires a view and stored procedures: Open-ILS/src/sql/Pg/oai.sql
132
133 Add the oai section to the database:
134 ```sql
135 -- VIEWS for the oai service
136 CREATE SCHEMA oai;
137
138
139 -- The view presents a lean table with unique bre.tc-numbers for oai paging;
140 CREATE VIEW oai.biblio AS
141   SELECT
142     bre.id                             AS tcn,
143     bre.edit_date                      AS datestamp,
144     bre.deleted                        AS deleted
145   FROM
146     biblio.record_entry bre
147   ORDER BY
148     bre.id;
149
150 -- The view presents a lean table with unique are.tc-numbers for oai paging;
151 CREATE VIEW oai.authority AS
152   SELECT
153     are.id               AS tcn,
154     are.edit_date        AS datestamp,
155     are.deleted          AS deleted
156   FROM
157     authority.record_entry AS are
158   ORDER BY
159     are.id;
160 ```
161
162 ==== 2.3.2 Optional, setting the datestamp
163
164 If you want the OAI2 datestamp to reflect changes in assets as well, add the following triggers
165  ```sql
166  
167 -- If an edit date changes in the asset.call_number or asset.copy and you want this to persist to an OAI2 datestamp,
168 -- then add these stored procedures and triggers:
169 CREATE OR REPLACE FUNCTION oai.datestamp(rid BIGINT)
170   RETURNS VOID AS $$
171 BEGIN
172   UPDATE biblio.record_entry AS bre
173   SET edit_date = now()
174   WHERE bre.id = rid;
175 END
176 $$ LANGUAGE plpgsql;
177
178 CREATE OR REPLACE FUNCTION oai.call_number_datestamp()
179   RETURNS TRIGGER AS $$
180 BEGIN
181   IF TG_OP = 'DELETE'
182   THEN
183     PERFORM oai.datestamp(OLD.record);
184     RETURN OLD;
185   END IF;
186
187   PERFORM oai.datestamp(NEW.record);
188   RETURN NEW;
189
190 END
191 $$ LANGUAGE plpgsql;
192
193 CREATE OR REPLACE FUNCTION oai.copy_datestamp()
194   RETURNS TRIGGER AS $$
195 BEGIN
196   IF TG_OP = 'DELETE'
197   THEN
198     PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = OLD.call_number));
199     RETURN OLD;
200   END IF;
201
202   PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = NEW.call_number));
203   RETURN NEW;
204
205 END
206 $$ LANGUAGE plpgsql;
207
208 CREATE TRIGGER call_number_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.call_number FOR EACH ROW EXECUTE PROCEDURE oai.call_number_datestamp();
209 CREATE TRIGGER copy_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE oai.copy_datestamp(); 
210  ```
211
212 ==== 2.3.3 The fieldmapper
213
214 Proceed by declaring the views in the fm_IDL.xml file so, as the example shows here [Open-ILS/examples/fm_ILD.xml](Open-ILS/examples/fm_IDL.xml):
215
216 ```xml
217 <class id="oai_biblio" controller="open-ils.cstore" oils_obj:fieldmapper="oai::biblio"
218        oils_persist:readonly="true" reporter:core="false" reporter:label="OAI2 record list"
219        oils_persist:tablename="oai.biblio">
220     <fields>
221         <field reporter:label="TCN Value\OAI identifier postfix" name="tcn" reporter:datatype="number"/>
222         <field reporter:label="Last edit date\OAI datestamp" name="datestamp" reporter:datatype="timestamp"/>
223         <field reporter:label="Is Deleted?" name="deleted" reporter:datatype="bool"/>
224         <field reporter:label="Setspec" name="set_spec" oils_persist:virtual="true"/>
225     </fields>
226 </class>
227 <class id="oai_authority" controller="open-ils.cstore" oils_obj:fieldmapper="oai::authority"
228        oils_persist:readonly="true" reporter:core="false" reporter:label="OAI2 record list"
229        oils_persist:tablename="oai.authority">
230     <fields>
231         <field reporter:label="TCN Value\OAI identifier postfix" name="tcn" reporter:datatype="number"/>
232         <field reporter:label="Last edit date\OAI datestamp" name="datestamp" reporter:datatype="timestamp"/>
233         <field reporter:label="Is Deleted?" name="deleted" reporter:datatype="bool"/>
234         <field reporter:label="Setspec" name="set_spec" oils_persist:virtual="true"/>
235     </fields>
236 </class>
237 ```
238
239 === 2.4 The xslt stylesheets
240
241 Lookup the two documents here:
242
243  - [Open-ILS/xsl/OAI2_OAIDC.xsl](Open-ILS/xsl/OAI2_OAIDC.xsl)
244  - [Open-ILS/xsl/OAI2_MARC21slim.xsl](Open-ILS/xsl/OAI2_MARC21slim.xsl)
245
246 Place the stylesheets in your codebase next to the other xsl documents and let them thus become part of the build.
247 Or install them on your servers that host the openils services:
248
249     /<openils sysdir>/var/xsl/OAI2_OAIDC.xsl
250     /<openils sysdir>/var/xsl/OAI2_MARC21slim.xsl
251     
252 === 2.5 Dependencies
253 The openils-oai service depends on a running openils-supercat service.
254 And the OAI2_OAIDC.xsl document uses the file [MARC21slim2OAIDC.xsl](Open-ILS/xsl/MARC21slim2OAIDC.xsl).
255 The service and stylesheet are part of the out-of-the-box Evergreen distributions.
256         
257 But do install the ['HTTP::OAI' perl library from a CPAN repository](http://search.cpan.org/dist/HTTP-OAI/):
258
259     $ cpan HTTP::OAI    
260     
261
262 == 3. Configuration
263
264 === 3.1 Declare the service
265
266 Add the openils-oai service to your /&lt;openils sysdir&gt;/conf/opensrf.xml file.
267 ```xml
268 ....
269 <open-ils.oai>
270     <keepalive>5</keepalive>
271     <stateless>1</stateless>
272     <language>perl</language>
273     <implementation>OpenILS::Application::OAI</implementation>
274     <max_requests>199</max_requests>
275     <unix_config>
276         <unix_sock>open-ils.oai_unix.sock</unix_sock>
277         <unix_pid>open-ils.oai_unix.pid</unix_pid>
278         <max_requests>1000</max_requests>
279         <unix_log>open-ils.oai_unix.log</unix_log>
280         <min_children>1</min_children>
281         <max_children>5</max_children>
282         <min_spare_children>1</min_spare_children>
283         <max_spare_children>2</max_spare_children>
284     </unix_config>
285     <app_settings>
286
287         <!-- Where necessary, override the default settings here in the app_settings element. -->
288
289         <!-- The OAI endpoint. The domain is the name of your proxy or frontend opac website. -->
290         <!-- <base_url>http://mydomain.org/opac/extras/oai</base_url> -->
291
292         <!-- <repository_name>My organization(s)</repository_name> -->
293         <!-- <admin_email>admin@mydomain.org</admin_email> -->
294
295         <!-- The maximum number of records in a ListRecords and ListIdentifiers response. -->
296         <!-- <max_count>50</max_count> -->
297
298         <!-- <granularity>YYYY-MM-DDThh:mm:ss</granularity> -->
299         <!-- <earliest_datestamp>0001-01-01</earliest_datestamp> -->
300         <!-- <deleted_record>yes</deleted_record> -->
301         <!-- <scheme>oai</scheme> -->
302         <!-- <repository_identifier>mydomain.org</repository_identifier> -->
303         <!-- <delimiter>:</delimiter> -->
304         <!-- <sample_identifier>oai:mydomain.org:12345</sample_identifier> -->
305         <!-- <list_sets>false</list_sets> -->
306
307         <!--
308         The metadataformat element contains the schema for the oai_dc and marcxml metadata formats.
309         Each schema needs a reference to an xslt document.
310         You can replace them with your custom xslt stylesheets.
311         Place those in the /<openils sysdir>/var/xsl folder.
312         You can also extend the OAI2 service further with new metadata schema.
313         
314         Bibliographic and authority records share the same stylesheet.
315         Should you want to render them differently, use the
316         marc:datafield[@tag='901']/marc:subfield[@code='t']
317         value to identify the record type. -->
318
319         <!--
320         <metadataformat>
321             <oai_dc>
322                 <namespace_uri>http://www.openarchives.org/OAI/2.0/oai_dc/</namespace_uri>
323                 <schema_location>http://www.openarchives.org/OAI/2.0/oai_dc.xsd</schema_location>
324                 <xslt>OAI2_OAIDC.xsl</xslt>
325             </oai_dc>
326             <marcxml>
327                 <namespace_uri>http://www.loc.gov/MARC21/slim</namespace_uri>
328                 <schema_location>http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd</schema_location>
329                 <xslt>OAI2_MARC21slim.xsl</xslt>
330             </marcxml>
331         </metadataformat> -->
332
333         <!--
334         You can add different schema to the metadataformat element thus:
335             <mods>
336                 <namespace_uri>http://www.loc.gov/mods/</namespace_uri>
337                 <schema_location>http://www.loc.gov/standards/mods/mods.xsd</schema_location>
338                 <xslt>my-custom-marc2mods.xsl</xslt>
339             </mods>
340             <my-metadata_prefix>
341                 <namespace_uri>my-namespace_uri</namespace_uri>
342                 <schema_location>my-schema_location</schema_location>
343                 <xslt>my-marc2my-metadata.xsl</xslt>
344             </my-metadata_prefix>
345         -->
346
347         <!-- Change the way the asset copy values are mapped to which subfield codes: -->
348         <!--
349         <copies>
350             <a>location</a>
351             <b>owning_lib</b>
352             <c>callnumber</c>
353             <d>circlib</d>
354             <g>barcode</g>
355             <n>status</n>
356         </copies>
357         -->
358         <!-- Or add static values to the copies element like this:
359             <z>A value that always should for example be in the 852$z</z>
360         -->
361         
362         <!-- Accept only 852$[barcode] values that match this regular expression. E.g.
363         <barcode_filter>^[A-Za-z0-9]+</barcode_filter>
364         only renders 852 datafields that contain barcodes values that begin with letters and numbers. 
365         <!--
366         <barcode_filter><barcode_filter>
367         -->
368                 
369         <!-- Accept only 852$[status] values that match this regular expression. E.g.
370         <status_filter>^Available$</status_filter>
371         only renders 852 datafields that contain status code values that exactly match the string 'Available'. 
372         <!--
373         <status_filter></status_filter>
374         -->
375
376     </app_settings>
377 </open-ils.oai>
378 ```
379
380 ==== 3.2 Activate the service
381
382 Refer to the service in the opensrf.xml's activeapps element:
383 ```xml
384 ....
385 <activeapps>
386     <appname>open-ils.oai</appname>
387 ```
388
389 ==== 3.3 Register the service with the router
390
391 Add the service to the public router with your /&lt;openils sysdir&gt;/conf/opensrf_core.xml
392 ```xml
393 <config>
394     <opensrf>
395         <routers>
396             <router>
397                 <name>router</name>
398                 <domain>public.realm</domain>
399                 <services>
400                     <service>openils.oai</service>
401                     ...
402 ```
403     
404
405
406
407
408