LP1849212: Add a course reserves facet
authorJane Sandberg <sandbej@linnbenton.edu>
Sun, 2 Aug 2020 18:10:30 +0000 (11:10 -0700)
committerJane Sandberg <sandbej@linnbenton.edu>
Fri, 14 Aug 2020 17:35:25 +0000 (10:35 -0700)
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.html
Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.ts
Open-ILS/src/sql/Pg/030.schema.metabib.sql
Open-ILS/src/sql/Pg/040.schema.asset.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/999.functions.global.sql
Open-ILS/src/sql/Pg/live_t/course_materials.pg [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/XXXX.schema.course-materials-module.sql
Open-ILS/src/templates/opac/parts/config.tt2
Open-ILS/src/templates/opac/parts/result/facets.tt2
Open-ILS/tests/datasets/sql/course_materials.sql

index 2c2cb14..94f9d5c 100644 (file)
@@ -7,7 +7,7 @@
   }
   .list-group-item {padding: .5rem .75rem .5rem .75rem}
 </style>
-<div *ngIf="searchContext.result.facetData">
+<div *ngIf="searchContext && searchContext.result.facetData">
   <div *ngFor="let facetConf of facetConfig.display">
     <div *ngIf="searchContext.result.facetData[facetConf.facetClass]">
       <div *ngFor="let name of facetConf.facetOrder">
                     <a class="card-link"
                       routerLink="/staff/catalog/search"
                       [queryParams]="getFacetUrlParams(facetConf.facetClass, name, value.value)">
-                      {{value.value}}
+                      <eg-bool *ngIf="'boolean_facet' === facetConf.facetClass" value="{{value.value}}"></eg-bool>
+                      <ng-container *ngIf="'boolean_facet' !== facetConf.facetClass">
+                        {{value.value}}
+                      </ng-container>
                     </a>
                   </div>
                   <div class="col-lg-3">{{value.count}}</div>
index aacb27c..38a99a7 100644 (file)
@@ -1,4 +1,5 @@
 import {Component, OnInit, Input} from '@angular/core';
+import {OrgService} from '@eg/core/org.service';
 import {CatalogService} from '@eg/share/catalog/catalog.service';
 import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
 import {CatalogSearchContext, FacetFilter} from '@eg/share/catalog/search-context';
@@ -10,11 +11,13 @@ export const FACET_CONFIG = {
         {facetClass : 'subject', facetOrder : ['topic']},
         {facetClass : 'identifier', facetOrder : ['genre']},
         {facetClass : 'series',  facetOrder : ['seriestitle']},
-        {facetClass : 'subject', facetOrder : ['name', 'geographic']}
+        {facetClass : 'subject', facetOrder : ['name', 'geographic']},
     ],
     displayCount : 5
 };
 
+const RESERVE_FACET_DISPLAY = {facetClass : 'boolean_facet', facetOrder : ['on_reserve']};
+
 @Component({
   selector: 'eg-catalog-result-facets',
   templateUrl: 'facets.component.html'
@@ -27,13 +30,22 @@ export class ResultFacetsComponent implements OnInit {
     constructor(
         private cat: CatalogService,
         private catUrl: CatalogUrlService,
-        private staffCat: StaffCatalogService
+        private org: OrgService,
+        private staffCat: StaffCatalogService,
     ) {
         this.facetConfig = FACET_CONFIG;
     }
 
     ngOnInit() {
-        this.searchContext = this.staffCat.searchContext;
+        // If the org unit uses the Course Materials module, make sure to display the
+        // on_reserve facet
+        this.org.settings('circ.course_materials_opt_in').then((results) => {
+            if (results['circ.course_materials_opt_in'] &&
+                (this.facetConfig.display.indexOf(RESERVE_FACET_DISPLAY) === -1)) {
+                this.facetConfig.display.push(RESERVE_FACET_DISPLAY);
+            }
+            this.searchContext = this.staffCat.searchContext;
+        });
     }
 
     facetIsApplied(cls: string, name: string, value: string): boolean {
index 0c82fc8..ac2d7f0 100644 (file)
@@ -1073,7 +1073,7 @@ BEGIN
     PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
     IF NOT FOUND THEN
         IF NOT b_skip_search THEN
-            FOR fclass IN SELECT * FROM config.metabib_class LOOP
+            FOR fclass IN SELECT * FROM config.metabib_class WHERE name != 'boolean_facet' LOOP
                 -- RAISE NOTICE 'Emptying out %', fclass.name;
                 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
             END LOOP;
index 0207797..bf51594 100644 (file)
@@ -1136,5 +1136,58 @@ CREATE TABLE asset.course_module_course_materials (
     unique (course, item, record)
 );
 
-COMMIT;
+CREATE OR REPLACE FUNCTION asset.update_course_reserves_facet(old_bib_id BIGINT, new_bib_id BIGINT) RETURNS VOID AS $func$
+DECLARE
+    facet_field INT;
+BEGIN
+    SELECT id INTO facet_field FROM config.metabib_field WHERE name='on_reserve';
+    IF facet_field IS NOT NULL THEN
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+        IF NOT FOUND THEN
+            DELETE FROM metabib.facet_entry WHERE source = old_bib_id AND field=facet_field;
+        END IF;
+       IF EXISTS (SELECT id FROM biblio.record_entry WHERE id=new_bib_id AND NOT deleted) THEN
+           IF EXISTS (SELECT id FROM asset.course_module_course_materials WHERE record=new_bib_id) THEN
+                INSERT INTO metabib.facet_entry (source, field, value) VALUES (new_bib_id, facet_field, 't');
+            ELSE
+                INSERT INTO metabib.facet_entry (source, field, value) VALUES (new_bib_id, facet_field, 'f');
+            END IF;
+        END IF;
+    END IF;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION asset.course_materials_inserted () RETURNS TRIGGER AS $func$
+BEGIN
+    PERFORM asset.update_course_reserves_facet(NEW.record, NEW.record);
+    RETURN NEW;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION asset.course_materials_changed () RETURNS TRIGGER AS $func$
+BEGIN
+    PERFORM asset.update_course_reserves_facet(OLD.record, NEW.record);
+    RETURN NEW;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION asset.course_materials_deleted () RETURNS TRIGGER AS $func$
+BEGIN
+    PERFORM asset.update_course_reserves_facet(OLD.record, -1);
+    RETURN OLD;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE TRIGGER acmcm_inserted_trig
+    AFTER INSERT ON asset.course_module_course_materials
+    FOR EACH ROW EXECUTE PROCEDURE asset.course_materials_inserted();
 
+CREATE TRIGGER acmcm_changed_trig
+    AFTER UPDATE ON asset.course_module_course_materials
+    FOR EACH ROW EXECUTE PROCEDURE asset.course_materials_changed();
+
+CREATE TRIGGER acmcm_deleted_trig
+    AFTER DELETE ON asset.course_module_course_materials
+    FOR EACH ROW EXECUTE PROCEDURE asset.course_materials_deleted();
+
+COMMIT;
index 64fea9f..506d9ab 100644 (file)
@@ -100,6 +100,7 @@ INSERT INTO config.metabib_class ( name, label ) VALUES ( 'title', oils_i18n_get
 INSERT INTO config.metabib_class ( name, label ) VALUES ( 'author', oils_i18n_gettext('author', 'Author', 'cmc', 'label'));
 INSERT INTO config.metabib_class ( name, label ) VALUES ( 'subject', oils_i18n_gettext('subject', 'Subject', 'cmc', 'label') );
 INSERT INTO config.metabib_class ( name, label ) VALUES ( 'series', oils_i18n_gettext('series', 'Series', 'cmc', 'label') );
+INSERT INTO config.metabib_class ( name, label ) VALUES ( 'boolean_facet', oils_i18n_gettext('boolean_facet', 'Boolean facet', 'cmc', 'label') );
 
 -- enable combined search for only the subject class by default
 UPDATE config.metabib_class SET combined = TRUE WHERE name = 'subject';
@@ -336,6 +337,14 @@ VALUES (
     FALSE, TRUE, FALSE, FALSE
 );
 
+INSERT INTO config.metabib_field (id, field_class, name, label, facet_field,
+    search_field, browse_field, display_field)
+VALUES (
+    54, 'boolean_facet', 'on_reserve',
+    oils_i18n_gettext(54, 'On Reserve', 'cmf', 'label'),
+    TRUE, FALSE, FALSE, FALSE
+);
+
 INSERT INTO config.metabib_field_virtual_map (real, virtual)
     SELECT  id,
             45
index 45321a0..a6c0244 100644 (file)
@@ -1516,10 +1516,19 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION biblio.course_reserves_facet () RETURNS TRIGGER AS $func$
+BEGIN
+    PERFORM asset.update_course_reserves_facet(NEW.id, NEW.id);
+    RETURN NEW;
+END;
+$func$ LANGUAGE plpgsql;
+
+
 -- Ingest triggers
 CREATE TRIGGER fingerprint_tgr BEFORE INSERT OR UPDATE ON biblio.record_entry FOR EACH ROW EXECUTE PROCEDURE biblio.fingerprint_trigger ('eng','BKS');
 CREATE TRIGGER aaa_indexing_ingest_or_delete AFTER INSERT OR UPDATE ON biblio.record_entry FOR EACH ROW EXECUTE PROCEDURE biblio.indexing_ingest_or_delete ();
 CREATE TRIGGER bbb_simple_rec_trigger AFTER INSERT OR UPDATE OR DELETE ON biblio.record_entry FOR EACH ROW EXECUTE PROCEDURE reporter.simple_rec_trigger ();
+CREATE TRIGGER course_reserves_facet AFTER INSERT OR UPDATE ON biblio.record_entry FOR EACH ROW EXECUTE PROCEDURE biblio.course_reserves_facet ();
 
 CREATE TRIGGER map_thesaurus_to_control_set BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.map_thesaurus_to_control_set ();
 CREATE TRIGGER aaa_auth_ingest_or_delete AFTER INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.indexing_ingest_or_delete ();
diff --git a/Open-ILS/src/sql/Pg/live_t/course_materials.pg b/Open-ILS/src/sql/Pg/live_t/course_materials.pg
new file mode 100644 (file)
index 0000000..4c2d15e
--- /dev/null
@@ -0,0 +1,34 @@
+BEGIN;
+
+SELECT plan(3);
+
+SELECT is(
+    (SELECT value FROM metabib.facet_entry
+     WHERE source = 2 AND
+     field IN (SELECT id FROM config.metabib_field WHERE name='on_reserve' )),
+    'f',
+    'Initially, On Reserves facet is false'
+);
+
+INSERT INTO asset.course_module_course_materials (course, record)
+VALUES (1, 2);
+
+SELECT is(
+    (SELECT value FROM metabib.facet_entry
+     WHERE source = 2 AND
+     field IN (SELECT id FROM config.metabib_field WHERE name='on_reserve' )),
+    't',
+    'When a bib record is added to a course, the On Reserves facet is true'
+);
+
+DELETE asset.course_module_course_materials WHERE course=1 AND record=2;
+
+SELECT is(
+    (SELECT value FROM metabib.facet_entry
+     WHERE source = 2 AND
+     field IN (SELECT id FROM config.metabib_field WHERE name='on_reserve' )),
+    'f',
+    'After removing a record from a course, On Reserves facet goes back to false'
+);
+
+ROLLBACK;
index e07b7c5..c8f4cff 100644 (file)
@@ -103,4 +103,76 @@ INSERT INTO actor.org_unit_setting (org_unit, name, value)
     FROM config.bib_source
     WHERE source='Course materials module';
 
+INSERT INTO config.metabib_class ( name, label ) VALUES ( 'boolean_facet', oils_i18n_gettext('boolean_facet', 'Boolean facet', 'cmc', 'label') );
+
+INSERT INTO config.metabib_field (id, field_class, name, label, facet_field, search_field, browse_field, display_field ) VALUES
+    (54, 'boolean_facet', 'on_reserve', oils_i18n_gettext(54, 'On Reserve', 'cmf', 'label'), TRUE, FALSE, FALSE, FALSE );
+
+CREATE OR REPLACE FUNCTION asset.update_course_reserves_facet(old_bib_id BIGINT, new_bib_id BIGINT) RETURNS VOID AS $func$
+DECLARE
+    facet_field INT;
+BEGIN
+    SELECT id INTO facet_field FROM config.metabib_field WHERE name='on_reserve';
+    IF facet_field IS NOT NULL THEN
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+        IF NOT FOUND THEN
+            DELETE FROM metabib.facet_entry WHERE source = old_bib_id AND field=facet_field;
+        END IF;
+       IF EXISTS (SELECT id FROM biblio.record_entry WHERE id=new_bib_id AND NOT deleted) THEN
+           IF EXISTS (SELECT id FROM asset.course_module_course_materials WHERE record=new_bib_id) THEN
+                INSERT INTO metabib.facet_entry (source, field, value) VALUES (new_bib_id, facet_field, 't');
+            ELSE
+                INSERT INTO metabib.facet_entry (source, field, value) VALUES (new_bib_id, facet_field, 'f');
+            END IF;
+        END IF;
+    END IF;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION asset.course_materials_inserted () RETURNS TRIGGER AS $func$
+BEGIN
+    PERFORM asset.update_course_reserves_facet(NEW.record, NEW.record);
+    RETURN NEW;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION asset.course_materials_changed () RETURNS TRIGGER AS $func$
+BEGIN
+    PERFORM asset.update_course_reserves_facet(OLD.record, NEW.record);
+    RETURN NEW;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION asset.course_materials_deleted () RETURNS TRIGGER AS $func$
+BEGIN
+    PERFORM asset.update_course_reserves_facet(OLD.record, -1);
+    RETURN OLD;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE TRIGGER acmcm_inserted_trig
+    AFTER INSERT ON asset.course_module_course_materials
+    FOR EACH ROW EXECUTE PROCEDURE asset.course_materials_inserted();
+CREATE TRIGGER acmcm_changed_trig
+    AFTER UPDATE ON asset.course_module_course_materials
+    FOR EACH ROW EXECUTE PROCEDURE asset.course_materials_changed();
+
+CREATE TRIGGER acmcm_deleted_trig
+    AFTER DELETE ON asset.course_module_course_materials
+    FOR EACH ROW EXECUTE PROCEDURE asset.course_materials_deleted();
+    
+CREATE OR REPLACE FUNCTION course_reserves_facet () RETURNS TRIGGER AS $func$
+BEGIN
+    PERFORM asset.update_course_reserves_facet(NEW.id, NEW.id);
+    RETURN NEW;
+END;
+$func$ LANGUAGE plpgsql;
+CREATE TRIGGER course_reserves_facet AFTER INSERT OR UPDATE ON biblio.record_entry FOR EACH ROW EXECUTE PROCEDURE biblio.update_reserves_facet ();
+
+--- Populate the On reserve facet
+INSERT INTO metabib.facet_entry (source, field, value)
+    SELECT id, 54, 'f' FROM biblio.record_entry WHERE NOT deleted;
+
 COMMIT;
index 48b4f0f..010b37d 100644 (file)
@@ -115,7 +115,9 @@ facet.display = [
     {facet_class => 'subject', facet_order => ['topic']},
     {facet_class => 'identifier', facet_order => ['genre']},
     {facet_class => 'series',  facet_order => ['seriestitle']},
-    {facet_class => 'subject', facet_order => ['name', 'geographic']}
+    {facet_class => 'subject', facet_order => ['name', 'geographic']},
+    # Uncomment the next line for the course reserves facet:
+    # {facet_class => 'boolean_facet', facet_order => ['on_reserve']},
 ];
 facet.default_display_count = 5;
 
index 38f231f..2d7d126 100644 (file)
@@ -83,7 +83,15 @@ FOR facet IN sorted_facets;
         <div class="box_wrapper">
             <div class="box">
             [% FOR facet_data IN facet.data;
-                display_value = facet_data.value | html;
+                IF fclass == 'boolean_facet';
+                    IF facet_data.value == 't';
+                        display_value = l('Yes');
+                    ELSE;
+                        display_value = l('No');
+                    END;
+                ELSE;
+                    display_value = facet_data.value | html;
+                END;
                 param_string = fclass _ '|' _ fname _ '[' _ facet_data.value _ ']';
                 new_facets = [];
                 this_selected = 0;
index 271802f..84220b7 100644 (file)
@@ -10,3 +10,5 @@ INSERT INTO asset.course_module_course_materials
 (1, 200, 'Required'),
 (1, 201, 'Optional');
 
+SELECT asset.update_course_reserves_facet(id, id)
+FROM biblio.record_entry WHERE NOT deleted;