File: gmClin-Vaccination-dynamic.sql

package info (click to toggle)
gnumed-server 21.11-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 45,408 kB
  • ctags: 3,891
  • sloc: sql: 1,210,640; python: 13,526; sh: 1,476; makefile: 19
file content (772 lines) | stat: -rw-r--r-- 24,977 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
-- Project: GNUmed - vaccination related dynamic relations
-- ===================================================================
-- $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/server/sql/gmClin-Vaccination-dynamic.sql,v $
-- $Revision: 1.8 $
-- license: GPL v2 or later
-- author: Ian Haywood, Karsten Hilbert, Richard Terry

-- ===================================================================
-- force terminate + exit(3) on errors if non-interactive
\set ON_ERROR_STOP 1

-- ============================================
-- vacc_indication --
select audit.add_table_for_audit('clin', 'vacc_indication');

comment on table clin.vacc_indication is
	'definition of indications for vaccinations';
comment on column clin.vacc_indication.description is
	'description of indication, eg "Measles",
	 note that this does not have to be a scientific
	 diagnosis, it is simply intended to be an agreed-upon,
	 medically-comprehensible unique identifier for
	 the indication';

-- vacc_route --
select audit.add_table_for_audit('clin', 'vacc_route');

comment on table clin.vacc_route is
	'definition of route via which vaccine is given,
	 currently i.m. and p.o. only but may include
	 "via genetically engineered food" etc in the
	 future';

-- vaccine --
select audit.add_table_for_audit('clin', 'vaccine');

comment on table clin.vaccine is
	'definition of a vaccine as available on the market';
comment on column clin.vaccine.id_route is
	'route this vaccine is given';
comment on column clin.vaccine.trade_name is
	'full name the vaccine is traded under';
comment on column clin.vaccine.short_name is
	'common, maybe practice-specific shorthand name
	 for referring to this vaccine';
comment on column clin.vaccine.is_live is
	'whether this is a live vaccine';
comment on column clin.vaccine.min_age is
	'minimum age this vaccine is licensed for according to the information by the manufacturer';
comment on column clin.vaccine.max_age is
	'maximum age this vaccine is licensed for according to the information
	 by the manufacturer, use "5555 years" to indicate "no maximum age"';


-- clin.vaccine_batches --
select audit.add_table_for_audit('clin', 'vaccine_batches');

comment on column clin.vaccine_batches.batch_no is
	'serial # of a batch of a given vaccine that
	 is awaiting usage in the fridge';


-- clin.lnk_vaccine2inds --
select audit.add_table_for_audit('clin', 'lnk_vaccine2inds');

comment on table clin.lnk_vaccine2inds is
	'links vaccines to their indications';


-- clin.vaccination_course --
select audit.add_table_for_audit('clin', 'vaccination_course');

comment on table clin.vaccination_course is
	'holds vaccination courses defined at a techno-medical level for
	 a single indication and will in many cases represent a part of
	 a "recommended multi-epitope schedule",
	 note that one organization can indeed recommend several courses
	 for one and the same indication - which then only differ in their
	 constraints, PostgreSQL does not currently offer the best tools
	 to enforce such constraints';
comment on column clin.vaccination_course.fk_recommended_by is
	'the source/organization which defined this course, can be used to
	 differentiate several locale-dependant courses for the same indication
	 and yet tell them apart';
comment on column clin.vaccination_course.fk_indication is
	'vaccination indication this course is targeted at';
comment on column clin.vaccination_course.is_active is
	'whether this course is active or not,
	 if False: do not newly *start* patients on this course';
comment on column clin.vaccination_course.comment is
	'a free-text comment on this vaccination course';


-- -- clin.vaccination_course_constraint --
select audit.add_table_for_audit('clin', 'vaccination_course_constraint');

comment on table clin.vaccination_course_constraint is
	'non-age constraints applying to a certain vaccination course
	 such as "female only", "Torres-Strait-Islander", etc.';
comment on column clin.vaccination_course_constraint.description is
	'description/label/name of the constraint';


-- -- clin.lnk_constraint2vacc_course --
select audit.add_table_for_audit('clin', 'lnk_constraint2vacc_course');

comment on table clin.lnk_constraint2vacc_course is
	'this table actually links constraints to vaccination courses';


-- -- clin.vaccincation_schedule --
select audit.add_table_for_audit('clin', 'vaccination_schedule');

comment on table clin.vaccination_schedule is
	'This table holds schedules as recommended by some authority
	 such as a Vaccination Council. There will be numerous schedules
	 depending on locale, constraints, age group etc. These schedules
	 may be single or multi-epitope depending on their definition.
	 A schedule acts as a convenient handle aggregating possibly several
	 vaccination courses under a common name.';
comment on column clin.vaccination_schedule.name is
	'name of the schedule as defined by some authority';


-- -- clin.lnk_vaccination_course2schedule --
select audit.add_table_for_audit('clin', 'lnk_vaccination_course2schedule');

comment on table clin.lnk_vaccination_course2schedule is
	'this table links vaccination courses for a single epitope
	 into schedules defined and recommended by a vaccination
	 council or similar entity';


-- -- clin.lnk_pat2vaccination_course --
select audit.add_table_for_audit('clin', 'lnk_pat2vaccination_course');
-- select add_table_for_notifies('lnk_pat2vaccination_course', 'vacc');

comment on table clin.lnk_pat2vaccination_course is
	'links patients to vaccination courses they are actually on,
	 this allows for per-patient selection of courses to be
	 followed, eg. children at different ages may be on different
	 vaccination courses or some people are on a course due
	 to a trip abroad while most others are not';


-- -- clin.vaccination_definition --
select audit.add_table_for_audit('clin', 'vaccination_definition');

comment on table clin.vaccination_definition is
	'defines a given vaccination event for a particular course';
comment on column clin.vaccination_definition.fk_course is
	'course to which this event belongs';
comment on column clin.vaccination_definition.is_booster is
	'does this definition represent a booster,
	 also set for quasi-booster courses such as
	 Influenza';
comment on column clin.vaccination_definition.seq_no is
	'sequence number for this vaccination event
	 within a particular course,
	 NULL if (is_booster == true)';
comment on column clin.vaccination_definition.min_age_due is
	'minimum age at which this shot is due';
comment on column clin.vaccination_definition.max_age_due is
	'maximum age at which this shot is due,
	 if max_age_due = "5555 years": no maximum age';
comment on column clin.vaccination_definition.min_interval is
	'if (is_booster == true):
		recommended interval for boostering
	 id (is_booster == false):
	 	minimum interval after previous vaccination,
		NULL if seq_no == 1';

\unset ON_ERROR_STOP
alter table clin.vaccination_definition
	drop constraint numbered_shot_xor_booster;
alter table clin.vaccination_definition
	drop constraint sensible_min_interval;
\set ON_ERROR_STOP 1

alter table clin.vaccination_definition
	add constraint numbered_shot_xor_booster
		check (
			((is_booster is true) and (seq_no is null)) or
			((is_booster is false) and (seq_no > 0))
		);

alter table clin.vaccination_definition
	add constraint sensible_min_interval
		check (
			((min_interval is null) and (seq_no = 1))
				or
			((min_interval is not null) and (min_interval > '0 seconds'::interval) and (is_booster is true))
				or
			((min_interval is not null) and (min_interval > '0 seconds'::interval) and (seq_no > 1))
		);

\unset ON_ERROR_STOP
drop trigger tr_ins_booster_must_have_base_immunity on clin.vaccination_definition;
drop trigger tr_upd_booster_must_have_base_immunity on clin.vaccination_definition;
drop trigger tr_del_booster_must_have_base_immunity on clin.vaccination_definition;
\set ON_ERROR_STOP 1

-- insert
create or replace function clin.f_ins_booster_must_have_base_immunity()
	returns trigger
	language 'plpgsql' as '
BEGIN
	-- do not worry about non-booster inserts
	if NEW.is_booster is false then
		return NEW;
	end if;
	-- only insert booster def if non-booster def exists
	perform 1 from clin.vaccination_definition where fk_course = NEW.fk_course and seq_no is not null;
	if FOUND then
		return NEW;
	end if;
	raise exception ''Cannot define booster shot for course [%]. There is no base immunization definition.'', NEW.fk_course;
	return null;
END;
';

create trigger tr_ins_booster_must_have_base_immunity
	before insert on clin.vaccination_definition
	for each row execute procedure clin.f_ins_booster_must_have_base_immunity();

-- update
create or replace function clin.f_upd_booster_must_have_base_immunity()
	returns trigger
	language 'plpgsql'
	as '
DECLARE
	msg text;
BEGIN
	-- do not worry about non-booster updates
	if NEW.is_booster is false then
		return null;
	end if;
	-- after update to booster still non-booster def available ?
	perform 1 from clin.vaccination_definition where fk_course = NEW.fk_course and seq_no is not null;
	if FOUND then
		return null;
	end if;
	msg := ''Cannot set vacc def ['' || NEW.pk || ''] to booster for course ['' || NEW.fk_course || '']. There would be no base immunization definition left.'';
	raise exception ''%'', msg;
	return null;
END;';

create trigger tr_upd_booster_must_have_base_immunity
	after update on clin.vaccination_definition
	for each row execute procedure clin.f_upd_booster_must_have_base_immunity();

-- delete
create or replace function clin.f_del_booster_must_have_base_immunity()
	returns trigger
	language 'plpgsql'
	as '
DECLARE
	msg text;
BEGIN
	-- do not worry about booster deletes
	if OLD.is_booster then
		return null;
	end if;
	-- any non-booster rows left ?
	perform 1 from clin.vaccination_definition where fk_course = OLD.fk_course and seq_no is not null;
	if FOUND then
		return null;
	end if;
	-- *any* rows left ?
	perform 1 from clin.vaccination_definition where fk_course = OLD.fk_course;
	if not FOUND then
		-- no problem
		return null;
	end if;
	-- any remaining rows can only be booster rows - which is a problem
	msg := ''Cannot delete last non-booster vacc def ['' || OLD.pk || ''] from course ['' || OLD.fk_course || '']. There would be only booster definitions left.'';
	raise exception ''%'', msg;
	return null;
END;';

create trigger tr_del_booster_must_have_base_immunity
	after delete on clin.vaccination_definition
	for each row execute procedure clin.f_del_booster_must_have_base_immunity();

-- clin.vaccination --
select audit.add_table_for_audit('clin', 'vaccination');
select add_table_for_notifies('clin', 'vaccination', 'vacc');

comment on table clin.vaccination is
	'holds vaccinations actually given';

-- clin.vaccination_course_constraint --
select audit.add_table_for_audit('clin', 'vaccination_course_constraint');

comment on table clin.vaccination_course_constraint is
	'holds constraints which apply to a vaccination course';

-- clin.lnk_constraint2vacc_course --
select audit.add_table_for_audit('clin', 'lnk_constraint2vacc_course');

comment on table clin.lnk_constraint2vacc_course is
	'links constraints to courses';

-- -----------------------------------------------------------------------
-- -- views --
-- -----------------------------------------------------------------------
\unset ON_ERROR_STOP
drop view clin.v_vaccine cascade;
\set ON_ERROR_STOP 1

create view clin.v_vaccine as
select
	v.pk as pk_vaccine,
	v.trade_name,
	v.short_name,
	vr.abbreviation as route_abbreviation,
	vr.description as route_description,
	_(vr.description) as l10n_route_description,
	v.is_live,
	v.min_age,
	v.max_age,
	v.comment,
	v.id_route as pk_route
from
	clin.vaccine v,
	clin.vacc_route vr
where
	v.id_route = vr.id
;

comment on view clin.v_vaccine is
	'denormalized data about vaccines';

--
\unset ON_ERROR_STOP
drop view clin.v_indications4vaccine cascade;
\set ON_ERROR_STOP 1

create view clin.v_indications4vaccine as
select
	v.trade_name,
	v.short_name,
	i.description as indication,
	_(i.description) as l10n_indication,
	v.is_live,
	v.min_age,
	v.max_age,
	v.comment,
	v.pk as pk_vaccine,
	v.id_route as pk_route,
	i.id as pk_vacc_indication
from
	clin.vaccine v,
	clin.vacc_indication i,
	clin.lnk_vaccine2inds lv2i
where
	v.pk = lv2i.fk_vaccine and
	i.id = lv2i.fk_indication
;

comment on view clin.v_indications4vaccine is
	'lists indications for vaccines';


\unset ON_ERROR_STOP
drop view clin.v_vaccination_courses cascade;
\set ON_ERROR_STOP 1

create view clin.v_vaccination_courses as
select
	vcourse.pk as pk_course,
	vind.description as indication,
	_(vind.description) as l10n_indication,
	(select name_long from ref_source where ref_source.pk = vcourse.fk_recommended_by)
		as recommended_by_name_long,
	(select name_short from ref_source where ref_source.pk = vcourse.fk_recommended_by)
		as recommended_by_name_short,
	(select version from ref_source where ref_source.pk = vcourse.fk_recommended_by)
		as recommended_by_version,
	(select max(vdef.seq_no) from clin.vaccination_definition vdef where vcourse.pk = vdef.fk_course)
		as shots,
	coalesce(vcourse.comment, '') as comment,
	(select vdef.min_age_due from clin.vaccination_definition vdef where vcourse.pk = vdef.fk_course and vdef.seq_no=1)
		as min_age_due,
	vcourse.is_active as is_active,
	vcourse.fk_indication as pk_indication,
	vcourse.fk_recommended_by as pk_recommended_by,
	vcourse.xmin as xmin_vaccination_course
from
	clin.vaccination_course vcourse,
	clin.vacc_indication vind
where
	vcourse.fk_indication = vind.id
;

comment on view clin.v_vaccination_courses is
	'all vaccination courses known to the system';

-- -----------------------------------------------------
\unset ON_ERROR_STOP
drop view clin.v_vaccination_definitions4course cascade;
\set ON_ERROR_STOP 1

create view clin.v_vaccination_definitions4course as
select
	vcourse.pk as pk_course,
	vind.description as indication,
	_(vind.description) as l10n_indication,
	coalesce(vcourse.comment, '') as course_comment,
	vcourse.is_active as is_active,
	vdef.id as pk_vaccination_definition,
	vdef.is_booster as is_booster,
	vdef.seq_no as vacc_seq_no,
	vdef.min_age_due as age_due_min,
	vdef.max_age_due as age_due_max,
	vdef.min_interval as min_interval,
	coalesce(vdef.comment, '') as vacc_comment,
	vind.id as pk_indication,
	vcourse.fk_recommended_by as pk_recommended_by
from
	clin.vaccination_course vcourse,
	clin.vacc_indication vind,
	clin.vaccination_definition vdef
where
	vcourse.pk = vdef.fk_course
		and
	vcourse.fk_indication = vind.id
order by
	indication,
	vacc_seq_no
;

comment on view clin.v_vaccination_definitions4course is
	'vaccination event definitions for all courses known to the system';


-- -----------------------------------------------------
\unset ON_ERROR_STOP
drop view clin.v_vaccination_courses_in_schedule;
\set ON_ERROR_STOP 1

create view clin.v_vaccination_courses_in_schedule as
select
	cvs.name as vaccination_schedule,
	cvc.is_active as is_active,
	cvc.fk_recommended_by as pk_recommended_by,
	cvc.comment as comment_course,
	cvs.comment as comment_schedule,
	cvc.pk as pk_vaccination_course,
	cvc.fk_indication as pk_indication,
	cvs.pk as pk_vaccination_schedule
from
	clin.vaccination_course cvc,
	clin.vaccination_schedule cvs,
	clin.lnk_vaccination_course2schedule clvc2s
where
	clvc2s.fk_course = cvc.pk
		and
	clvc2s.fk_schedule = cvs.pk
;

-- -----------------------------------------------------
create view clin.v_vacc_courses4pat as
select
	lp2vc.fk_patient as pk_patient,
	vvr.indication as indication,
	vvr.l10n_indication as l10n_indication,
	vvr.comment as comment,
	vvr.is_active as is_active,
	vvr.pk_course as pk_course,
	vvr.pk_indication as pk_indication,
	vvr.pk_recommended_by as pk_recommended_by
from
	clin.lnk_pat2vaccination_course lp2vc,
	clin.v_vaccination_courses vvr
where
	vvr.pk_course = lp2vc.fk_course
;

comment on view clin.v_vacc_courses4pat is
	'lists the vaccination courses a patient is actually on';

-- -----------------------------------------------------
create view clin.v_vaccs_scheduled4pat as
select
	vvr4p.pk_patient as pk_patient,
	vvr4p.indication as indication,
	vvr4p.l10n_indication as l10n_indication,
--	vvr4p.course as course, -- no name, use name of actual regime if any
	vvr4p.comment as course_comment,
	vvd4r.is_booster,
	vvd4r.vacc_seq_no,
	vvd4r.age_due_min,
	vvd4r.age_due_max,
	vvd4r.min_interval,
	vvd4r.vacc_comment as vacc_comment,
	vvd4r.pk_vaccination_definition as pk_vaccination_definition,
	vvr4p.pk_course as pk_course,
	vvr4p.pk_indication as pk_indication,
	vvr4p.pk_recommended_by as pk_recommended_by
from
	clin.v_vacc_courses4pat vvr4p,
	clin.v_vaccination_definitions4course vvd4r
where
	vvd4r.pk_course = vvr4p.pk_course
;

comment on view clin.v_vaccs_scheduled4pat is
	'vaccinations scheduled for a patient according
	 to the vaccination courses he/she is on';

-- -----------------------------------------------------
create view clin.v_pat_vaccinations4indication as
select
	v.fk_patient as pk_patient,
	v.id as pk_vaccination,
	v.clin_when as date,
	vi4v.indication,
	vi4v.l10n_indication,
	vi4v.trade_name as vaccine,
	vi4v.short_name as vaccine_short,
	v.batch_no as batch_no,
	v.site as site,
	coalesce(v.narrative, '') as narrative,
	vi4v.pk_vacc_indication as pk_indication,
	v.fk_provider as pk_provider,
	vi4v.pk_vaccine as pk_vaccine,
	vpep.pk_health_issue as pk_health_issue,
	v.fk_episode as pk_episode,
	v.fk_encounter as pk_encounter,
	v.modified_when as modified_when,
	v.modified_by as modified_by,
	v.xmin as xmin_vaccination
from
	clin.vaccination v,
	clin.v_indications4vaccine vi4v,
	clin.v_pat_episodes vpep
where
	vpep.pk_episode=v.fk_episode
		and
	v.fk_vaccine = vi4v.pk_vaccine
;

comment on view clin.v_pat_vaccinations4indication is
	'vaccinations a patient has actually received for the various
	 indications, we operate under the assumption that every shot
	 given counts toward base immunisation, eg. all shots are valid';

-- -----------------------------------------------------
create view clin.v_pat_missing_vaccs as
select
	vvs4p.pk_patient,
	vvs4p.indication,
	vvs4p.l10n_indication,
--	vvs4p.course,
	vvs4p.course_comment,
	vvs4p.vacc_seq_no as seq_no,
	case when vvs4p.age_due_max is null
		then (now() + coalesce(vvs4p.min_interval, vvs4p.age_due_min))
		else ((select dem.identity.dob from dem.identity where dem.identity.pk=vvs4p.pk_patient) + vvs4p.age_due_max)
	end as latest_due,
	-- note that ...
	-- ... 1) time_left ...
	case when vvs4p.age_due_max is null
		then coalesce(vvs4p.min_interval, vvs4p.age_due_min)
		else (((select dem.identity.dob from dem.identity where dem.identity.pk=vvs4p.pk_patient) + vvs4p.age_due_max) - now())
	end as time_left,
	-- ... and 2) amount_overdue ...
	case when vvs4p.age_due_max is null
		then coalesce(vvs4p.min_interval, vvs4p.age_due_min)
		else (now() - ((select dem.identity.dob from dem.identity where dem.identity.pk=vvs4p.pk_patient) + vvs4p.age_due_max))
	end as amount_overdue,
	-- ... are just the inverse of each other
	vvs4p.age_due_min,
	vvs4p.age_due_max,
	vvs4p.min_interval,
	vvs4p.vacc_comment,
	vvs4p.pk_course,
	vvs4p.pk_indication,
	vvs4p.pk_recommended_by
from
	clin.v_vaccs_scheduled4pat vvs4p
where
	vvs4p.is_booster is false
		and
	vvs4p.vacc_seq_no > (
		select count(*)
		from clin.v_pat_vaccinations4indication vpv4i
		where
			vpv4i.pk_patient = vvs4p.pk_patient
				and
			vpv4i.indication = vvs4p.indication
	)
;

comment on view clin.v_pat_missing_vaccs is
	'vaccinations a patient has not been given yet according
	 to the courses a patient is on and the previously
	 received vaccinations';

-- -----------------------------------------------------
-- FIXME: only list those that DO HAVE a previous vacc (max(date) is not null)
create view clin.v_pat_missing_boosters as
select
	vvs4p.pk_patient,
	vvs4p.indication,
	vvs4p.l10n_indication,
--	vvs4p.course,
	vvs4p.course_comment,
	vvs4p.vacc_seq_no as seq_no,
	coalesce(
		((select max(vpv4i11.date)
		  from clin.v_pat_vaccinations4indication vpv4i11
		  where
			vpv4i11.pk_patient = vvs4p.pk_patient
				and
			vpv4i11.indication = vvs4p.indication
		) + vvs4p.min_interval),
		(now() - '1 day'::interval)
	) as latest_due,
	coalesce(
		(now() - (
			(select max(vpv4i12.date)
			from clin.v_pat_vaccinations4indication vpv4i12
			where
				vpv4i12.pk_patient = vvs4p.pk_patient
					and
				vpv4i12.indication = vvs4p.indication) + vvs4p.min_interval)
		),
		'1 day'::interval
	) as amount_overdue,
	vvs4p.age_due_min,
	vvs4p.age_due_max,
	vvs4p.min_interval,
	vvs4p.vacc_comment,
	vvs4p.pk_course,
	vvs4p.pk_indication,
	vvs4p.pk_recommended_by
from
	clin.v_vaccs_scheduled4pat vvs4p
where
	vvs4p.is_booster is true
		and
	vvs4p.min_interval < age (
		(select max(vpv4i13.date)
			from clin.v_pat_vaccinations4indication vpv4i13
			where
				vpv4i13.pk_patient = vvs4p.pk_patient
					and
				vpv4i13.indication = vvs4p.indication
		))
;

comment on view clin.v_pat_missing_boosters is
	'boosters a patient has not been given yet according
	 to the courses a patient is on and the previously
	 received vaccinations';

-- -----------------------------------------------------
\unset ON_ERROR_STOP
drop trigger tr_unique_indication_in_schedule on clin.lnk_vaccination_course2schedule;
\set ON_ERROR_STOP 1

create or replace function clin.trf_unique_indication_in_schedule()
	returns trigger
	language 'plpgsql'
	as '
DECLARE
	_msg text;
BEGIN
	-- is the indication already linked ?
	perform 1 from clin.v_vaccination_courses_in_schedule where
		pk_vaccination_schedule = NEW.fk_schedule and
		pk_indication = (select fk_indication from clin.vaccination_course where pk=NEW.fk_course);
	if FOUND then
		_msg := ''Cannot link course ['' || NEW.fk_course || ''] into schedule ['' || NEW.fk_schedule || '']. The indication is already linked.'';
		raise exception ''%'', _msg;
		return null;
	end if;
	return null;
END;';

create trigger tr_unique_indication_in_schedule
	before insert or update on clin.lnk_vaccination_course2schedule
	for each row execute procedure clin.trf_unique_indication_in_schedule();


-- ============================================
GRANT SELECT, INSERT, UPDATE, DELETE ON
	clin.vaccination
	, clin.vaccination_id_seq
	, clin.vaccine
	, clin.vaccine_pk_seq
	, clin.lnk_vaccine2inds
	, clin.lnk_vaccine2inds_id_seq
	, clin.vacc_indication
	, clin.vacc_indication_id_seq
	, clin.vaccination_definition
	, clin.vaccination_definition_id_seq
	, clin.vaccination_course
	, clin.vaccination_course_pk_seq
	, clin.lnk_pat2vaccination_course
	, clin.lnk_pat2vaccination_course_pk_seq
	, clin.vaccination_course_constraint
	, clin.vaccination_course_constraint_pk_seq
	, clin.lnk_constraint2vacc_course
	, clin.lnk_constraint2vacc_course_pk_seq
	, clin.vaccination_schedule
	, clin.vaccination_schedule_pk_seq
	, clin.lnk_vaccination_course2schedule
	, clin.lnk_vaccination_course2schedule_pk_seq
TO GROUP "gm-doctors";

grant select on
	clin.v_vaccination_courses
	, clin.v_vaccination_definitions4course
	, clin.v_vaccination_courses_in_schedule
	, clin.v_vacc_courses4pat
	, clin.v_vaccs_scheduled4pat
	, clin.v_indications4vaccine
	, clin.v_pat_vaccinations4indication
	, clin.v_pat_missing_vaccs
	, clin.v_pat_missing_boosters
	, clin.v_vaccine
to group "gm-doctors";

-- ===================================================================
-- do simple schema revision tracking
select log_script_insertion('$RCSfile: gmClin-Vaccination-dynamic.sql,v $', '$Revision: 1.8 $');

-- ===================================================================
-- $Log: gmClin-Vaccination-dynamic.sql,v $
-- Revision 1.8  2006-05-04 17:50:54  ncq
-- - improved comment
--
-- Revision 1.7  2006/03/08 09:23:54  ncq
-- - improve naming of some views so everyone can understand their meaning
--   even if being to lazy to read the documentation
--
-- Revision 1.6  2006/03/07 21:14:10  ncq
-- - fixed trf_unique_indication_in_schedule()
--
-- Revision 1.5  2006/03/06 09:42:46  ncq
-- - spell out more table names
-- - general cleanup
--
-- Revision 1.4  2006/03/04 16:16:27  ncq
-- - adjust to regime -> course name change
-- - enhanced comments
-- - audit more tables
-- - add v_vaccination_courses_in_schedule
-- - adjust grants
--
-- Revision 1.3  2006/02/27 11:24:38  ncq
-- - explain *_age: NULL -> 5555 years
-- - add clin.v_vaccine and improve other views
--
-- Revision 1.2  2006/02/19 13:45:05  ncq
-- - move the rest of the dynamic vacc stuff from gmClinicalViews.sql
--   into gmClin-Vaccination-dynamic.sql
-- - add vaccination schedule constraint enumeration data
-- - add is_active to clin.vacc_regime
-- - add clin.vacc_regime_constraint
-- - add clin.lnk_constraint2vacc_reg
-- - proper grants
--
-- Revision 1.1  2006/02/08 15:15:39  ncq
-- - factor our vaccination stuff into its own set of files
-- - remove clin.lnk_vacc_ind2code in favour of clin.coded_term usage
-- - improve comments as discussed on the list
--
--