trigger for item import
[Evergreen.git] / Open-ILS / src / sql / Pg / 012.schema.vandelay.sql
1 DROP SCHEMA vandelay CASCADE;
2
3 BEGIN;
4
5 CREATE SCHEMA vandelay;
6
7 CREATE TABLE vandelay.queue (
8         id                              BIGSERIAL       PRIMARY KEY,
9         owner                   INT                     NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
10         name                    TEXT            NOT NULL,
11         complete                BOOL            NOT NULL DEFAULT FALSE,
12         queue_type              TEXT            NOT NULL DEFAULT 'bib' CHECK (queue_type IN ('bib','authority')),
13         CONSTRAINT vand_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
14 );
15
16 CREATE TABLE vandelay.queued_record (
17     id                  BIGSERIAL                   PRIMARY KEY,
18     create_time TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
19     import_time TIMESTAMP WITH TIME ZONE,
20         purpose         TEXT                                            NOT NULL DEFAULT 'import' CHECK (purpose IN ('import','overlay')),
21     marc                TEXT                        NOT NULL
22 );
23
24
25
26 /* Bib stuff at the top */
27 ----------------------------------------------------
28
29 CREATE TABLE vandelay.bib_attr_definition (
30         id                      SERIAL  PRIMARY KEY,
31         code            TEXT    UNIQUE NOT NULL,
32         description     TEXT,
33         xpath           TEXT    NOT NULL,
34         remove          TEXT    NOT NULL DEFAULT '',
35         ident           BOOL    NOT NULL DEFAULT FALSE
36 );
37
38 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath ) VALUES ('title','Title of work','//*[@tag="245"]/*[contains("abcmnopr",@code)]');
39 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath ) VALUES ('author','Author of work','//*[@tag="100" or @tag="110" or @tag="113"]/*[contains("ad",@code)]');
40 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath ) VALUES ('language','Lanuage of work','//*[@tag="240"]/*[@code="l"][1]');
41 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath ) VALUES ('pagination','Pagination','//*[@tag="300"]/*[@code="a"][1]');
42 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath, ident, remove ) VALUES ('isbn','ISBN','//*[@tag="020"]/*[@code="a"]', TRUE, $r$(?:-|\s.+$)$r$);
43 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath, ident, remove ) VALUES ('issn','ISSN','//*[@tag="022"]/*[@code="a"]', TRUE, $r$(?:-|\s.+$)$r$);
44 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath ) VALUES ('price','Price','//*[@tag="020" or @tag="022"]/*[@code="c"][1]');
45 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath, ident ) VALUES ('rec_identifier','Accession Number','//*[@tag="001"]', TRUE);
46 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath, ident ) VALUES ('eg_tcn','TCN Value','//*[@tag="901"]/*[@code="a"]', TRUE);
47 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath ) VALUES ('eg_tcn_source','TCN Source','//*[@tag="901"]/*[@code="b"]', TRUE);
48 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath, ident ) VALUES ('eg_identifier','Internal ID','//*[@tag="901"]/*[@code="c"]', TRUE);
49 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath ) VALUES ('publisher','Publisher','//*[@tag="260"]/*[@code="b"][1]');
50 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath, remove ) VALUES ('pubdate','Publication Date','//*[@tag="260"]/*[@code="c"][1]',$r$\D$r$);
51 INSERT INTO vandelay.bib_attr_definition ( code, description, xpath ) VALUES ('edition','Edition','//*[@tag="250"]/*[@code="a"][1]');
52
53
54 CREATE TABLE vandelay.bib_queue (
55         queue_type          TEXT        NOT NULL DEFAULT 'bib' CHECK (queue_type = 'bib'),
56         item_attr_def   TEXT    REFERENCES vandelay.import_item_attr_definition (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
57         CONSTRAINT vand_bib_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
58 ) INHERITS (vandelay.queue);
59 ALTER TABLE vandelay.bib_queue ADD PRIMARY KEY (id);
60
61 CREATE TABLE vandelay.queued_bib_record (
62         queue           INT             NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
63         bib_source      INT             REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
64         imported_as     INT             REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
65 ) INHERITS (vandelay.queued_record);
66 ALTER TABLE vandelay.queued_bib_record ADD PRIMARY KEY (id);
67
68 CREATE TABLE vandelay.queued_bib_record_attr (
69         id                      BIGSERIAL       PRIMARY KEY,
70         record          BIGINT          NOT NULL REFERENCES vandelay.queued_bib_record (id) DEFERRABLE INITIALLY DEFERRED,
71         field           INT                     NOT NULL REFERENCES vandelay.bib_attr_definition (id) DEFERRABLE INITIALLY DEFERRED,
72         attr_value      TEXT            NOT NULL
73 );
74
75 CREATE TABLE vandelay.bib_match (
76         id                              BIGSERIAL       PRIMARY KEY,
77         field_type              TEXT            NOT NULL CHECK (field_type in ('isbn','tcn_value','id')),
78         matched_attr    INT                     REFERENCES vandelay.queued_bib_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
79         queued_record   BIGINT          REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
80         eg_record               BIGINT          REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
81 );
82
83 -- Each TEXT field (other than 'name') should hold an XPath predicate for pulling the data needed
84 -- DROP TABLE vandelay.import_item_attr_definition CASCADE;
85 CREATE TABLE vandelay.import_item_attr_definition (
86     id              BIGSERIAL   PRIMARY KEY,
87     owner           INT         NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
88     name            TEXT        NOT NULL,
89     tag             TEXT        NOT NULL,
90     keep            BOOL        NOT NULL DEFAULT FALSE,
91     owning_lib      TEXT,
92     circ_lib        TEXT,
93     call_number     TEXT,
94     copy_number     TEXT,
95     status          TEXT,
96     location        TEXT,
97     circulate       TEXT,
98     deposit         TEXT,
99     deposit_amount  TEXT,
100     ref             TEXT,
101     holdable        TEXT,
102     price           TEXT,
103     barcode         TEXT,
104     circ_modifier   TEXT,
105     circ_as_type    TEXT,
106     alert_message   TEXT,
107     opac_visible    TEXT,
108     pub_note_title  TEXT,
109     pub_note        TEXT,
110     priv_note_title TEXT,
111     priv_note       TEXT,
112         CONSTRAINT vand_import_item_attr_def_idx UNIQUE (owner,name)
113 );
114
115 INSERT INTO vandelay.import_item_attr_definition (
116     owner, name, tag, owning_lib, circ_lib, location,
117     call_number, circ_modifier, barcode, price, copy_number,
118     circulate, ref, holdable, opac_visible, status
119 ) VALUES (
120     1,
121     'Evergreen 852 export format',
122     '852',
123     '[@code = "b"][1]',
124     '[@code = "b"][2]',
125     'c',
126     'j',
127     'g',
128     'p',
129     'y',
130     't',
131     '[@code = "x" and text() = "ncirculating"]',
132     '[@code = "x" and text() = "reference"]',
133     '[@code = "x" and text() = "holdable"]',
134     '[@code = "x" and text() = "visible"]',
135     'z'
136 );
137
138 INSERT INTO vandelay.import_item_attr_definition (
139     owner,
140     name,
141     tag,
142     owning_lib,
143     location,
144     call_number,
145     circ_modifier,
146     barcode,
147     price,
148     status
149 ) VALUES (
150     1,
151     'Unicorn Import format -- 999',
152     '999',
153     'm',
154     'l',
155     'a',
156     't',
157     'i',
158     'p',
159     'k'
160 );
161
162 -- DROP TABLE vandelay.import_item CASCADE;
163 CREATE TABLE vandelay.import_item (
164     id              BIGSERIAL   PRIMARY KEY,
165     definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id),
166     owning_lib      INT,
167     circ_lib        INT,
168     call_number     TEXT,
169     copy_number     INT,
170     status          INT,
171     location        INT,
172     circulate       BOOL,
173     deposit         BOOL,
174     deposit_amount  NUMERIC(8,2),
175     ref             BOOL,
176     holdable        BOOL,
177     price           NUMERIC(8,2),
178     barcode         TEXT,
179     circ_modifier   TEXT,
180     circ_as_type    TEXT,
181     alert_message   TEXT,
182     pub_note        TEXT,
183     priv_note       TEXT,
184     opac_visible    BOOL
185 );
186  
187 CREATE TABLE vandelay.import_bib_trash_fields (
188     id              BIGSERIAL   PRIMARY KEY,
189     owner           INT         NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
190     field           TEXT        NOT NULL,
191         CONSTRAINT vand_import_bib_trash_fields_idx UNIQUE (owner,field)
192 );
193
194 CREATE OR REPLACE FUNCTION vandelay.strip_field ( xml TEXT, field TEXT ) RETURNS TEXT AS $_$
195
196     use MARC::Record;
197     use MARC::File::XML;
198
199     my $xml = shift;
200     my $field_spec = shift;
201
202     my @fields 
203
204     my $r = MARC::Record->new_from_xml( $xml );
205     $r->delete_field( $_ ) for ( $r->field( $field_spec ) );
206
207     $xml = $r->as_xml_record;
208     $xml =~ s/^<\?.+?\?>$//mo;
209     $xml =~ s/\n//sgo;
210     $xml =~ s/>\s+</></sgo;
211
212     return $xml;
213
214 $_$ LANGUAGE PLPERLU;
215
216
217 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
218 DECLARE
219
220     owning_lib      TEXT;
221     circ_lib        TEXT;
222     call_number     TEXT;
223     copy_number     TEXT;
224     status          TEXT;
225     location        TEXT;
226     circulate       TEXT;
227     deposit         TEXT;
228     deposit_amount  TEXT;
229     ref             TEXT;
230     holdable        TEXT;
231     price           TEXT;
232     barcode         TEXT;
233     circ_modifier   TEXT;
234     circ_as_type    TEXT;
235     alert_message   TEXT;
236     opac_visible    TEXT;
237     pub_note        TEXT;
238     priv_note       TEXT;
239
240     attr_def        RECORD;
241     tmp_attr_set    RECORD;
242     attr_set        vandelay.import_item%ROWTYPE;
243
244     xpath           TEXT;
245
246 BEGIN
247
248     SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
249
250     IF FOUND THEN
251
252         attr_set.definition := attr_def.id; 
253     
254         -- Build the combined XPath
255     
256         owning_lib :=
257             CASE
258                 WHEN attr_def.owning_lib IS NULL THEN 'null()'
259                 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
260                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
261             END;
262     
263         circ_lib :=
264             CASE
265                 WHEN attr_def.circ_lib IS NULL THEN 'null()'
266                 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
267                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
268             END;
269     
270         call_number :=
271             CASE
272                 WHEN attr_def.call_number IS NULL THEN 'null()'
273                 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
274                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
275             END;
276     
277         copy_number :=
278             CASE
279                 WHEN attr_def.copy_number IS NULL THEN 'null()'
280                 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
281                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
282             END;
283     
284         status :=
285             CASE
286                 WHEN attr_def.status IS NULL THEN 'null()'
287                 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
288                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
289             END;
290     
291         location :=
292             CASE
293                 WHEN attr_def.location IS NULL THEN 'null()'
294                 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
295                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
296             END;
297     
298         circulate :=
299             CASE
300                 WHEN attr_def.circulate IS NULL THEN 'null()'
301                 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
302                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
303             END;
304     
305         deposit :=
306             CASE
307                 WHEN attr_def.deposit IS NULL THEN 'null()'
308                 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
309                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
310             END;
311     
312         deposit_amount :=
313             CASE
314                 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
315                 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
316                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
317             END;
318     
319         ref :=
320             CASE
321                 WHEN attr_def.ref IS NULL THEN 'null()'
322                 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
323                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
324             END;
325     
326         holdable :=
327             CASE
328                 WHEN attr_def.holdable IS NULL THEN 'null()'
329                 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
330                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
331             END;
332     
333         price :=
334             CASE
335                 WHEN attr_def.price IS NULL THEN 'null()'
336                 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
337                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
338             END;
339     
340         barcode :=
341             CASE
342                 WHEN attr_def.barcode IS NULL THEN 'null()'
343                 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
344                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
345             END;
346     
347         circ_modifier :=
348             CASE
349                 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
350                 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
351                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
352             END;
353     
354         circ_as_type :=
355             CASE
356                 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
357                 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
358                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
359             END;
360     
361         alert_message :=
362             CASE
363                 WHEN attr_def.alert_message IS NULL THEN 'null()'
364                 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
365                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
366             END;
367     
368         opac_visible :=
369             CASE
370                 WHEN attr_def.opac_visible IS NULL THEN 'null()'
371                 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
372                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
373             END;
374
375         pub_note :=
376             CASE
377                 WHEN attr_def.pub_note IS NULL THEN 'null()'
378                 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
379                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
380             END;
381         priv_note :=
382             CASE
383                 WHEN attr_def.priv_note IS NULL THEN 'null()'
384                 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
385                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
386             END;
387     
388     
389         xpath := 
390             owning_lib      || '|' || 
391             circ_lib        || '|' || 
392             call_number     || '|' || 
393             copy_number     || '|' || 
394             status          || '|' || 
395             location        || '|' || 
396             circulate       || '|' || 
397             deposit         || '|' || 
398             deposit_amount  || '|' || 
399             ref             || '|' || 
400             holdable        || '|' || 
401             price           || '|' || 
402             barcode         || '|' || 
403             circ_modifier   || '|' || 
404             circ_as_type    || '|' || 
405             alert_message   || '|' || 
406             pub_note        || '|' || 
407             priv_note       || '|' || 
408             opac_visible;
409
410         -- RAISE NOTICE 'XPath: %', xpath;
411         
412         FOR tmp_attr_set IN
413                 SELECT  *
414                   FROM  xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
415                             AS t( id BIGINT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
416                                   dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
417                                   circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
418         LOOP
419     
420             tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
421             tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
422
423             tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
424             tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
425     
426             SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
427             SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
428             SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
429     
430             SELECT  id INTO attr_set.location
431               FROM  asset.copy_location
432               WHERE LOWER(name) = LOWER(tmp_attr_set.cl)
433                     AND owning_lib = COALESCE(attr_set.owning_lib, attr_set.circ_lib); -- INT
434     
435             attr_set.circulate      :=
436                 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
437                 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
438
439             attr_set.deposit        :=
440                 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
441                 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
442
443             attr_set.holdable       :=
444                 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
445                 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
446
447             attr_set.opac_visible   :=
448                 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
449                 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
450
451             attr_set.ref            :=
452                 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
453                 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
454     
455             attr_set.copy_number    := tmp_attr_set.cnum::INT; -- INT,
456             attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
457             attr_set.price          := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
458     
459             attr_set.call_number    := tmp_attr_set.cn; -- TEXT
460             attr_set.barcode        := tmp_attr_set.bc; -- TEXT,
461             attr_set.circ_modifier  := tmp_attr_set.circ_mod; -- TEXT,
462             attr_set.circ_as_type   := tmp_attr_set.circ_as; -- TEXT,
463             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
464             attr_set.pub_note       := tmp_attr_set.note; -- TEXT,
465             attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
466             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
467     
468             RETURN NEXT attr_set;
469     
470         END LOOP;
471     
472     END IF;
473
474 END;
475 $$ LANGUAGE PLPGSQL;
476
477
478 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
479 DECLARE
480     value   TEXT;
481     atype   TEXT;
482     adef    RECORD;
483 BEGIN
484     FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
485
486         SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
487         IF (value IS NOT NULL AND value <> '') THEN
488             INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
489         END IF;
490
491     END LOOP;
492
493     RETURN NULL;
494 END;
495 $$ LANGUAGE PLPGSQL;
496
497 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
498 DECLARE
499     queue_rec   RECORD;
500     item_rule   RECORD;
501     item_data   vandelay.ingest_items%ROWTYPE;
502 BEGIN
503
504     SELECT * INTO queue_rec FROM vandelay.bib_queue WHERE id = NEW.queue;
505
506     FOR item_rule IN SELECT r.* FROM actor.org_unit_anscestors( queue_rec.owner ) o JOIN vandelay.import_item_attr_definition r ON ( r.owner = o.id ) LOOP
507         FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id, item_rule.id ) LOOP
508             INSERT INTO vandelay.import_item (
509                 record,
510                 definition,
511                 owning_lib,
512                 circ_lib,
513                 call_number,
514                 copy_number,
515                 status,
516                 location,
517                 circulate,
518                 deposit,
519                 deposit_amount,
520                 ref,
521                 holdable,
522                 price,
523                 barcode,
524                 circ_modifier,
525                 circ_as_type,
526                 alert_message,
527                 pub_note,
528                 priv_note,
529                 opac_visible
530             ) VALUES (
531                 NEW.id,
532                 item_data.definition,
533                 item_data.owning_lib,
534                 item_data.circ_lib,
535                 item_data.call_number,
536                 item_data.copy_number,
537                 item_data.status,
538                 item_data.location,
539                 item_data.circulate,
540                 item_data.deposit,
541                 item_data.deposit_amount,
542                 item_data.ref,
543                 item_data.holdable,
544                 item_data.price,
545                 item_data.barcode,
546                 item_data.circ_modifier,
547                 item_data.circ_as_type,
548                 item_data.alert_message,
549                 item_data.pub_note,
550                 item_data.priv_note,
551                 item_data.opac_visible
552             );
553         END LOOP;
554     END LOOP;
555
556     RETURN NULL;
557 END;
558 $func$ LANGUAGE PLPGSQL
559
560 CREATE OR REPLACE FUNCTION vandelay.match_bib_record ( ) RETURNS TRIGGER AS $func$
561 DECLARE
562     attr    RECORD;
563     eg_rec  RECORD;
564 BEGIN
565     FOR attr IN SELECT a.* FROM vandelay.queued_bib_record_attr a JOIN vandelay.bib_attr_definition d ON (d.id = a.field) WHERE record = NEW.id AND d.ident IS TRUE LOOP
566
567                 -- All numbers? check for an id match
568                 IF (attr.attr_value ~ $r$^\d+$$r$) THEN
569                 FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE id = attr.attr_value::BIGINT AND deleted IS FALSE LOOP
570                         INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, eg_rec.id);
571                         END LOOP;
572                 END IF;
573
574                 -- Looks like an ISBN? check for an isbn match
575                 IF (attr.attr_value ~* $r$^[0-9x]+$$r$ AND character_length(attr.attr_value) IN (10,13)) THEN
576                 FOR eg_rec IN EXECUTE $$SELECT * FROM metabib.full_rec fr WHERE fr.value LIKE LOWER('$$ || attr.attr_value || $$%') AND fr.tag = '020' AND fr.subfield = 'a'$$ LOOP
577                                 PERFORM id FROM biblio.record_entry WHERE id = eg_rec.record AND deleted IS FALSE;
578                                 IF FOUND THEN
579                                 INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('isbn', attr.id, NEW.id, eg_rec.record);
580                                 END IF;
581                         END LOOP;
582
583                         -- subcheck for isbn-as-tcn
584                     FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = 'i' || attr.attr_value AND deleted IS FALSE LOOP
585                             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
586                 END LOOP;
587                 END IF;
588
589                 -- check for an OCLC tcn_value match
590                 IF (attr.attr_value ~ $r$^o\d+$$r$) THEN
591                     FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = regexp_replace(attr.attr_value,'^o','ocm') AND deleted IS FALSE LOOP
592                             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
593                 END LOOP;
594                 END IF;
595
596                 -- check for a direct tcn_value match
597         FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = attr.attr_value AND deleted IS FALSE LOOP
598             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
599         END LOOP;
600
601     END LOOP;
602
603     RETURN NULL;
604 END;
605 $func$ LANGUAGE PLPGSQL;
606
607 CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
608 BEGIN
609     DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
610     DELETE FROM vandelay.import_item WHERE record = OLD.id;
611
612     IF TG_OP = 'UPDATE' THEN
613         RETURN NEW;
614     END IF;
615     RETURN OLD;
616 END;
617 $$ LANGUAGE PLPGSQL;
618
619 CREATE TRIGGER cleanup_bib_trigger
620     BEFORE UPDATE OR DELETE ON vandelay.queued_bib_record
621     FOR EACH ROW EXECUTE PROCEDURE vandelay.cleanup_bib_marc();
622
623 CREATE TRIGGER ingest_bib_trigger
624     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
625     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_marc();
626
627 CREATE TRIGGER ingest_item_trigger
628     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
629     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_items();
630
631 CREATE TRIGGER zz_match_bibs_trigger
632     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
633     FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
634
635
636 /* Authority stuff down here */
637 ---------------------------------------
638 CREATE TABLE vandelay.authority_attr_definition (
639         id                      SERIAL  PRIMARY KEY,
640         code            TEXT    UNIQUE NOT NULL,
641         description     TEXT,
642         xpath           TEXT    NOT NULL,
643         remove          TEXT    NOT NULL DEFAULT '',
644         ident           BOOL    NOT NULL DEFAULT FALSE
645 );
646 INSERT INTO vandelay.authority_attr_definition ( code, description, xpath, ident ) VALUES ('rec_identifier','Identifier','//*[@tag="001"]', TRUE);
647
648 CREATE TABLE vandelay.authority_queue (
649         queue_type      TEXT            NOT NULL DEFAULT 'authority' CHECK (queue_type = 'authority'),
650         CONSTRAINT vand_authority_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
651 ) INHERITS (vandelay.queue);
652 ALTER TABLE vandelay.authority_queue ADD PRIMARY KEY (id);
653
654 CREATE TABLE vandelay.queued_authority_record (
655         queue           INT     NOT NULL REFERENCES vandelay.authority_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
656         imported_as     INT     REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
657 ) INHERITS (vandelay.queued_record);
658 ALTER TABLE vandelay.queued_authority_record ADD PRIMARY KEY (id);
659
660 CREATE TABLE vandelay.queued_authority_record_attr (
661         id                      BIGSERIAL       PRIMARY KEY,
662         record          BIGINT          NOT NULL REFERENCES vandelay.queued_authority_record (id) DEFERRABLE INITIALLY DEFERRED,
663         field           INT                     NOT NULL REFERENCES vandelay.authority_attr_definition (id) DEFERRABLE INITIALLY DEFERRED,
664         attr_value      TEXT            NOT NULL
665 );
666
667 CREATE TABLE vandelay.authority_match (
668         id                              BIGSERIAL       PRIMARY KEY,
669         matched_attr    INT                     REFERENCES vandelay.queued_authority_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
670         queued_record   BIGINT          REFERENCES vandelay.queued_authority_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
671         eg_record               BIGINT          REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
672 );
673
674 CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
675 DECLARE
676     value   TEXT;
677     atype   TEXT;
678     adef    RECORD;
679 BEGIN
680     FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
681
682         SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
683         IF (value IS NOT NULL AND value <> '') THEN
684             INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
685         END IF;
686
687     END LOOP;
688
689     RETURN NULL;
690 END;
691 $$ LANGUAGE PLPGSQL;
692
693 CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
694 BEGIN
695     DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
696     IF TG_OP = 'UPDATE' THEN
697         RETURN NEW;
698     END IF;
699     RETURN OLD;
700 END;
701 $$ LANGUAGE PLPGSQL;
702
703 CREATE TRIGGER cleanup_authority_trigger
704     BEFORE UPDATE OR DELETE ON vandelay.queued_authority_record
705     FOR EACH ROW EXECUTE PROCEDURE vandelay.cleanup_authority_marc();
706
707 CREATE TRIGGER ingest_authority_trigger
708     AFTER INSERT OR UPDATE ON vandelay.queued_authority_record
709     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_authority_marc();
710
711 COMMIT;
712