--- /dev/null
+-- Deploy kcls-evergreen:new-headings-report to pg
+-- requires: po-print-li-count-and-date
+
+BEGIN;
+
+ALTER TABLE metabib.browse_author_entry
+ ADD COLUMN create_date TIMESTAMP WITH TIME ZONE
+ NOT NULL DEFAULT NOW();
+
+ALTER TABLE metabib.browse_subject_entry
+ ADD COLUMN create_date TIMESTAMP WITH TIME ZONE
+ NOT NULL DEFAULT NOW();
+
+ALTER TABLE metabib.browse_series_entry
+ ADD COLUMN create_date TIMESTAMP WITH TIME ZONE
+ NOT NULL DEFAULT NOW();
+
+ALTER TABLE metabib.browse_title_entry
+ ADD COLUMN create_date TIMESTAMP WITH TIME ZONE
+ NOT NULL DEFAULT NOW();
+
+ALTER TABLE metabib.browse_call_number_entry
+ ADD COLUMN create_date TIMESTAMP WITH TIME ZONE
+ NOT NULL DEFAULT NOW();
+
+CREATE INDEX by_cataloging_date_idx
+ ON biblio.record_entry (cataloging_date)
+ WHERE deleted IS FALSE or deleted = FALSE;
+
+-- Given a tag (or tag substring), create a single string
+-- concatenating the combined set of tags, indicators, subfields,
+-- and values into a single string.
+CREATE OR REPLACE FUNCTION evergreen.marc_tag_to_string
+ (tag TEXT, marcxml TEXT) RETURNS TEXT AS $FUNK$
+DECLARE
+ final TEXT DEFAULT '';
+ datafield XML;
+ subfield XML;
+BEGIN
+ FOR datafield IN SELECT UNNEST(XPATH(
+ '//*[starts-with(@tag,"'||tag||'")]', marcxml::XML)) LOOP
+
+ -- delineate marc tags
+ IF final <> '' THEN final := final || ' | '; END IF;
+
+ -- add the tag
+ final := final || (SELECT ARRAY_TO_STRING(
+ XPATH('//*/@tag[1]', datafield ), ''));
+
+ -- add indicator 1 and 2, defaulting to _
+ final := final || ' '
+ || (SELECT REGEXP_REPLACE(ARRAY_TO_STRING(
+ XPATH('//*/@ind1[1]', datafield), ''), '^\s?$', '_'))
+ || (SELECT REGEXP_REPLACE(ARRAY_TO_STRING(
+ XPATH('//*/@ind2[1]', datafield), ''), '^\s?$', '_'));
+
+ -- append each code and subfield value
+ FOR subfield IN SELECT UNNEST(XPATH(
+ '//*[@code]', datafield)) LOOP
+ final := final || ' $' || (
+ SELECT evergreen.xml_famous5_to_text( -- unescape XML
+ ARRAY_TO_STRING(XPATH(
+ '//*/@code[1]|//*/text()[1]', subfield), ' ')));
+ END LOOP;
+
+ END LOOP;
+ RETURN final;
+END;
+$FUNK$ LANGUAGE PLPGSQL;
+
+-- Returns a browse entry summary object of the selected type directly
+-- preceeding or following the provided browse entry sort value.
+-- If 'is_next' is true, return the next entry, otherwise the preceeding.
+CREATE OR REPLACE FUNCTION
+ reporter.metabib_browse_entry_window_part
+ (browse_type TEXT, pivot_sort_value TEXT, is_next BOOLEAN)
+ RETURNS TABLE (
+ entry BIGINT,
+ entry_value TEXT,
+ entry_sort_value TEXT,
+ entry_create_date TIMESTAMPTZ,
+ bib_record BIGINT,
+ bib_editor INTEGER,
+ bib_edit_date TIMESTAMPTZ,
+ bib_create_date TIMESTAMPTZ,
+ bib_cataloging_date TIMESTAMPTZ,
+ field_def INTEGER,
+ field_label TEXT,
+ auth_tag CHARACTER(3)
+ ) AS $FUNK$
+DECLARE
+ pivot_where TEXT;
+ pivot_sort TEXT;
+BEGIN
+
+ pivot_where := '<';
+ pivot_sort := 'DESC';
+
+ IF is_next THEN
+ pivot_where := '>';
+ pivot_sort := 'ASC';
+ END IF;
+
+ RETURN QUERY EXECUTE $$
+ SELECT
+ entry.id, entry.value, entry.sort_value, entry.create_date,
+ bre.id, bre.editor, bre.edit_date, bre.create_date,
+ bre.cataloging_date, map.def, cmf.label, field.tag
+ FROM metabib.browse_$$ || browse_type || $$_entry entry
+ LEFT JOIN metabib.browse_$$ ||
+ browse_type || $$_entry_def_map map
+ ON (map.entry = entry.id)
+ LEFT JOIN biblio.record_entry bre
+ ON (bre.id = map.source AND NOT bre.deleted)
+ LEFT JOIN config.metabib_field cmf ON (cmf.id = map.def)
+ LEFT JOIN metabib.browse_$$ ||
+ browse_type || $$_entry_simple_heading_map hmap
+ ON (map.source IS NULL AND hmap.entry = entry.id)
+ LEFT JOIN authority.simple_heading ash ON
+ (ash.id = hmap.simple_heading)
+ LEFT JOIN authority.control_set_authority_field field
+ ON (field.id = ash.atag)
+ WHERE
+ (map.source IS NOT NULL OR hmap.entry IS NOT NULL) AND
+ entry.sort_value $$ || pivot_where || ' '
+ || QUOTE_LITERAL(pivot_sort_value) || $$
+ ORDER BY entry.sort_value $$ || pivot_sort || $$
+ LIMIT 1
+ $$;
+END;
+$FUNK$ LANGUAGE PLPGSQL;
+
+
+-- Returns a browse entry summary object of the selected type directly
+-- preceeding the provided browse entry sort value.
+CREATE OR REPLACE FUNCTION
+ reporter.metabib_browse_entry_window_prev
+ (browse_type TEXT, pivot_sort_value TEXT)
+ RETURNS TABLE (
+ prev_entry BIGINT,
+ prev_entry_value TEXT,
+ prev_entry_sort_value TEXT,
+ prev_entry_create_date TIMESTAMPTZ,
+ prev_bib_record BIGINT,
+ prev_bib_editor INTEGER,
+ prev_bib_edit_date TIMESTAMPTZ,
+ prev_bib_create_date TIMESTAMPTZ,
+ prev_bib_cataloging_date TIMESTAMPTZ,
+ prev_field_def INTEGER,
+ prev_field_label TEXT,
+ prev_auth_tag CHARACTER(3)
+ )
+AS $FUNK$
+ SELECT * FROM
+ reporter.metabib_browse_entry_window_part(
+ browse_type, pivot_sort_value, FALSE);
+$FUNK$ LANGUAGE SQL;
+
+-- Returns a browse entry summary object of the selected type directly
+-- preceeding the provided browse entry sort value.
+CREATE OR REPLACE FUNCTION
+ reporter.metabib_browse_entry_window_next
+ (browse_type TEXT, pivot_sort_value TEXT)
+ RETURNS TABLE (
+ next_entry BIGINT,
+ next_entry_value TEXT,
+ next_entry_sort_value TEXT,
+ next_entry_create_date TIMESTAMPTZ,
+ next_bib_record BIGINT,
+ next_bib_editor INTEGER,
+ next_bib_edit_date TIMESTAMPTZ,
+ next_bib_create_date TIMESTAMPTZ,
+ next_bib_cataloging_date TIMESTAMPTZ,
+ next_field_def INTEGER,
+ next_field_label TEXT,
+ next_auth_tag CHARACTER(3)
+ )
+AS $FUNK$
+ SELECT * FROM
+ reporter.metabib_browse_entry_window_part(
+ browse_type, pivot_sort_value, TRUE);
+$FUNK$ LANGUAGE SQL;
+
+-- Returns all browse entries that link to at least one non-deleted bib
+-- record that has a value set for the cataloging date. Includes details
+-- on the linked record.
+CREATE OR REPLACE FUNCTION
+ reporter.cataloged_browse_entries(browse_type TEXT)
+ RETURNS TABLE (
+ entry BIGINT,
+ entry_value TEXT,
+ entry_sort_value TEXT,
+ entry_create_date TIMESTAMPTZ,
+ bib_record BIGINT,
+ bib_editor INTEGER,
+ bib_edit_date TIMESTAMPTZ,
+ bib_cataloging_date TIMESTAMPTZ,
+ bib_create_date TIMESTAMPTZ,
+ field_def INTEGER,
+ heading_date TIMESTAMPTZ,
+ rank BIGINT
+ ) AS $FUNK$
+BEGIN
+ RETURN QUERY EXECUTE $$
+ WITH cataloged_entries AS (
+ SELECT
+ entry.id, entry.value, entry.sort_value, entry.create_date,
+ map.source, bre.editor, bre.edit_date, bre.cataloging_date,
+ bre.create_date, map.def,
+ GREATEST(bre.cataloging_date, entry.create_date),
+ RANK() OVER (
+ PARTITION BY entry.id
+ ORDER BY
+ bre.cataloging_date,
+ map.id -- tie breaker
+ )
+ FROM metabib.browse_$$ || browse_type || $$_entry entry
+ JOIN metabib.browse_$$ || browse_type || $$_entry_def_map map
+ ON (map.entry = entry.id)
+ JOIN biblio.record_entry bre ON (bre.id = map.source)
+ WHERE
+ bre.deleted IS FALSE AND
+ bre.cataloging_date IS NOT NULL
+ )
+ SELECT all_entries.*
+ FROM cataloged_entries all_entries
+ -- we only want the most relevent linked bib record
+ WHERE all_entries.rank = 1
+ $$;
+END;
+$FUNK$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE VIEW reporter.cataloged_browse_entry_combined AS
+ SELECT 'subject'::TEXT AS browse_axis, *
+ FROM reporter.cataloged_browse_entries('subject')
+ UNION ALL
+ SELECT 'author'::TEXT AS browse_axis, *
+ FROM reporter.cataloged_browse_entries('author')
+ UNION ALL
+ SELECT 'series'::TEXT AS browse_axis, *
+ FROM reporter.cataloged_browse_entries('series')
+ ;
+
+CREATE OR REPLACE VIEW reporter.cataloged_browse_entry AS
+SELECT entry.*,
+ cmf.label AS field_label,
+ usr.usrname AS bib_editor_usrname,
+ evergreen.marc_tag_to_string('1', bre.marc) AS bib_marc_1xx,
+ evergreen.marc_tag_to_string('245', bre.marc) AS bib_marc_245,
+ (reporter.metabib_browse_entry_window_prev(
+ entry.browse_axis, entry.entry_sort_value)).*,
+ (reporter.metabib_browse_entry_window_next(
+ entry.browse_axis, entry.entry_sort_value)).*
+FROM reporter.cataloged_browse_entry_combined entry
+ JOIN config.metabib_field cmf ON (cmf.id = entry.field_def)
+ JOIN biblio.record_entry bre ON (bre.id = entry.bib_record)
+ JOIN actor.usr usr ON (usr.id = bre.editor)
+ORDER BY
+ CASE entry.browse_axis
+ WHEN 'author' THEN 1
+ WHEN 'subject' THEN 2
+ ELSE 3
+ END,
+ entry.entry_sort_value
+;
+
+-- Local testing
+
+/*
+update metabib.browse_series_entry set create_date = now() - '2 days'::interval;
+update metabib.browse_series_entry set create_date = now() WHERE id between 1 and 1000;
+update metabib.browse_subject_entry set create_date = now() - '2 days'::interval;
+update metabib.browse_subject_entry set create_date = now() WHERE id between 1 and 1000;
+update metabib.browse_author_entry set create_date = now() - '2 days'::interval;
+update metabib.browse_author_entry set create_date = now() WHERE id between 1 and 1000;
+*/
+
+/*
+SELECT *
+FROM reporter.cataloged_browse_subject_entry
+WHERE DATE(heading_date) >= current_date ORDER BY entry_sort_value;
+*/
+
+-- /LOCAL TESTING
+
+COMMIT;
--- /dev/null
+-- Revert kcls-evergreen:new-headings-report from pg
+
+BEGIN;
+
+/*
+DROP VIEW reporter.cataloged_browse_subject_entry;
+DROP VIEW reporter.cataloged_browse_author_entry;
+DROP VIEW reporter.cataloged_browse_series_entry;
+*/
+
+DROP VIEW reporter.cataloged_browse_entry;
+DROP VIEW reporter.cataloged_browse_entry_combined;
+
+DROP FUNCTION IF EXISTS
+ reporter.cataloged_browse_entries(TEXT);
+DROP FUNCTION IF EXISTS
+ reporter.metabib_browse_entry_window_prev(TEXT, TEXT);
+DROP FUNCTION IF EXISTS
+ reporter.metabib_browse_entry_window_next(TEXT, TEXT);
+DROP FUNCTION IF EXISTS
+ reporter.metabib_browse_entry_window_part (TEXT, TEXT, BOOLEAN);
+
+DROP FUNCTION evergreen.marc_tag_to_string(TEXT, TEXT);
+
+ALTER TABLE metabib.browse_author_entry DROP COLUMN create_date;
+ALTER TABLE metabib.browse_subject_entry DROP COLUMN create_date;
+ALTER TABLE metabib.browse_series_entry DROP COLUMN create_date;
+ALTER TABLE metabib.browse_title_entry DROP COLUMN create_date;
+ALTER TABLE metabib.browse_call_number_entry DROP COLUMN create_date;
+
+COMMIT;
audit-table-maint [payflow-hosted-org-settings] 2016-07-22T14:49:08Z Bill Erickson <berickxx@gmail.com> # Audit table cleanup functions
payflow-hosted-static-silent-post-url [payflow-hosted-org-settings] 2016-08-17T13:14:48Z Bill Erickson <berickxx@gmail.com> # Fall back to PP silent post URL
teacher-group [student-groups] 2016-09-16T17:43:41Z Bill Erickson <berickxx@gmail.com> # Teach ecard group and policy configs
+new-headings-report [po-print-li-count-and-date] 2016-01-28T22:23:12Z Bill Erickson <berickxx@gmail.com> # Support for reporting on new browse headings
--- /dev/null
+-- Verify kcls-evergreen:new-headings-report on pg
+
+BEGIN;
+
+-- XXX Add verifications here.
+
+ROLLBACK;
<link field="simple_heading" reltype="has_a" key="id" map="" class="ash"/>
</links>
</class>
+ <class id="rcbe"
+ controller="open-ils.cstore open-ils.pcrud"
+ oils_obj:fieldmapper="reporter::cataloged_browse_entry"
+ oils_persist:tablename="reporter.cataloged_browse_entry"
+ reporter:label="Cataloged Headings"
+ reporter:core="true" oils_persist:readonly="true">
+ <fields>
+ <field name="browse_axis" reporter:datatype="text" reporter:label="Heading Type"/>
+ <field name="heading_date" reporter:datatype="timestamp" reporter:label="Heading Date"/>
+ <field name="prev_entry_value" reporter:datatype="text" reporter:label="Previous Heading"/>
+ <field name="prev_entry" reporter:datatype="link" reporter:label="Previous Browse Entry"/>
+ <field name="prev_bib_record" reporter:datatype="link" reporter:label="Previous Bib Record"/>
+ <field name="prev_field_label" reporter:datatype="text" reporter:label="Previous Field Label"/>
+ <field name="prev_field_def" reporter:datatype="link" reporter:label="Previous Field Definition"/>
+ <field name="prev_auth_tag" reporter:datatype="text" reporter:label="Previous Authority Tag"/>
+ <field name="entry_value" reporter:datatype="text" reporter:label="Heading"/>
+ <field name="entry" reporter:datatype="link" reporter:label="Browse Entry"/>
+ <field name="entry_create_date" reporter:datatype="timestamp" reporter:label="Browse Entry Create Date"/>
+ <field name="bib_record" reporter:datatype="link" reporter:label="Bib Record"/>
+ <field name="field_label" reporter:datatype="text" reporter:label="Field Label"/>
+ <field name="field_def" reporter:datatype="link" reporter:label="Field Definition"/>
+ <field name="bib_edit_date" reporter:datatype="timestamp" reporter:label="Bib Record Edit Date"/>
+ <field name="bib_create_date" reporter:datatype="timestamp" reporter:label="Bib Record Create Date"/>
+ <field name="bib_cataloging_date" reporter:datatype="timestamp" reporter:label="Bib Record Cataloging Date"/>
+ <field name="bib_marc_1xx" reporter:datatype="text" reporter:label="Bib Record MARC 1XX"/>
+ <field name="bib_marc_245" reporter:datatype="text" reporter:label="Bib Record MARC 245"/>
+ <field name="bib_editor" reporter:datatype="link" reporter:label="Bib Record Editor"/>
+ <field name="bib_editor_usrname" reporter:datatype="text" reporter:label="Bib Record Editor Username"/>
+ <field name="next_entry" reporter:datatype="link" reporter:label="Next Browse Entry"/>
+ <field name="next_entry_value" reporter:datatype="text" reporter:label="Next Heading"/>
+ <field name="next_bib_record" reporter:datatype="link" reporter:label="Next Bib Record"/>
+ <field name="next_field_label" reporter:datatype="text" reporter:label="Next Field Label"/>
+ <field name="next_field_def" reporter:datatype="link" reporter:label="Next Field Definition"/>
+ <field name="next_auth_tag" reporter:datatype="text" reporter:label="Next Authority Tag"/>
+ </fields>
+ <links>
+ <link field="prev_entry" reltype="has_a" key="id" map="" class="mbae"/>
+ <link field="prev_bib_record" reltype="has_a" key="id" map="" class="bre"/>
+ <link field="prev_field_def" reltype="has_a" key="id" map="" class="cmf"/>
+ <link field="entry" reltype="has_a" key="id" map="" class="mbae"/>
+ <link field="bib_record" reltype="has_a" key="id" map="" class="bre"/>
+ <link field="bib_editor" reltype="has_a" key="id" map="" class="au"/>
+ <link field="field_def" reltype="has_a" key="id" map="" class="cmf"/>
+ <link field="next_entry" reltype="has_a" key="id" map="" class="mbae"/>
+ <link field="next_bib_record" reltype="has_a" key="id" map="" class="bre"/>
+ <link field="next_field_def" reltype="has_a" key="id" map="" class="cmf"/>
+ </links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <retrieve permission="STAFF_LOGIN" global_required='true'/>
+ </actions>
+ </permacrud>
+ </class>
<class id="mfe" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="metabib::field_entry" reporter:label="Combined Field Entry View" oils_persist:readonly="true">
<oils_persist:source_definition><![CDATA[
SELECT * FROM metabib.author_field_entry
--- /dev/null
+[% ctx.page_title = l('New Headings Report') %]
+[% WRAPPER base.tt2 %]
+
+<script type="text/javascript"
+ src='[% ctx.media_prefix %]/js/ui/default/cat/authority/new_headings.js'> </script>
+
+<style type="text/css">
+
+ .date-input { width: 6em; }
+
+ #headings-table th {
+ font-weight: bold;
+ font-size: 120%;
+ border-bottom: 2px solid grey;
+ }
+
+ .extended-field-label {
+ white-space : nowrap;
+ font-weight: bold;
+ }
+
+ .heading-row:nth-child(even) {
+ /*background-color: #9ee7fa;*/
+ background-color: rgba(158,231,250, 0.3);
+ }
+
+ .new-heading { font-weight:bold; }
+ .extended-heading-value {
+ color: red; font-weight: bold;
+ }
+
+ .dialog-div { width: 700px; }
+ .dialog-div td { padding: 5px; }
+ #headings-row-loading td { padding: 10px; background-color: #AAA }
+
+ #navigation-div-container {
+ font-size: 120%;
+ position:fixed;
+ top: 0px;
+ height: 30px;
+ background-color: white;
+ }
+
+ #navigation-div {
+ background-color: #9ee7fa;
+ border-bottom: solid grey 1px;
+ height: 25px;
+ }
+
+ #navigation-div span { padding-left: 10px; padding-right: 10px }
+
+ #below-nav-div {margin-top: 35px}
+
+ .type-heading-row td {
+ font-size: 125%;
+ font-weight: bold;
+ padding-top: 7px;
+ padding-bottom: 7px;
+ text-align: center;
+ background-color: #8c65d3;
+ color: snow;
+ }
+
+ #loading-indicator, #zero-hits {
+ width: 100%;
+ margin-top: 50px;
+ font-size: 120%;
+ text-align: center;
+ }
+
+ .headings-entry-table {
+ border-bottom: 2px dashed #333;
+ }
+
+ .headings-entry-table td {
+ padding: 3px;
+ }
+
+</style>
+
+<div id='navigation-div-container'>
+ <div id='navigation-div'>
+ <div style='float:left'>
+ <span>Start Date: <input type='text' jsId='startDate'
+ class='date-input' dojoType='dijit.form.DateTextBox'/></span>
+ <span>End Date: <input type='text' jsId='endDate'
+ class='date-input' dojoType='dijit.form.DateTextBox'/></span>
+ <span><button id='apply-dates-btn'>Apply Dates</button></span>
+ </div>
+ <div style='padding-left: 20px; padding-right: 20px; float:left'>|</div>
+ <div style='float:left'>
+ <span><button id='prev-page-btn'>Previous Page</button></span>
+ <span>Page: <b id='page-number'></b></span>
+ <span><button id='next-page-btn'>Next Page</button></span>
+ </div>
+ <div style='clear:both'></div>
+ </div>
+ <div id='navigation-div-pad'></div>
+</div>
+
+<div id='below-nav-div'>
+
+ <div id='loading-indicator'>
+ Loading page <span id='ind-page'></span>...
+ <img src='/opac/images/progressbar_green.gif'/>
+ </div>
+
+ <div id='zero-hits'>
+ No new headings were found for the date range specified
+ </div>
+
+ <table id='headings-table'>
+ <tbody id='headings-tbody'>
+
+ <tr id='author-header-row' class='type-heading-row hidden'>
+ <td colspan='20'>
+ New Author Headings <!--(<span id='author-count'></span>)-->
+ </td>
+ </tr>
+ <tr id='subject-header-row' class='type-heading-row hidden'>
+ <td colspan='20'>
+ New Subject Headings <!--(<span id='subject-count'></span>)-->
+ </td>
+ </tr>
+ <tr id='series-header-row' class='type-heading-row hidden'>
+ <td colspan='20'>
+ New Series Headings <!--(<span id='series-count'></span>)-->
+ </td>
+ </tr>
+
+ <tr id='headings-row-template' class='heading-row'>
+ <td>
+ <table class='headings-entry-table'>
+ <tr>
+ <td>Previous:</td>
+ <td>
+ <span name='prev_entry_value-auth-bib-tag'></span>
+ <span name='prev_entry_value'></span>
+ <span name='prev-auth-tag-wrapper' class='hidden'>
+ (From <span name='prev_auth_tag'></span>)
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <td>New:</td>
+ <td>
+ <a href='javascript:;' name='heading-dialog-link'>
+ <span name='entry_value' class='new-heading'></span>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>Next:</td>
+ <td>
+ <span name='next_entry_value-auth-bib-tag'></span>
+ <span name='next_entry_value'></span>
+ <span name='next-auth-tag-wrapper' class='hidden'>
+ (From <span name='next_auth_tag'></span>)
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <td>From TCN:</td>
+ <td>
+ <a href='[% ctx.base_path %]/opac/record/'
+ target='_blank' name='bib_record-catalog-link'>
+ <span name='bib_record'></span>
+ </a>
+ <span> </span>
+ <span>Indexed As:</span>
+ <span><span name='field_label'></span></span>
+ </td>
+ </tr>
+ <tr>
+ <td>Edited By:</td>
+ <td><span name='bib_editor_usrname'></span>
+ <span> </span>
+ <span>Heading Date:</span>
+ <span><span name='heading_date'></span></span>
+ </td>
+ </tr>
+ <tr>
+ <td>From 1XX:</td>
+ <td><span name='bib_marc_1xx'></span></td>
+ </tr>
+ <tr>
+ <td>From 245:</td>
+ <td><span name='bib_marc_245'></span></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div class='hidden'>
+ <div dojoType='dijit.Dialog' jsId='detail_dialog' class='dialog-div'>
+ <table id='extended-info-table'>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Previous Heading</td>
+ <td><span name='extended-prev_entry_value' class='extended-heading-value'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Previous Indexed As</td>
+ <td><span name='extended-prev_field_label'></span></td>
+ <td class='extended-field-label'>Previous TCN</td>
+ <td><span name='extended-prev_bib_record'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Previous Authority Tag</td>
+ <td><span name='extended-prev_auth_tag'></span></td>
+ <td class='extended-field-label'>Previous Browse Entry ID</td>
+ <td><span name='extended-prev_entry'></span></td>
+ </tr>
+
+ <tr><td colspan='4'><hr/></td></tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>New Heading</td>
+ <td><span name='extended-entry_value' class='extended-heading-value'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Heading Date</td>
+ <td><span name='extended-heading_date'></span></td>
+ <td class='extended-field-label'>Indexed As</td>
+ <td><span name='extended-field_label'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>From TCN</td>
+ <td><span name='extended-bib_record'></span></td>
+ <td class='extended-field-label'>Browse Entry ID</td>
+ <td><span name='extended-entry'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Heading Create Date</td>
+ <td><span name='extended-entry_create_date'></span></td>
+ <td class='extended-field-label'>Bib Record Edit Date</td>
+ <td><span name='extended-bib_edit_date'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Bib Record Cataloging Date</td>
+ <td><span name='extended-bib_cataloging_date'></span></td>
+ <td class='extended-field-label'>Bib Record Create Date</td>
+ <td><span name='extended-bib_create_date'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Bib Record Editor Username</td>
+ <td><span name='extended-bib_editor_usrname'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>From 1XX</td>
+ <td colspan='3'><span name='extended-bib_marc_1xx'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>From 245</td>
+ <td colspan='3'><span name='extended-bib_marc_245'></span></td>
+ </tr>
+
+ <tr><td colspan='4'><hr/></td></tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Next Heading</td>
+ <td><span name='extended-next_entry_value' class='extended-heading-value'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Next Indexed As</td>
+ <td><span name='extended-next_field_label'></span></td>
+ <td class='extended-field-label'>Next TCN</td>
+ <td><span name='extended-next_bib_record'></span></td>
+ </tr>
+
+ <tr class='heading-field-row'>
+ <td class='extended-field-label'>Next Authority Tag</td>
+ <td><span name='extended-next_auth_tag'></span></td>
+ <td class='extended-field-label'>Next Browse Entry ID</td>
+ <td><span name='extended-next_entry'></span></td>
+ </tr>
+
+ <tr>
+ <td colspan='2'>
+ <div dojoType='dijit.form.Button' type='submit'
+ jsId='detail_dialog_closer'>[% l('Close') %]</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+</div><!-- below-nav-div -->
+
+[% END %]
--- /dev/null
+dojo.require('dijit.Dialog');
+dojo.require('dijit.form.DateTextBox');
+dojo.require('dijit.form.Button');
+dojo.require('dojo.date.locale');
+dojo.require("fieldmapper.Fieldmapper");
+dojo.require('openils.CGI');
+dojo.require('openils.PermaCrud');
+dojo.require('openils.Util');
+dojo.require('openils.XUL');
+
+var cgi = new openils.CGI();
+var pcrud = new openils.PermaCrud();
+var page_size = 50; // TODO UI
+var page_offset = 0; // TODO UI
+var start_date;
+var end_date;
+// Max summary display field length before truncation
+var max_val_length = 80;
+var headings_tbody;
+var template_row;
+var cached_headings
+var all_fetched;
+
+var summary_fields = [
+ 'prev_entry_value',
+ 'entry_value',
+ 'next_entry_value',
+ 'field_label',
+ 'bib_record',
+ 'heading_date',
+ 'bib_editor_usrname',
+ 'prev_auth_tag',
+ 'next_auth_tag',
+ 'bib_marc_1xx',
+ 'bib_marc_245'
+];
+
+var extended_fields = [
+ 'prev_entry',
+ 'prev_bib_record',
+ 'prev_field_label',
+ 'entry',
+ 'entry_create_date',
+ 'bib_edit_date',
+ 'bib_create_date',
+ 'bib_cataloging_date',
+ 'next_entry',
+ 'next_bib_record',
+ 'next_field_label',
+];
+
+extended_fields = extended_fields.concat(summary_fields);
+
+function load() {
+
+ if (!headings_tbody) {
+ headings_tbody = dojo.byId('headings-tbody');
+ template_row = headings_tbody.removeChild(
+ dojo.byId('headings-row-template'));
+ }
+
+ setup_paging();
+
+ dojo.byId('apply-dates-btn').onclick = function() {
+ page_offset = 0;
+ load_headings(true); // reset
+ }
+
+ load_headings(true);
+}
+
+// Fetch headings.
+// Limiting each pcrud query by browse_axis significantly speeds up
+// the DB query, so fetch in order of display: author -> subject -> series.
+function load_headings(is_new, new_page) {
+
+ if (is_new) { // reset cache
+ cached_headings = [];
+ all_fetched = false;
+ new_page = true;
+ openils.Util.hide('zero-hits');
+ }
+
+ if (new_page) {
+ openils.Util.show('loading-indicator');
+ dojo.byId('ind-page').innerHTML = page_number();
+ openils.Util.hide('headings-table');
+ openils.Util.hide('author-header-row');
+ openils.Util.hide('subject-header-row');
+ openils.Util.hide('series-header-row');
+ dojo.query('[class=heading-row]').forEach(function(node) {
+ node.parentNode.removeChild(node)
+ });
+ }
+
+ // force the main dojo body div to scroll to the top
+ // during all navigation.
+ dojo.byId('oils-base-body-block').scrollTop = 0;
+
+ if (cached_headings.length >= page_offset + page_size || all_fetched) {
+ return draw();
+ }
+
+ /* cache 4 pages, plus 1 heading at a time */
+ var fetch_count = page_size * 4 + 1;
+
+ pcrud.search('rcbe', compile_date_filter(), {
+ offset : page_offset,
+ limit : fetch_count,
+ async : true,
+ oncomplete : function(r) {
+ var new_headings = openils.Util.readResponse(r);
+ cached_headings = cached_headings.concat(new_headings);
+
+ var fetched = new_headings.length;
+ console.log('Loaded ' + fetched + ' headings');
+
+ if (fetched < fetch_count) all_fetched = true;
+ draw();
+ }
+ });
+}
+
+var first_load = true;
+function compile_date_filter() {
+
+ if (first_load) {
+ first_load = false;
+ startDate.attr('value', new Date());
+ endDate.attr('value', new Date());
+ }
+
+ start_date = startDate.attr('value');
+ end_date = endDate.attr('value');
+
+ if (start_date) {
+ start_date = openils.Util.getYMD(start_date);
+ }
+
+ if (end_date) {
+ // the end date has to be extended by one day, since the between
+ // query cuts off at midnight (0 hour) on the end date, which
+ // would miss all headings for that date created after hour 0.
+ // note: setDate() will rollover to the next month when needed.
+ end_date.setDate(end_date.getDate() + 1);
+ end_date = openils.Util.getYMD(end_date);
+ }
+
+ var date_filter = {heading_date : {}};
+
+ if (start_date && end_date) {
+ date_filter.heading_date = {between : [start_date, end_date]}
+ } else if (start_date) {
+ date_filter.heading_date = {'>=' : start_date};
+ } else {
+ date_filter.heading_date = {'<=' : end_date};
+ }
+
+ return date_filter;
+}
+
+function page_number() {
+ return (page_offset / page_size) + 1
+}
+
+function setup_paging() {
+
+ dojo.byId('page-number').innerHTML = page_number();
+
+ dojo.byId('prev-page-btn').onclick = function() {
+ if (page_offset <= 0) return;
+ page_offset -= page_size;
+ load_headings(false, true);
+ dojo.byId('page-number').innerHTML = page_number();
+ }
+
+ dojo.byId('next-page-btn').onclick = function() {
+ page_offset += page_size;
+ load_headings(false, true);
+ dojo.byId('page-number').innerHTML = page_number();
+ }
+}
+
+function draw() {
+
+ headings = cached_headings.slice(page_offset, page_offset + page_size);
+ openils.Util.hide('loading-indicator');
+
+ if (headings.length == 0) {
+ openils.Util.show('zero-hits');
+ return;
+ }
+
+ openils.Util.show('headings-table', 'table');
+
+ if (page_offset == 0) {
+ dojo.byId('prev-page-btn').setAttribute('disabled', true);
+ } else {
+ dojo.byId('prev-page-btn').removeAttribute('disabled');
+ }
+
+ console.log('total = ' + cached_headings.length);
+ console.log('page size = ' + page_size);
+ console.log('page offset = ' + page_offset);
+
+ if (cached_headings.length > page_size + page_offset) {
+ dojo.byId('next-page-btn').removeAttribute('disabled');
+ } else {
+ dojo.byId('next-page-btn').setAttribute('disabled', true);
+ }
+
+ dojo.forEach(headings, function(heading, idx) {
+
+ var row = template_row.cloneNode(true);
+ row.id = 'headings-row-' + idx;
+
+ if (heading.browse_axis() == 'author') {
+ headings_tbody.insertBefore(row, dojo.byId('subject-header-row'));
+ openils.Util.show('author-header-row', 'table-row');
+ } else if (heading.browse_axis() == 'subject') {
+ openils.Util.show('subject-header-row', 'table-row');
+ headings_tbody.insertBefore(row, dojo.byId('series-header-row'));
+ } else {
+ openils.Util.show('series-header-row', 'table-row');
+ headings_tbody.appendChild(row);
+ }
+
+ if (!heading.prev_bib_record()) {
+ openils.Util.show(
+ openils.Util.getNodeByName('prev-auth-tag-wrapper', row),
+ 'inline'
+ );
+ }
+
+ if (!heading.next_bib_record()) {
+ openils.Util.show(
+ openils.Util.getNodeByName('next-auth-tag-wrapper', row),
+ 'inline'
+ );
+ }
+
+ dojo.forEach(summary_fields, function(heading_field) {
+ var value = heading[heading_field]();
+
+ if (heading_field.match(/date/)) {
+ value = dojo.date.locale.format(
+ new Date(Date.parse(value)), {formatLength:'short'});
+ } else {
+ // prevent values from being crazy long
+ if (value && value.length > max_val_length)
+ value = value.substr(0, max_val_length) + '...';
+ }
+
+ openils.Util.getNodeByName(heading_field, row).innerHTML = value;
+
+ if (heading_field == 'entry_value') {
+ var link = openils.Util.getNodeByName(
+ 'heading-dialog-link', row);
+
+ link.onclick = function() {
+ flesh_details_dialog(heading);
+ }
+ } else if (heading_field.match('entry_value')) {
+ var node = openils.Util.getNodeByName(
+ heading_field + '-auth-bib-tag', row);
+ if (heading_field == 'prev_entry_value') {
+ node.innerHTML = heading.prev_bib_record() ? 'B: ' : 'A: ';
+ } else {
+ node.innerHTML = heading.next_bib_record() ? 'B: ' : 'A: ';
+ }
+ }
+
+ if (heading_field == 'bib_record') {
+ var link = openils.Util.getNodeByName(
+ 'bib_record-catalog-link', row);
+
+ // append the bib record ID to the in-template catalog link.
+ link.setAttribute('href', link.getAttribute('href') + value);
+
+ if (openils.XUL.isXUL()) {
+ // open a new staff client tab to view the bib record
+ var target_url = oilsBasePath + '/opac/record/' + value;
+
+ link.onclick = function() {
+ openils.XUL.newTabEasy(
+ xulG.url_prefix(
+ 'chrome://open_ils_staff_client/content/cat/opac.xul'),
+ null, {'opac_url' : target_url}
+ );
+ return false;
+ }
+ }
+ }
+ });
+ });
+}
+
+function flesh_details_dialog(heading) {
+
+ detail_dialog.show();
+
+ dojo.forEach(extended_fields, function(heading_field) {
+ var value = heading[heading_field]();
+
+ if (heading_field.match(/date/)) {
+ value = dojo.date.locale.format(
+ new Date(Date.parse(value)), {formatLength:'short'});
+ }
+
+ try {
+ openils.Util.getNodeByName('extended-' + heading_field,
+ detail_dialog.domNode).innerHTML = value;
+ } catch (E) {
+ console.error('Error setting value for field ' + heading_field);
+ throw E;
+ }
+ });
+}
+
+dojo.addOnLoad(load);
+
<div style='padding: 8px;'>
<a href='javascript:window.xulG.new_tab("/xul/server/admin/transit_list.xul",{"tab_name":"&staff.server.admin.index.transits;"},{});'>&staff.server.admin.index.transit_list;</a>
</div>
+ <div style='padding: 8px;'>
+ <a href='/eg/cat/authority/new_headings'>New Headings Report</a>
+ </div>
</td>
</tr>
</tbody>