]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/reporter-schema.sql
allow multiple 260 tags in summary view
[Evergreen.git] / Open-ILS / src / sql / Pg / reporter-schema.sql
1 /*
2  * Copyright (C) 2004-2008  Georgia Public Library Service
3  * Copyright (C) 2007-2008  Equinox Software, Inc.
4  * Mike Rylander <miker@esilibrary.com> 
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 DROP SCHEMA reporter CASCADE;
19
20 BEGIN;
21
22 CREATE SCHEMA reporter;
23
24 CREATE TABLE reporter.template_folder (
25         id              SERIAL                          PRIMARY KEY,
26         parent          INT                             REFERENCES reporter.template_folder (id) DEFERRABLE INITIALLY DEFERRED,
27         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
28         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
29         name            TEXT                            NOT NULL,
30         shared          BOOL                            NOT NULL DEFAULT FALSE,
31         share_with      INT                             REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED
32 );
33 CREATE INDEX rpt_tmpl_fldr_owner_idx ON reporter.template_folder (owner);
34 CREATE UNIQUE INDEX rpt_template_folder_once_parent_idx ON reporter.template_folder (name,parent);
35 CREATE UNIQUE INDEX rpt_template_folder_once_idx ON reporter.template_folder (name,owner) WHERE parent IS NULL;
36
37 CREATE TABLE reporter.report_folder (
38         id              SERIAL                          PRIMARY KEY,
39         parent          INT                             REFERENCES reporter.report_folder (id) DEFERRABLE INITIALLY DEFERRED,
40         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
41         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
42         name            TEXT                            NOT NULL,
43         shared          BOOL                            NOT NULL DEFAULT FALSE,
44         share_with      INT                             REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED
45 );
46 CREATE INDEX rpt_rpt_fldr_owner_idx ON reporter.report_folder (owner);
47 CREATE UNIQUE INDEX rpt_report_folder_once_parent_idx ON reporter.report_folder (name,parent);
48 CREATE UNIQUE INDEX rpt_report_folder_once_idx ON reporter.report_folder (name,owner) WHERE parent IS NULL;
49
50 CREATE TABLE reporter.output_folder (
51         id              SERIAL                          PRIMARY KEY,
52         parent          INT                             REFERENCES reporter.output_folder (id) DEFERRABLE INITIALLY DEFERRED,
53         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
54         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
55         name            TEXT                            NOT NULL,
56         shared          BOOL                            NOT NULL DEFAULT FALSE,
57         share_with      INT                             REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED
58 );
59 CREATE INDEX rpt_output_fldr_owner_idx ON reporter.output_folder (owner);
60 CREATE UNIQUE INDEX rpt_output_folder_once_parent_idx ON reporter.output_folder (name,parent);
61 CREATE UNIQUE INDEX rpt_output_folder_once_idx ON reporter.output_folder (name,owner) WHERE parent IS NULL;
62
63
64 CREATE TABLE reporter.template (
65         id              SERIAL                          PRIMARY KEY,
66         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
67         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
68         name            TEXT                            NOT NULL,
69         description     TEXT                            NOT NULL,
70         data            TEXT                            NOT NULL,
71         folder          INT                             NOT NULL REFERENCES reporter.template_folder (id) DEFERRABLE INITIALLY DEFERRED
72 );
73 CREATE INDEX rpt_tmpl_owner_idx ON reporter.template (owner);
74 CREATE INDEX rpt_tmpl_fldr_idx ON reporter.template (folder);
75 CREATE UNIQUE INDEX rtp_template_folder_once_idx ON reporter.template (name,folder);
76
77 CREATE TABLE reporter.report (
78         id              SERIAL                          PRIMARY KEY,
79         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
80         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
81         name            TEXT                            NOT NULL DEFAULT '',
82         description     TEXT                            NOT NULL DEFAULT '',
83         template        INT                             NOT NULL REFERENCES reporter.template (id) DEFERRABLE INITIALLY DEFERRED,
84         data            TEXT                            NOT NULL,
85         folder          INT                             NOT NULL REFERENCES reporter.report_folder (id) DEFERRABLE INITIALLY DEFERRED,
86         recur           BOOL                            NOT NULL DEFAULT FALSE,
87         recurrence      INTERVAL
88 );
89 CREATE INDEX rpt_rpt_owner_idx ON reporter.report (owner);
90 CREATE INDEX rpt_rpt_fldr_idx ON reporter.report (folder);
91 CREATE UNIQUE INDEX rtp_report_folder_once_idx ON reporter.report (name,folder);
92
93 CREATE TABLE reporter.schedule (
94         id              SERIAL                          PRIMARY KEY,
95         report          INT                             NOT NULL REFERENCES reporter.report (id) DEFERRABLE INITIALLY DEFERRED,
96         folder          INT                             NOT NULL REFERENCES reporter.output_folder (id) DEFERRABLE INITIALLY DEFERRED,
97         runner          INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
98         run_time        TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
99         start_time      TIMESTAMP WITH TIME ZONE,
100         complete_time   TIMESTAMP WITH TIME ZONE,
101         email           TEXT,
102         excel_format    BOOL                            NOT NULL DEFAULT TRUE,
103         html_format     BOOL                            NOT NULL DEFAULT TRUE,
104         csv_format      BOOL                            NOT NULL DEFAULT TRUE,
105         chart_pie       BOOL                            NOT NULL DEFAULT FALSE,
106         chart_bar       BOOL                            NOT NULL DEFAULT FALSE,
107         chart_line      BOOL                            NOT NULL DEFAULT FALSE,
108         error_code      INT,
109         error_text      TEXT
110 );
111 CREATE INDEX rpt_sched_runner_idx ON reporter.schedule (runner);
112 CREATE INDEX rpt_sched_folder_idx ON reporter.schedule (folder);
113
114 CREATE OR REPLACE VIEW reporter.simple_record AS
115 SELECT  r.id,
116         s.metarecord,
117         r.fingerprint,
118         r.quality,
119         r.tcn_source,
120         r.tcn_value,
121         title.value AS title,
122         uniform_title.value AS uniform_title,
123         author.value AS author,
124         publisher.value AS publisher,
125         SUBSTRING(pubdate.value FROM $$\d+$$) AS pubdate,
126         series_title.value AS series_title,
127         series_statement.value AS series_statement,
128         summary.value AS summary,
129         ARRAY_ACCUM( SUBSTRING(isbn.value FROM $$^\S+$$) ) AS isbn,
130         ARRAY_ACCUM( REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) AS issn,
131         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '650' AND subfield = 'a' AND record = r.id)) AS topic_subject,
132         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '651' AND subfield = 'a' AND record = r.id)) AS geographic_subject,
133         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '655' AND subfield = 'a' AND record = r.id)) AS genre,
134         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '600' AND subfield = 'a' AND record = r.id)) AS name_subject,
135         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '610' AND subfield = 'a' AND record = r.id)) AS corporate_subject,
136         ARRAY((SELECT value FROM metabib.full_rec WHERE tag = '856' AND subfield IN ('3','y','u') AND record = r.id ORDER BY CASE WHEN subfield IN ('3','y') THEN 0 ELSE 1 END)) AS external_uri
137   FROM  biblio.record_entry r
138         JOIN metabib.metarecord_source_map s ON (s.source = r.id)
139         LEFT JOIN metabib.full_rec uniform_title ON (r.id = uniform_title.record AND uniform_title.tag = '240' AND uniform_title.subfield = 'a')
140         LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
141         LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag = '100' AND author.subfield = 'a')
142         LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND publisher.tag = '260' AND publisher.subfield = 'b')
143         LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND pubdate.tag = '260' AND pubdate.subfield = 'c')
144         LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
145         LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
146         LEFT JOIN metabib.full_rec series_title ON (r.id = series_title.record AND series_title.tag IN ('830','440') AND series_title.subfield = 'a')
147         LEFT JOIN metabib.full_rec series_statement ON (r.id = series_statement.record AND series_statement.tag = '490' AND series_statement.subfield = 'a')
148         LEFT JOIN metabib.full_rec summary ON (r.id = summary.record AND summary.tag = '520' AND summary.subfield = 'a')
149   GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14;
150
151 CREATE OR REPLACE VIEW reporter.old_super_simple_record AS
152 SELECT  r.id,
153         r.fingerprint,
154         r.quality,
155         r.tcn_source,
156         r.tcn_value,
157         title.value AS title,
158         FIRST(author.value) AS author,
159         ARRAY_TO_STRING(ARRAY_ACCUM( DISTINCT publisher.value), ', ') AS publisher,
160         ARRAY_TO_STRING(ARRAY_ACCUM( DISTINCT SUBSTRING(pubdate.value FROM $$\d+$$) ), ', ') AS pubdate,
161         ARRAY_ACCUM( DISTINCT SUBSTRING(isbn.value FROM $$^\S+$$) ) AS isbn,
162         ARRAY_ACCUM( DISTINCT SUBSTRING(issn.value FROM $$^\S+$$) ) AS issn
163   FROM  biblio.record_entry r
164         LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
165         LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag IN ('100','110','111') AND author.subfield = 'a')
166         LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND publisher.tag = '260' AND publisher.subfield = 'b')
167         LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND pubdate.tag = '260' AND pubdate.subfield = 'c')
168         LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
169         LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
170   GROUP BY 1,2,3,4,5,6;
171
172 CREATE TABLE reporter.materialized_simple_record AS SELECT * FROM reporter.old_super_simple_record WHERE 1=0;
173 ALTER TABLE reporter.materialized_simple_record ADD PRIMARY KEY (id);
174
175 CREATE VIEW reporter.super_simple_record AS SELECT * FROM reporter.materialized_simple_record;
176
177 CREATE OR REPLACE FUNCTION reporter.simple_rec_update (r_id BIGINT, deleted BOOL) RETURNS BOOL AS $$
178 BEGIN
179
180     DELETE FROM reporter.materialized_simple_record WHERE id = r_id;
181
182     IF NOT deleted THEN
183         INSERT INTO reporter.materialized_simple_record SELECT DISTINCT ON (id) * FROM reporter.old_super_simple_record WHERE id = r_id;
184     END IF;
185
186     RETURN TRUE;
187
188 END;
189 $$ LANGUAGE PLPGSQL;
190
191 CREATE OR REPLACE FUNCTION reporter.simple_rec_update (r_id BIGINT) RETURNS BOOL AS $$
192     SELECT reporter.simple_rec_update($1, FALSE);
193 $$ LANGUAGE SQL;
194
195 CREATE OR REPLACE FUNCTION reporter.simple_rec_delete (r_id BIGINT) RETURNS BOOL AS $$
196     SELECT reporter.simple_rec_update($1, TRUE);
197 $$ LANGUAGE SQL;
198
199 CREATE OR REPLACE FUNCTION reporter.simple_rec_trigger () RETURNS TRIGGER AS $func$
200 BEGIN
201     IF TG_OP = 'DELETE' THEN
202         PERFORM reporter.simple_rec_delete(NEW.id);
203     ELSE
204         PERFORM reporter.simple_rec_update(NEW.id);
205     END IF;
206
207     RETURN NEW;
208 END;
209 $func$ LANGUAGE PLPGSQL;
210
211 CREATE OR REPLACE FUNCTION reporter.simple_rec_sync () RETURNS TRIGGER AS $$
212 DECLARE
213     r_id        BIGINT;
214     deleted     BOOL;
215 BEGIN
216     IF TG_OP IN ('DELETE') THEN
217         r_id := OLD.record;
218         deleted := TRUE;
219     ELSE
220         r_id := NEW.record;
221         deleted := FALSE;
222     END IF;
223
224     PERFORM reporter.simple_rec_update(r_id, deleted);
225
226     IF deleted THEN
227         RETURN OLD;
228     ELSE
229         RETURN NEW;
230     END IF;
231
232 END;
233 $$ LANGUAGE PLPGSQL;
234
235 --
236 -- Disabling this by default for now, but we'll keep it around
237 --
238 --CREATE TRIGGER zzz_update_materialized_simple_record_tgr
239 --    AFTER INSERT OR UPDATE OR DELETE ON metabib.real_full_rec
240 --    FOR EACH ROW EXECUTE PROCEDURE reporter.simple_rec_sync();
241
242 CREATE OR REPLACE FUNCTION reporter.simple_rec_bib_sync () RETURNS TRIGGER AS $$
243 BEGIN
244     IF NEW.deleted THEN
245         DELETE FROM reporter.materialized_simple_record WHERE id = NEW.id;
246         RETURN NEW;
247     ELSE
248         RETURN NEW;
249     END IF;
250 END;
251 $$ LANGUAGE PLPGSQL;
252
253 --CREATE TRIGGER zzz_update_materialized_simple_rec_delete_tgr
254 --    AFTER UPDATE ON biblio.record_entry
255 --    FOR EACH ROW EXECUTE PROCEDURE reporter.simple_rec_bib_sync();
256
257
258 CREATE OR REPLACE FUNCTION reporter.disable_materialized_simple_record_trigger () RETURNS VOID AS $$
259     DROP TRIGGER IF EXISTS zzz_update_materialized_simple_record_tgr ON metabib.real_full_rec;
260 $$ LANGUAGE SQL;
261
262 CREATE OR REPLACE FUNCTION reporter.enable_materialized_simple_record_trigger () RETURNS VOID AS $$
263
264     TRUNCATE TABLE reporter.materialized_simple_record;
265
266     INSERT INTO reporter.materialized_simple_record
267         (id,fingerprint,quality,tcn_source,tcn_value,title,author,publisher,pubdate,isbn,issn)
268         SELECT DISTINCT ON (id) * FROM reporter.old_super_simple_record;
269
270     CREATE TRIGGER zzz_update_materialized_simple_record_tgr
271         AFTER INSERT OR UPDATE OR DELETE ON metabib.real_full_rec
272         FOR EACH ROW EXECUTE PROCEDURE reporter.simple_rec_sync();
273
274 $$ LANGUAGE SQL;
275
276 CREATE OR REPLACE FUNCTION reporter.refresh_materialized_simple_record () RETURNS VOID AS $$
277     SELECT reporter.disable_materialized_simple_record_trigger();
278     SELECT reporter.enable_materialized_simple_record_trigger();
279 $$ LANGUAGE SQL;
280
281 CREATE OR REPLACE VIEW reporter.demographic AS
282 SELECT  u.id,
283         u.dob,
284         CASE
285                 WHEN u.dob IS NULL
286                         THEN 'Adult'
287                 WHEN AGE(u.dob) > '18 years'::INTERVAL
288                         THEN 'Adult'
289                 ELSE 'Juvenile'
290         END AS general_division
291   FROM  actor.usr u;
292
293 CREATE OR REPLACE VIEW reporter.circ_type AS
294 SELECT  id,
295         CASE WHEN opac_renewal OR phone_renewal OR desk_renewal
296                 THEN 'RENEWAL'
297                 ELSE 'CHECKOUT'
298         END AS "type"
299   FROM  action.circulation;
300
301 CREATE OR REPLACE VIEW reporter.hold_request_record AS
302 SELECT  id,
303         target,
304         hold_type,
305         CASE
306                 WHEN hold_type = 'T'
307                         THEN target
308                 WHEN hold_type = 'V'
309                         THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = ahr.target)
310                 WHEN hold_type = 'C'
311                         THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = ahr.target)
312                 WHEN hold_type = 'M'
313                         THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = ahr.target)
314         END AS bib_record
315   FROM  action.hold_request ahr;
316
317 CREATE OR REPLACE VIEW reporter.xact_billing_totals AS
318 SELECT  b.xact,
319         SUM( CASE WHEN b.voided THEN 0 ELSE amount END ) as unvoided,
320         SUM( CASE WHEN b.voided THEN amount ELSE 0 END ) as voided,
321         SUM( amount ) as total
322   FROM  money.billing b
323   GROUP BY 1;
324
325 CREATE OR REPLACE VIEW reporter.xact_paid_totals AS
326 SELECT  b.xact,
327         SUM( CASE WHEN b.voided THEN 0 ELSE amount END ) as unvoided,
328         SUM( CASE WHEN b.voided THEN amount ELSE 0 END ) as voided,
329         SUM( amount ) as total
330   FROM  money.payment b
331   GROUP BY 1;
332
333 CREATE OR REPLACE VIEW reporter.overdue_circs AS
334 SELECT  *
335   FROM  "action".circulation
336   WHERE checkin_time is null
337         AND (stop_fines NOT IN ('LOST','CLAIMSRETURNED') OR stop_fines IS NULL)
338         AND due_date < now();
339
340 CREATE OR REPLACE VIEW reporter.overdue_reports AS
341  SELECT s.id, c.barcode AS runner_barcode, r.name, s.run_time, s.run_time - now() AS scheduled_wait_time
342    FROM reporter.schedule s
343    JOIN reporter.report r ON r.id = s.report
344    JOIN actor.usr u ON s.runner = u.id
345    JOIN actor.card c ON c.id = u.card
346   WHERE s.start_time IS NULL AND s.run_time < now();
347
348 CREATE OR REPLACE VIEW reporter.pending_reports AS
349  SELECT s.id, c.barcode AS runner_barcode, r.name, s.run_time, s.run_time - now() AS scheduled_wait_time
350    FROM reporter.schedule s
351    JOIN reporter.report r ON r.id = s.report
352    JOIN actor.usr u ON s.runner = u.id
353    JOIN actor.card c ON c.id = u.card
354   WHERE s.start_time IS NULL;
355
356 CREATE OR REPLACE VIEW reporter.currently_running AS
357  SELECT s.id, c.barcode AS runner_barcode, r.name, s.run_time, s.run_time - now() AS scheduled_wait_time
358    FROM reporter.schedule s
359    JOIN reporter.report r ON r.id = s.report
360    JOIN actor.usr u ON s.runner = u.id
361    JOIN actor.card c ON c.id = u.card
362   WHERE s.start_time IS NOT NULL AND s.complete_time IS NULL;
363
364 COMMIT;
365