LP#1832897: add tables, IDL, and seed data for carousels
authorGalen Charlton <gmc@equinoxinitiative.org>
Sun, 9 Jun 2019 22:54:04 +0000 (18:54 -0400)
committerJane Sandberg <sandbej@linnbenton.edu>
Wed, 4 Sep 2019 02:31:40 +0000 (19:31 -0700)
Carousels
^^^^^^^^^
This feature fully integrates the creation and management of book carousels
into Evergreen, allowing for the display of book cover images on a library’s
public catalog home page.  Carousels may be animated or static.  They can be
manually maintained by staff or automatically maintained by Evergreen.  Titles
can appear in carousels based on newly cataloged items, recent returns,
popularity, etc.  Titles must have copies that are visible to the public
catalog, be circulating, and holdable to appear in a carousel.  Serial titles
cannot be displayed in carousels.

Administration
++++++++++++++
This feature introduces the concepts of Carousel Types, Carousels, and Carousel
Library Mappings. The first can be administered in Server Administration
while the latter two can be administerd in Local Administration.

Carousel Types define the attributes of a carousel, such as whether it is
automatically managed and how it is filtered.  A carousel must be associated
with a carousel type to function properly.

There are five stock Carousel Types:

  * Newly Cataloged Items - titles appear automatically based on the
    active date of the title’s copies
  * Recently Returned Items - titles appear automatically based on the
    mostly recently circulated copy’s check-in scan date and time
  * Top Circulated Titles - titles appear automatically based on the
    most circulated copies in the Item Libraries identified in the
    carousel definition; titles are chosen based on the number of
    action.circulation rows created during an interval specified
    in the carousel definition and includes both circulations and renewals
  * Newest Items by Shelving Location - titles appear automatically
    based on the active date and shelving location of the title’s copies
  * Manual - titles are added and managed manually by library staff

While additional Carousel Types can be added using the administration
interface, new automatic types currently require additional Perl code
to be recognized.

Carousel definitions allow the operator to specify the type, owner,
name and, for automatically-maintained types, the item libraries and
shelving locations to look for titles to populate the carousels as
well as how far back to look for titles.

Carousel Library Mappings specify the libraries that the carousel
should be displayed out. The visibility of a carousel at a given organizational
unit is not automatically inherited by the descendants of that unit.  The
carousel’s owning organizational unit is automatically added to the list of
display organizational units.

A server-side job, refresh_carousels.srfsh, is available to periodically
refresh the contents of automatic carousels.

Staff Interface
+++++++++++++++
Each carousel has a record bucket associated with it. Library staff can
add titles to a carousel's bucket, and for the manual Carousel Type, that
is the only way to populate the carousel. Records added to an automatic
carousel's bucket will be removed whenever the carousel is next
refreshed.

Public Catalog
++++++++++++++
A new Template Toolkit macro called “carousels” allows the Evergreen
administrator to inject the contents of one or more carousels into any point in
the OPAC.  The macro will accept the following parameters:

  * carousel_id
  * dynamic (Boolean, default value false)
  * image_size (small, medium, or large)
  * width (number of titles to display on a “pane” of the carousel)
  * animated (Boolean to specify whether the carousel should automatically cycle through its panes)
  * animation_interval (the interval (in seconds) to wait before advancing to the next pane)

If the carousel_id parameter is supplied, the carousel with that ID will be
displayed.  If carousel_id is not supplied, all carousels visible to the public
catalog’s physical_loc organizational unit is displayed.

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/070.schema.container.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.schema.carousels.sql [new file with mode: 0644]

index 544488e..2fc7aa1 100644 (file)
@@ -12851,6 +12851,94 @@ SELECT  usr,
                </permacrud>
        </class>
 
+       <class id="cct" 
+               controller="open-ils.cstore open-ils.pcrud"
+               oils_obj:fieldmapper="config::carousel_type" 
+               oils_persist:tablename="config.carousel_type" 
+               reporter:label="Carousel Types">
+               <fields oils_persist:primary="id" oils_persist:sequence="config.carousel_type_id_seq">
+                       <field reporter:label="Carousel Type ID" reporter:selector="name" name="id" reporter:datatype="id" />
+                       <field reporter:label="Name" name="name" reporter:datatype="text" oils_obj:required="true" oils_obj:i18n="true"/>
+                       <field reporter:label="Automatically Managed?" name="automatic" reporter:datatype="bool"/>
+                       <field reporter:label="Filter By Age?" name="filter_by_age" reporter:datatype="bool"/>
+                       <field reporter:label="Filter By Item Owning Library?" name="filter_by_copy_owning_lib" reporter:datatype="bool"/>
+                       <field reporter:label="Filter By Item Location?" name="filter_by_copy_location" reporter:datatype="bool"/>
+               </fields>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="ADMIN_CAROUSEL_TYPE" global_required="true"/>
+                               <retrieve/>
+                               <update permission="ADMIN_CAROUSEL_TYPE" global_required="true"/>
+                               <delete permission="ADMIN_CAROUSEL_TYPE" global_required="true"/>
+                       </actions>
+               </permacrud>
+       </class>
+
+       <class id="cc" 
+               controller="open-ils.cstore open-ils.pcrud"
+               oils_obj:fieldmapper="container::carousel" 
+               oils_persist:tablename="container.carousel" 
+               reporter:label="Carousels">
+               <fields oils_persist:primary="id" oils_persist:sequence="container.carousel_id_seq">
+                       <field reporter:label="Carousel ID" name="id" reporter:datatype="id" reporter:selector="name"/>
+                       <field reporter:label="Carousel Type" name="type" reporter:datatype="link"/>
+                       <field reporter:label="Owner" name="owner" reporter:datatype="link"/>
+                       <field reporter:label="Name" name="name" reporter:datatype="text" oils_obj:required="true" oils_obj:i18n="true"/>
+                       <field reporter:label="Bucket" name="bucket" reporter:datatype="link"/>
+                       <field reporter:label="Creating User" name="creator" reporter:datatype="link"/>
+                       <field reporter:label="Editing User" name="editor" reporter:datatype="link"/>
+                       <field reporter:label="Create Time" name="create_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Edit Time" name="edit_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Age Limit" name="age_filter"  reporter:datatype="interval"/>
+                       <field reporter:label="Item Libraries" name="owning_lib_filter" reporter:datatype="text" />      <!-- Actually an int[], but this is the best we can do in fm_IDL.xml -->
+                       <field reporter:label="Shelving Locations" name="copy_location_filter" reporter:datatype="text" /> <!-- ditto -->
+                       <field reporter:label="Last Refresh Time" name="last_refresh_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Is Active" name="active" reporter:datatype="bool"/>
+                       <field reporter:label="Maximum Items" name="max_items" reporter:datatype="int"/>
+               </fields>
+               <links>
+                       <link field="type" reltype="has_a" key="id" map="" class="cct"/>
+                       <link field="owner" reltype="has_a" key="id" map="" class="aou"/>
+                       <link field="bucket" reltype="has_a" key="id" map="" class="cbreb"/>
+                       <link field="creator" reltype="has_a" key="id" map="" class="au"/>
+                       <link field="editor" reltype="has_a" key="id" map="" class="au"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="ADMIN_CAROUSEL" global_required="true"/>
+                               <retrieve/>
+                               <update permission="ADMIN_CAROUSEL" global_required="true"/>
+                               <delete permission="ADMIN_CAROUSEL" global_required="true"/>
+                       </actions>
+               </permacrud>
+       </class>
+
+       <class id="ccou" 
+               controller="open-ils.cstore open-ils.pcrud"
+               oils_obj:fieldmapper="container::carousel_org_unit" 
+               oils_persist:tablename="container.carousel_org_unit" 
+               reporter:label="Carousels Visible at Library">
+               <fields oils_persist:primary="id" oils_persist:sequence="container.carousel_org_unit_id_seq">
+                       <field reporter:label="ID" name="id" reporter:datatype="id" />
+                       <field reporter:label="Carousel" name="carousel" reporter:datatype="link"/>
+                       <field reporter:label="Override Name" name="override_name" reporter:datatype="text"/>
+                       <field reporter:label="Library" name="org_unit" reporter:datatype="link"/>
+                       <field reporter:label="Sequence Number" name="seq" reporter:datatype="int"/>
+               </fields>
+               <links>
+                       <link field="carousel" reltype="has_a" key="id" map="" class="cc"/>
+                       <link field="org_unit" reltype="has_a" key="id" map="" class="aou"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="ADMIN_CAROUSEL" global_required="true"/>
+                               <retrieve/>
+                               <update permission="ADMIN_CAROUSEL" global_required="true"/>
+                               <delete permission="ADMIN_CAROUSEL" global_required="true"/>
+                       </actions>
+               </permacrud>
+       </class>
+
        <!-- ********************************************************************************************************************* -->
 </IDL>
 
index 5068463..8afa76e 100644 (file)
@@ -1349,4 +1349,25 @@ CREATE TABLE config.print_template (
     CONSTRAINT   label_once_per_lib UNIQUE (owner, label)
 );
 
+CREATE TABLE config.carousel_type (
+    id                          SERIAL PRIMARY KEY,
+    name                        TEXT NOT NULL,
+    automatic                   BOOLEAN NOT NULL DEFAULT TRUE,
+    filter_by_age               BOOLEAN NOT NULL DEFAULT FALSE,
+    filter_by_copy_owning_lib   BOOLEAN NOT NULL DEFAULT FALSE,
+    filter_by_copy_location     BOOLEAN NOT NULL DEFAULT FALSE
+);
+
+INSERT INTO config.carousel_type
+    (id, name,                               automatic, filter_by_age, filter_by_copy_owning_lib, filter_by_copy_location)
+VALUES
+    (1, 'Manual',                            FALSE,     FALSE,         FALSE,                     FALSE),
+    (2, 'Newly Catalogued Items',            TRUE,      TRUE,          TRUE,                      TRUE),
+    (3, 'Recently Returned Items',           TRUE,      TRUE,          TRUE,                      TRUE),
+    (4, 'Top Circulated Items',              TRUE,      TRUE,          TRUE,                      FALSE),
+    (5, 'Newest Items By Shelving Location', TRUE,      TRUE,          TRUE,                      FALSE)
+;
+
+SELECT SETVAL('config.carousel_type_id_seq'::TEXT, 100);
+
 COMMIT;
index 594dfaf..8b1572f 100644 (file)
@@ -250,5 +250,30 @@ CREATE TABLE container.user_bucket_item_note (
     note    TEXT        NOT NULL
 );
 
+CREATE TABLE container.carousel (
+    id                      SERIAL PRIMARY KEY,
+    type                    INTEGER NOT NULL REFERENCES config.carousel_type (id),
+    owner                   INTEGER NOT NULL REFERENCES actor.org_unit (id),
+    name                    TEXT NOT NULL,
+    bucket                  INTEGER REFERENCES container.biblio_record_entry_bucket (id),
+    creator                 INTEGER NOT NULL REFERENCES actor.usr (id),
+    editor                  INTEGER NOT NULL REFERENCES actor.usr (id),
+    create_time             TIMESTAMPTZ NOT NULL DEFAULT now(),
+    edit_time               TIMESTAMPTZ NOT NULL DEFAULT now(),
+    age_filter              INTERVAL,
+    owning_lib_filter       INT[],
+    copy_location_filter    INT[],
+    last_refresh_time       TIMESTAMPTZ,
+    active                  BOOLEAN NOT NULL DEFAULT TRUE,
+    max_items               INTEGER NOT NULL
+);
+
+CREATE TABLE container.carousel_org_unit (
+    id              SERIAL PRIMARY KEY,
+    carousel        INTEGER NOT NULL REFERENCES container.carousel (id) ON DELETE CASCADE,
+    override_name   TEXT,
+    org_unit        INTEGER NOT NULL REFERENCES actor.org_unit (id),
+    seq             INTEGER NOT NULL
+);
 
 COMMIT;
index 4d9d376..2627a08 100644 (file)
@@ -1917,7 +1917,13 @@ INSERT INTO permission.perm_list ( id, code, description ) VALUES
  ( 610, 'CLEAR_PURCHASE_REQUEST', oils_i18n_gettext(610,
     'Clear Completed User Purchase Requests', 'ppl', 'description')),
  ( 611, 'ADMIN_PRINT_TEMPLATE', oils_i18n_gettext(611,
-    'Modify print templates', 'ppl', 'description'))
+    'Modify print templates', 'ppl', 'description')),
+ ( 612, 'ADMIN_CAROUSEL_TYPE', oils_i18n_gettext(612,
+    'Allow a user to manage carousel types', 'ppl', 'description')),
+ ( 613, 'ADMIN_CAROUSEL', oils_i18n_gettext(613,
+    'Allow a user to manage carousels', 'ppl', 'description')),
+ ( 614, 'REFRESH_CAROUSEL', oils_i18n_gettext(614,
+    'Allow a user to refresh carousels', 'ppl', 'description'))
 ;
 
 
@@ -5652,6 +5658,7 @@ INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('book
 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('reading_list', oils_i18n_gettext('reading_list', 'Reading List', 'cbrebt', 'label'));
 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('template_merge',oils_i18n_gettext('template_merge','Template Merge Container', 'cbrebt', 'label'));
 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('url_verify', oils_i18n_gettext('url_verify', 'URL Verification Queue', 'cbrebt', 'label'));
+INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('carousel', oils_i18n_gettext('url_verify', 'Carousel', 'cbrebt', 'label'));
 
 INSERT INTO container.user_bucket_type (code,label) VALUES ('misc', oils_i18n_gettext('misc', 'Miscellaneous', 'cubt', 'label'));
 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks', oils_i18n_gettext('folks', 'Friends', 'cubt', 'label'));
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.carousels.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.carousels.sql
new file mode 100644 (file)
index 0000000..9dd3669
--- /dev/null
@@ -0,0 +1,63 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+CREATE TABLE config.carousel_type (
+    id                          SERIAL PRIMARY KEY,
+    name                        TEXT NOT NULL,
+    automatic                   BOOLEAN NOT NULL DEFAULT TRUE,
+    filter_by_age               BOOLEAN NOT NULL DEFAULT FALSE,
+    filter_by_copy_owning_lib   BOOLEAN NOT NULL DEFAULT FALSE,
+    filter_by_copy_location     BOOLEAN NOT NULL DEFAULT FALSE
+);
+
+INSERT INTO config.carousel_type
+    (id, name,                               automatic, filter_by_age, filter_by_copy_owning_lib, filter_by_copy_location)
+VALUES
+    (1, 'Manual',                            FALSE,     FALSE,         FALSE,                     FALSE),
+    (2, 'Newly Catalogued Items',            TRUE,      TRUE,          TRUE,                      TRUE),
+    (3, 'Recently Returned Items',           TRUE,      TRUE,          TRUE,                      TRUE),
+    (4, 'Top Circulated Items',              TRUE,      TRUE,          TRUE,                      FALSE),
+    (5, 'Newest Items By Shelving Location', TRUE,      TRUE,          TRUE,                      FALSE)
+;
+
+SELECT SETVAL('config.carousel_type_id_seq'::TEXT, 100);
+
+CREATE TABLE container.carousel (
+    id                      SERIAL PRIMARY KEY,
+    type                    INTEGER NOT NULL REFERENCES config.carousel_type (id),
+    owner                   INTEGER NOT NULL REFERENCES actor.org_unit (id),
+    name                    TEXT NOT NULL,
+    bucket                  INTEGER REFERENCES container.biblio_record_entry_bucket (id),
+    creator                 INTEGER NOT NULL REFERENCES actor.usr (id),
+    editor                  INTEGER NOT NULL REFERENCES actor.usr (id),
+    create_time             TIMESTAMPTZ NOT NULL DEFAULT now(),
+    edit_time               TIMESTAMPTZ NOT NULL DEFAULT now(),
+    age_filter              INTERVAL,
+    owning_lib_filter       INT[],
+    copy_location_filter    INT[],
+    last_refresh_time       TIMESTAMPTZ,
+    active                  BOOLEAN NOT NULL DEFAULT TRUE,
+    max_items               INTEGER NOT NULL
+);
+
+CREATE TABLE container.carousel_org_unit (
+    id              SERIAL PRIMARY KEY,
+    carousel        INTEGER NOT NULL REFERENCES container.carousel (id) ON DELETE CASCADE,
+    override_name   TEXT,
+    org_unit        INTEGER NOT NULL REFERENCES actor.org_unit (id),
+    seq             INTEGER NOT NULL
+);
+
+INSERT INTO container.biblio_record_entry_bucket_type (code, label) VALUES ('carousel', 'Carousel');
+
+INSERT INTO permission.perm_list ( id, code, description ) VALUES
+ ( 612, 'ADMIN_CAROUSEL_TYPE', oils_i18n_gettext(611,
+    'Allow a user to manage carousel types', 'ppl', 'description')),
+ ( 613, 'ADMIN_CAROUSEL', oils_i18n_gettext(612,
+    'Allow a user to manage carousels', 'ppl', 'description')),
+ ( 614, 'REFRESH_CAROUSEL', oils_i18n_gettext(613,
+    'Allow a user to refresh carousels', 'ppl', 'description'))
+;
+
+COMMIT;