[open-ils-commits] [GIT] Evergreen ILS branch master updated. 6227dcdcd4cf3be18579b9239857c8c3f3ae0e11

Evergreen Git git at git.evergreen-ils.org
Wed Jul 6 15:29:18 EDT 2011


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  6227dcdcd4cf3be18579b9239857c8c3f3ae0e11 (commit)
       via  2fc6c995cd469353f494e336b8db3b677aa3d25e (commit)
       via  11218af3531a20925b6a9fdc5aa0eb97777f15a7 (commit)
       via  aa2579147103fef0d17f249336c088d041b5b403 (commit)
       via  05df9c099d497a017a03f37f5116750691d01087 (commit)
       via  53ed31174c82d971e931682d80c54e77b07c15f8 (commit)
       via  70ea48192396caf3051673162cc06a423d065e02 (commit)
       via  55b5194b26062cef811f5fde9487d4b64158bcb5 (commit)
       via  b79724aa8ad7ea1783b8f100e19effa493b39b34 (commit)
       via  a513654859fd4eeb188097355a34074406a85479 (commit)
       via  19ab89718293f1ae761a0237cea47ecbfda76f9c (commit)
       via  4c0d9bcacb0cb29c2a2c33032fed8392299353b5 (commit)
       via  669d12b7b58da33fa889fc0faa8b1ea40f4733d8 (commit)
       via  0612489eb8d325b30c4fa1bf7afa961cd9c45979 (commit)
       via  bae1ec5dd89662529127eab766ba100afe53c618 (commit)
       via  3b250d8c958b209e0739eda15bb58b1456e2ff5a (commit)
       via  a56d95c3218ed1a4c78434430df3206e752c1a54 (commit)
       via  205675af130077e3fe74325983f22c68d74267a9 (commit)
       via  780e59e5baa189e7c7fa01c41378cd51cbbfb834 (commit)
       via  f2218fa2015df835718d4bba856c3365e84333f3 (commit)
       via  f18a260e1f75c090e1a1567173c53d82fd651fb2 (commit)
       via  05c990e3c61a140b5036ea1b55f3274cd5cb04a4 (commit)
       via  70f893c2d049f17ae7ff35d86c2f48cae01e1503 (commit)
       via  12029c8f696863957ddbbdb784c4dda0a8e94f56 (commit)
       via  7f913b210b523d68dd4bb7395b37dd5f824e1e22 (commit)
       via  0cf7d27b68cba7d10942db2ee8d8ae3ff8fc17ec (commit)
       via  10c443b05e002ba1cf3c917c228b77e312f35c59 (commit)
       via  e9c64425a8df9e91c9f14051e87138edd5d84aeb (commit)
       via  c094b362e18e9fed3551c2c2fa9375dbb0627789 (commit)
       via  40a118a3ddf858ad88f5696406b6b3abac9dcc0f (commit)
       via  98d5a7532ec67ce24c385648ba4cc8a24fdefb26 (commit)
       via  a65d906c7591a7c1d6312d3772b44272f61627b2 (commit)
       via  bd61d29819665315ba2f8ac2de9e162e864dabd0 (commit)
       via  6e2fb189b6c199f0ec08662352803044b228abe7 (commit)
       via  78366c9c9a039c722fd5b1187a6a9d431f3a61e0 (commit)
       via  93aef19351dcb1538c9a4ddf2d5bfcbd37284015 (commit)
       via  6162208b18d9351870770648d70e68575d7d68a2 (commit)
       via  eb4fbe43bb76aefc210bb92137996474887da0bf (commit)
       via  9cc978be2ed4741410ce4d95913f4cf53cb93ba7 (commit)
       via  72522570c89283573ebf914a604c12c016761208 (commit)
       via  0853fdbf76f0ceadc77be39c7a5961dde409d756 (commit)
       via  97f5bf78b56c673678404eec7c8961e49316cb35 (commit)
       via  d49ebdc0a9995bfcf7829cd3f5e0b0c2a88509cb (commit)
       via  43e5b25daf7eecea247aebaf76fd2e2c722ab5af (commit)
       via  f9ef894ebd6d76f1a85c67d82473c6af31ad4ac3 (commit)
       via  57692a847e30c2a95c568f2b15ed0cc6669a392c (commit)
       via  e4f5a23f414a1afe91a758e563bed1112d0bc9d6 (commit)
       via  0f725603bf94f71bf71de57f9c9e8c42f445cc0c (commit)
       via  6baadbc2fcd5baf128d6ad7768726addae1d9a2b (commit)
       via  64e04af74bc1d40a168dd6e4244b0769cfafcb44 (commit)
       via  207dd51b5dae6f5c4f88f576f79d6736b16ef559 (commit)
       via  ff796bd6fc1ae0744a7f7efc73a0bdb11b9975e8 (commit)
       via  130a5dcf1f0c8bdc79dc617873e20033dc433311 (commit)
       via  815e3e2f48614f1cead864d4151f188c62a88d95 (commit)
       via  da3a453442050482a419deff91d3dc92fdbf132a (commit)
       via  9e9c1c3b6ab1c85e803f7611a38cfd4a9113e027 (commit)
       via  aa4fbb88b33a5b8f55295605caecf4841cfbb05a (commit)
       via  5a13cc0b51f5d5a96a125beac16bf587c6189701 (commit)
       via  b212468779aa74dd719cb9ac614919c749927260 (commit)
       via  45cf5cbebb15792d0439346d857e0a85aee7200d (commit)
       via  1395a488c2db041cffc9558d48c2beaef276845a (commit)
       via  054ac72165d347166553d3b0987d1442c194ee2d (commit)
       via  d856654f61afd4c674440c2d2b068336c3d5dfbd (commit)
       via  76699369880f251fa0aae5f936642e53cb658a92 (commit)
       via  9af9f804e7e272845b0dce09d98c1f88f0097670 (commit)
       via  5c858d48ef21bb7ed74697b209120faf88c5fcef (commit)
       via  c3201eb4258ce9f1317910417ea6d3b810354727 (commit)
       via  8c40a2e78e76a495bd3d77abdf565ac1175c89c0 (commit)
       via  b27b7881d3c3c776bca73b78e02149b3a9459f4c (commit)
       via  8c223ecec89a5e097f13d788e9db016544f761e9 (commit)
       via  065b545d3e1ad5ab06205ce8205d49b7dc632610 (commit)
       via  b81fd284cb7e600c6f4011741f9d7f91618cab12 (commit)
       via  f1170755a54cfeb7f55d394989ebbb5c14d9cc85 (commit)
       via  ce07a23e829a7271fc4b11af72117b8c14837ad3 (commit)
       via  d2127cf97568f02c8b7cf40e8d981b677455f619 (commit)
       via  0d784d89f00fd350181e6559a9c94bca039210c8 (commit)
       via  a978e74d8303bf39bdcd081c2f77bb5289d1159d (commit)
       via  0aec475a117564063a98a4e0219b35946eb393db (commit)
       via  90047c17588fa07bb6cabca1ec14f290198d8883 (commit)
       via  a8f7f42453896dc97af0d51729a1a04ea7b3e900 (commit)
       via  7e245cb05b5978b19c7f7f5236cff93906e11718 (commit)
       via  41432f29df97e0ca44a7ac4dc7163f8ce7900803 (commit)
       via  ff2ab6bfd10050306dbbb65e1134703433b6b46a (commit)
       via  19bb47e04e1de3764fc1e3b93be7589bae0d0e14 (commit)
       via  f44258e7f54013e6c64f1aeeb617f4b7f016a696 (commit)
       via  d50e03f75c2fa5635e715dcaa8f057f577a508f1 (commit)
       via  e14a3f4e11bc91d6cc0100209be4e2147f559301 (commit)
       via  7cca21c8c126e98a64ec8677d2981d4a472ca26a (commit)
       via  3585f727e20b8d986e1983213fb6b6677ae169ab (commit)
       via  e1a271d2525911e5c1cdcbb4bb495b6295acb5df (commit)
       via  06e1fa2563792748dcebdabbd0a32efa11d559b4 (commit)
       via  b61162301e6c81b2be3b0def8438535d2fb4c28b (commit)
       via  2620862d7ebafed7a86c26c682a01dcc2d11f705 (commit)
       via  d5c786e515791eaf53992dac8a6bd11520a661d6 (commit)
       via  1aaf8a040116c614eb014fd2582e6bbc702b5e92 (commit)
       via  ca861020320e5a412114655eba10efe120a668a7 (commit)
       via  4de86d2c97b9117db2808681d091f85c79279373 (commit)
       via  b2f78352b012820c1bbf4a6caa49d2380d9a3145 (commit)
       via  c7b99f8d90aa0b3a2be4fc609bfa83e5da261431 (commit)
       via  813ac365b8ebf1e4d2e4434002ab248a0160833e (commit)
       via  a02e77558027149e5a8d405daba583baa394c796 (commit)
       via  a2a99fc7368401e56c0ff331b39f800ff320ca60 (commit)
       via  8aa208b2796b05c74d2a4efa126b71514448e4fc (commit)
       via  091e82590d2d7b63fa4e882cbc9681b470ba97e2 (commit)
       via  c9ee753591520129794452347403801545894caf (commit)
       via  a050890c67f43f989810bcb76695ff4f13eea938 (commit)
       via  bae118889863f9fa37fbafd195c5d8ed86404f11 (commit)
       via  fe0c574d0fd474cf1145786ed959ceb4df840874 (commit)
       via  3a406e5cb384a22ade99069a69cec8d9091073c6 (commit)
       via  9e82b590abbc53e2e2059067028a25e4c25dbef2 (commit)
       via  c78f3ec233c08fa7a3526621f06ea1521a47074c (commit)
       via  e04c57f1234925ba9acd681e2d944060a843975a (commit)
       via  128554e383bafb7f9ac7b0edd3ec17ebb3ff90e0 (commit)
       via  5e0b24e22043d49b2b87eb7157df1b97e7713b91 (commit)
       via  6c32ca2e574f57e2eb57bc29c75b3ff506651a9e (commit)
       via  18f70edb704e984ff70b7182d4795f5e28ef4168 (commit)
       via  752e696bba75275c8e6330ff8fe7444318b9e21f (commit)
       via  55bd5e663a1a7d3e611d124fea3aee68f54daeba (commit)
       via  6795405f13dead5dff39cf884fa727c01d6f4636 (commit)
       via  b1854026fc611c259a66b5c537af6018be981d15 (commit)
       via  13041c7e9aabd1f8067ad51efaa2b92b74c2f319 (commit)
       via  e0114f18272c347795c21ac59e9c1e4520e2de65 (commit)
       via  efba3908ce39499b007161f1c25b0d5de42e10fa (commit)
       via  934603b401b6db47f65e3d706283f291cef8d96f (commit)
       via  efa9520b2133321b1649c92eda5db29d1d6d8ec5 (commit)
       via  57b7f22475b6b3cdbe3514546c82eb5b2947041c (commit)
       via  66144a226706c2ee8cccbcf2c5248f3c4aed404c (commit)
      from  b17e3b14d28839739764ce4c29bd3bec01f0fc3c (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 6227dcdcd4cf3be18579b9239857c8c3f3ae0e11
Merge: b17e3b1 2fc6c99
Author: Bill Erickson <berick at esilibrary.com>
Date:   Wed Jul 6 15:21:58 2011 -0400

    Vandelay matching, quality, error reporting, and queue export additions
    
     * Configuration of direct MARC field comparisons of incoming record to existing records
     * Support for nested boolean MARC comparison expressions: e.g. 100a and (020a or 024a)
     * Support for field-level match scores to create an overall per-record match score
     * Support for best match merge/overlay
     * Support for field-level quality metrics to determine overall record quality
     * Support for enforcing a minimum quality ratio on import to prevent import of lower quality records
     * Support for capturing and reporting record and item import failures.
     * Support for exporting record queues and items as print, CSV, and email
     * Various small display fixes and cleanup
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>


commit 2fc6c995cd469353f494e336b8db3b677aa3d25e
Author: Bill Erickson <berick at esilibrary.com>
Date:   Wed Jul 6 15:18:48 2011 -0400

    Stamped DB version
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index d9ad349..6276a29 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -86,7 +86,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0571', :eg_version); -- miker
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0572', :eg_version); -- berick
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql b/Open-ILS/src/sql/Pg/upgrade/0572.schema.vandelay-record-matching-and-quality.sql
similarity index 99%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
rename to Open-ILS/src/sql/Pg/upgrade/0572.schema.vandelay-record-matching-and-quality.sql
index 34a0b90..3b986b0 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0572.schema.vandelay-record-matching-and-quality.sql
@@ -1,12 +1,10 @@
--- Evergreen DB patch XXXX.vandelay-record-matching-and-quality.sql
---
--- FIXME: insert description of change, if needed
+-- Evergreen DB patch 0572.vandelay-record-matching-and-quality.sql
 --
 BEGIN;
 
 
 -- check whether patch can be applied
---SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('0572', :eg_version);
 
 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
 

commit 11218af3531a20925b6a9fdc5aa0eb97777f15a7
Author: Bill Erickson <berick at squeeze.localnet>
Date:   Wed Jul 6 12:52:04 2011 -0400

    Seed data and schema cleanup for merge
    
     * Updated A/T event-def ID in seed data to match master
     * Move schema elements into the correct order in the baseline files (mike)
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Bill Erickson <berick at esilibrary.com>

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 56edddd..8095e3b 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -153,7 +153,7 @@ CREATE TABLE vandelay.import_item (
     definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	import_error	TEXT        REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	error_detail	TEXT,
-    imported_as     BIGINT      REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
+    imported_as     BIGINT,
     import_time	    TIMESTAMP WITH TIME ZONE,
     owning_lib      INT,
     circ_lib        INT,
@@ -1423,269 +1423,6 @@ CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue ( queue_id BIGINT ) R
     SELECT * FROM vandelay.auto_overlay_bib_queue( $1, NULL );
 $$ LANGUAGE SQL;
 
-CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
-DECLARE
-
-    owning_lib      TEXT;
-    circ_lib        TEXT;
-    call_number     TEXT;
-    copy_number     TEXT;
-    status          TEXT;
-    location        TEXT;
-    circulate       TEXT;
-    deposit         TEXT;
-    deposit_amount  TEXT;
-    ref             TEXT;
-    holdable        TEXT;
-    price           TEXT;
-    barcode         TEXT;
-    circ_modifier   TEXT;
-    circ_as_type    TEXT;
-    alert_message   TEXT;
-    opac_visible    TEXT;
-    pub_note        TEXT;
-    priv_note       TEXT;
-
-    attr_def        RECORD;
-    tmp_attr_set    RECORD;
-    attr_set        vandelay.import_item%ROWTYPE;
-
-    xpath           TEXT;
-
-BEGIN
-
-    SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
-
-    IF FOUND THEN
-
-        attr_set.definition := attr_def.id; 
-    
-        -- Build the combined XPath
-    
-        owning_lib :=
-            CASE
-                WHEN attr_def.owning_lib IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
-            END;
-    
-        circ_lib :=
-            CASE
-                WHEN attr_def.circ_lib IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
-            END;
-    
-        call_number :=
-            CASE
-                WHEN attr_def.call_number IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
-            END;
-    
-        copy_number :=
-            CASE
-                WHEN attr_def.copy_number IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
-            END;
-    
-        status :=
-            CASE
-                WHEN attr_def.status IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
-            END;
-    
-        location :=
-            CASE
-                WHEN attr_def.location IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
-            END;
-    
-        circulate :=
-            CASE
-                WHEN attr_def.circulate IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
-            END;
-    
-        deposit :=
-            CASE
-                WHEN attr_def.deposit IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
-            END;
-    
-        deposit_amount :=
-            CASE
-                WHEN attr_def.deposit_amount IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
-            END;
-    
-        ref :=
-            CASE
-                WHEN attr_def.ref IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
-            END;
-    
-        holdable :=
-            CASE
-                WHEN attr_def.holdable IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
-            END;
-    
-        price :=
-            CASE
-                WHEN attr_def.price IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
-            END;
-    
-        barcode :=
-            CASE
-                WHEN attr_def.barcode IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
-            END;
-    
-        circ_modifier :=
-            CASE
-                WHEN attr_def.circ_modifier IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
-            END;
-    
-        circ_as_type :=
-            CASE
-                WHEN attr_def.circ_as_type IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
-            END;
-    
-        alert_message :=
-            CASE
-                WHEN attr_def.alert_message IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
-            END;
-    
-        opac_visible :=
-            CASE
-                WHEN attr_def.opac_visible IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
-            END;
-
-        pub_note :=
-            CASE
-                WHEN attr_def.pub_note IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
-            END;
-        priv_note :=
-            CASE
-                WHEN attr_def.priv_note IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
-            END;
-    
-    
-        xpath := 
-            owning_lib      || '|' || 
-            circ_lib        || '|' || 
-            call_number     || '|' || 
-            copy_number     || '|' || 
-            status          || '|' || 
-            location        || '|' || 
-            circulate       || '|' || 
-            deposit         || '|' || 
-            deposit_amount  || '|' || 
-            ref             || '|' || 
-            holdable        || '|' || 
-            price           || '|' || 
-            barcode         || '|' || 
-            circ_modifier   || '|' || 
-            circ_as_type    || '|' || 
-            alert_message   || '|' || 
-            pub_note        || '|' || 
-            priv_note       || '|' || 
-            opac_visible;
-
-        -- RAISE NOTICE 'XPath: %', xpath;
-        
-        FOR tmp_attr_set IN
-                SELECT  *
-                  FROM  oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
-                            AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
-                                  dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
-                                  circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
-        LOOP
-    
-            tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
-            tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
-
-            tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
-            tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
-    
-            SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
-            SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
-            SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
-    
-            SELECT  id INTO attr_set.location
-              FROM  asset.copy_location
-              WHERE LOWER(name) = LOWER(tmp_attr_set.cl)
-                    AND asset.copy_location.owning_lib = COALESCE(attr_set.owning_lib, attr_set.circ_lib); -- INT
-    
-            attr_set.circulate      :=
-                LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
-                OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
-
-            attr_set.deposit        :=
-                LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
-                OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
-
-            attr_set.holdable       :=
-                LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
-                OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
-
-            attr_set.opac_visible   :=
-                LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
-                OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
-
-            attr_set.ref            :=
-                LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
-                OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
-    
-            attr_set.copy_number    := tmp_attr_set.cnum::INT; -- INT,
-            attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
-            attr_set.price          := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
-    
-            attr_set.call_number    := tmp_attr_set.cn; -- TEXT
-            attr_set.barcode        := tmp_attr_set.bc; -- TEXT,
-            attr_set.circ_modifier  := tmp_attr_set.circ_mod; -- TEXT,
-            attr_set.circ_as_type   := tmp_attr_set.circ_as; -- TEXT,
-            attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
-            attr_set.pub_note       := tmp_attr_set.note; -- TEXT,
-            attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
-            attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
-    
-            RETURN NEXT attr_set;
-    
-        END LOOP;
-    
-    END IF;
-
-    RETURN;
-
-END;
-$$ LANGUAGE PLPGSQL;
-
-
 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
 DECLARE
     value   TEXT;
@@ -1709,70 +1446,6 @@ BEGIN
 END;
 $$ LANGUAGE PLPGSQL;
 
-CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
-DECLARE
-    attr_def    BIGINT;
-    item_data   vandelay.import_item%ROWTYPE;
-BEGIN
-
-    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
-        RETURN NEW;
-    END IF;
-
-    SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
-
-    FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
-        INSERT INTO vandelay.import_item (
-            record,
-            definition,
-            owning_lib,
-            circ_lib,
-            call_number,
-            copy_number,
-            status,
-            location,
-            circulate,
-            deposit,
-            deposit_amount,
-            ref,
-            holdable,
-            price,
-            barcode,
-            circ_modifier,
-            circ_as_type,
-            alert_message,
-            pub_note,
-            priv_note,
-            opac_visible
-        ) VALUES (
-            NEW.id,
-            item_data.definition,
-            item_data.owning_lib,
-            item_data.circ_lib,
-            item_data.call_number,
-            item_data.copy_number,
-            item_data.status,
-            item_data.location,
-            item_data.circulate,
-            item_data.deposit,
-            item_data.deposit_amount,
-            item_data.ref,
-            item_data.holdable,
-            item_data.price,
-            item_data.barcode,
-            item_data.circ_modifier,
-            item_data.circ_as_type,
-            item_data.alert_message,
-            item_data.pub_note,
-            item_data.priv_note,
-            item_data.opac_visible
-        );
-    END LOOP;
-
-    RETURN NULL;
-END;
-$func$ LANGUAGE PLPGSQL;
-
 CREATE OR REPLACE FUNCTION vandelay.match_bib_record ( ) RETURNS TRIGGER AS $func$
 DECLARE
     attr        RECORD;
@@ -1877,10 +1550,6 @@ CREATE TRIGGER ingest_bib_trigger
     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_marc();
 
-CREATE TRIGGER ingest_item_trigger
-    AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
-    FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_items();
-
 CREATE TRIGGER zz_match_bibs_trigger
     BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
     FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
diff --git a/Open-ILS/src/sql/Pg/030.schema.metabib.sql b/Open-ILS/src/sql/Pg/030.schema.metabib.sql
index 9f00d0c..b70fae4 100644
--- a/Open-ILS/src/sql/Pg/030.schema.metabib.sql
+++ b/Open-ILS/src/sql/Pg/030.schema.metabib.sql
@@ -595,7 +595,7 @@ CREATE OR REPLACE FUNCTION biblio.marc21_extract_fixed_field( rid BIGINT, ff TEX
     SELECT * FROM vandelay.marc21_extract_fixed_field( (SELECT marc FROM biblio.record_entry WHERE id = $1), $2 );
 $func$ LANGUAGE SQL;
 
-CREATE TYPE biblio.record_ff_map AS (record BIGINT, ff_name TEXT, ff_value TEXT);
+-- CREATE TYPE biblio.record_ff_map AS (record BIGINT, ff_name TEXT, ff_value TEXT);
 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
 DECLARE
     tag_data    TEXT;
diff --git a/Open-ILS/src/sql/Pg/800.fkeys.sql b/Open-ILS/src/sql/Pg/800.fkeys.sql
index d0ee887..ad05dd7 100644
--- a/Open-ILS/src/sql/Pg/800.fkeys.sql
+++ b/Open-ILS/src/sql/Pg/800.fkeys.sql
@@ -82,6 +82,8 @@ ALTER TABLE serial.unit ADD CONSTRAINT serial_unit_call_number_fkey FOREIGN KEY
 ALTER TABLE serial.unit ADD CONSTRAINT serial_unit_creator_fkey FOREIGN KEY (creator) REFERENCES actor.usr (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE serial.unit ADD CONSTRAINT serial_unit_editor_fkey FOREIGN KEY (editor) REFERENCES actor.usr (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 
+ALTER TABLE vandelay.import_item ADD CONSTRAINT imported_as_fkey FOREIGN KEY (imported_as) REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED;
+
 ALTER TABLE asset.copy_note ADD CONSTRAINT asset_copy_note_copy_fkey FOREIGN KEY (owning_copy) REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE asset.copy_note ADD CONSTRAINT asset_copy_note_creator_fkey FOREIGN KEY (creator) REFERENCES actor.usr (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 
diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index fe7618d..c7372ed 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -8816,13 +8816,7 @@ INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
     ),
     'bool'
 );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
+
 ----------------------------------------------------------------
 -- Seed data for queued record/item exports
 ----------------------------------------------------------------
@@ -8940,7 +8934,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        38,
+        39,
         TRUE,
         1,
         'Print Output for Queued Bib Records',
@@ -8982,8 +8976,8 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    38, 'attributes')
-    ,( 38, 'queue')
+    39, 'attributes')
+    ,( 39, 'queue')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -8998,7 +8992,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        39,
+        40,
         TRUE,
         1,
         'CSV Output for Queued Bib Records',
@@ -9017,8 +9011,8 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    39, 'attributes')
-    ,( 39, 'queue')
+    40, 'attributes')
+    ,( 40, 'queue')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -9033,7 +9027,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        40,
+        41,
         TRUE,
         1,
         'Email Output for Queued Bib Records',
@@ -9079,9 +9073,9 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    40, 'attributes')
-    ,( 40, 'queue')
-    ,( 40, 'queue.owner')
+    41, 'attributes')
+    ,( 41, 'queue')
+    ,( 41, 'queue.owner')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -9096,7 +9090,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        41,
+        42,
         TRUE,
         1,
         'Print Output for Queued Authority Records',
@@ -9124,8 +9118,8 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    41, 'attributes')
-    ,( 41, 'queue')
+    42, 'attributes')
+    ,( 42, 'queue')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -9140,7 +9134,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        42,
+        43,
         TRUE,
         1,
         'CSV Output for Queued Authority Records',
@@ -9159,8 +9153,8 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    42, 'attributes')
-    ,( 42, 'queue')
+    43, 'attributes')
+    ,( 43, 'queue')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -9175,7 +9169,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        43,
+        44,
         TRUE,
         1,
         'Email Output for Queued Authority Records',
@@ -9207,9 +9201,9 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    43, 'attributes')
-    ,( 43, 'queue')
-    ,( 43, 'queue.owner')
+    44, 'attributes')
+    ,( 44, 'queue')
+    ,( 44, 'queue.owner')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -9224,7 +9218,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        44,
+        45,
         TRUE,
         1,
         'Print Output for Import Items from Queued Bib Records',
@@ -9276,10 +9270,10 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    44, 'record')
-    ,( 44, 'record.attributes')
-    ,( 44, 'record.queue')
-    ,( 44, 'record.queue.owner')
+    45, 'record')
+    ,( 45, 'record.attributes')
+    ,( 45, 'record.queue')
+    ,( 45, 'record.queue.owner')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -9294,7 +9288,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        45,
+        46,
         TRUE,
         1,
         'CSV Output for Import Items from Queued Bib Records',
@@ -9313,10 +9307,10 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    45, 'record')
-    ,( 45, 'record.attributes')
-    ,( 45, 'record.queue')
-    ,( 45, 'record.queue.owner')
+    46, 'record')
+    ,( 46, 'record.attributes')
+    ,( 46, 'record.queue')
+    ,( 46, 'record.queue.owner')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -9331,7 +9325,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        46,
+        47,
         TRUE,
         1,
         'Email Output for Import Items from Queued Bib Records',
@@ -9386,10 +9380,10 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    46, 'record')
-    ,( 46, 'record.attributes')
-    ,( 46, 'record.queue')
-    ,( 46, 'record.queue.owner')
+    47, 'record')
+    ,( 47, 'record.attributes')
+    ,( 47, 'record.queue')
+    ,( 47, 'record.queue.owner')
 ;
 
 
diff --git a/Open-ILS/src/sql/Pg/999.functions.global.sql b/Open-ILS/src/sql/Pg/999.functions.global.sql
index 0152417..0faeea6 100644
--- a/Open-ILS/src/sql/Pg/999.functions.global.sql
+++ b/Open-ILS/src/sql/Pg/999.functions.global.sql
@@ -1457,3 +1457,334 @@ BEGIN
 	RETURN config.interval_to_seconds( interval_string::INTERVAL );
 END;
 $$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
+DECLARE
+
+    owning_lib      TEXT;
+    circ_lib        TEXT;
+    call_number     TEXT;
+    copy_number     TEXT;
+    status          TEXT;
+    location        TEXT;
+    circulate       TEXT;
+    deposit         TEXT;
+    deposit_amount  TEXT;
+    ref             TEXT;
+    holdable        TEXT;
+    price           TEXT;
+    barcode         TEXT;
+    circ_modifier   TEXT;
+    circ_as_type    TEXT;
+    alert_message   TEXT;
+    opac_visible    TEXT;
+    pub_note        TEXT;
+    priv_note       TEXT;
+
+    attr_def        RECORD;
+    tmp_attr_set    RECORD;
+    attr_set        vandelay.import_item%ROWTYPE;
+
+    xpath           TEXT;
+
+BEGIN
+
+    SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
+
+    IF FOUND THEN
+
+        attr_set.definition := attr_def.id;
+
+        -- Build the combined XPath
+
+        owning_lib :=
+            CASE
+                WHEN attr_def.owning_lib IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
+            END;
+
+        circ_lib :=
+            CASE
+                WHEN attr_def.circ_lib IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
+            END;
+
+        call_number :=
+            CASE
+                WHEN attr_def.call_number IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
+            END;
+
+        copy_number :=
+            CASE
+                WHEN attr_def.copy_number IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
+            END;
+
+        status :=
+            CASE
+                WHEN attr_def.status IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
+            END;
+
+        location :=
+            CASE
+                WHEN attr_def.location IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
+            END;
+
+        circulate :=
+            CASE
+                WHEN attr_def.circulate IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
+            END;
+
+        deposit :=
+            CASE
+                WHEN attr_def.deposit IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
+            END;
+
+        deposit_amount :=
+            CASE
+                WHEN attr_def.deposit_amount IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
+            END;
+
+        ref :=
+            CASE
+                WHEN attr_def.ref IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
+            END;
+
+        holdable :=
+            CASE
+                WHEN attr_def.holdable IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
+            END;
+
+        price :=
+            CASE
+                WHEN attr_def.price IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
+            END;
+
+        barcode :=
+            CASE
+                WHEN attr_def.barcode IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
+            END;
+
+        circ_modifier :=
+            CASE
+                WHEN attr_def.circ_modifier IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
+            END;
+
+        circ_as_type :=
+            CASE
+                WHEN attr_def.circ_as_type IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
+            END;
+
+        alert_message :=
+            CASE
+                WHEN attr_def.alert_message IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
+            END;
+
+        opac_visible :=
+            CASE
+                WHEN attr_def.opac_visible IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
+            END;
+
+        pub_note :=
+            CASE
+                WHEN attr_def.pub_note IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
+            END;
+        priv_note :=
+            CASE
+                WHEN attr_def.priv_note IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
+            END;
+
+
+        xpath :=
+            owning_lib      || '|' ||
+            circ_lib        || '|' ||
+            call_number     || '|' ||
+            copy_number     || '|' ||
+            status          || '|' ||
+            location        || '|' ||
+            circulate       || '|' ||
+            deposit         || '|' ||
+            deposit_amount  || '|' ||
+            ref             || '|' ||
+            holdable        || '|' ||
+            price           || '|' ||
+            barcode         || '|' ||
+            circ_modifier   || '|' ||
+            circ_as_type    || '|' ||
+            alert_message   || '|' ||
+            pub_note        || '|' ||
+            priv_note       || '|' ||
+            opac_visible;
+
+        -- RAISE NOTICE 'XPath: %', xpath;
+
+        FOR tmp_attr_set IN
+                SELECT  *
+                  FROM  oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
+                            AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
+                                  dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
+                                  circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
+        LOOP
+
+            tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
+            tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
+
+            tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
+            tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
+
+            SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
+            SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
+            SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
+
+            SELECT  id INTO attr_set.location
+              FROM  asset.copy_location
+              WHERE LOWER(name) = LOWER(tmp_attr_set.cl)
+                    AND asset.copy_location.owning_lib = COALESCE(attr_set.owning_lib, attr_set.circ_lib); -- INT
+
+            attr_set.circulate      :=
+                LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
+
+            attr_set.deposit        :=
+                LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
+
+            attr_set.holdable       :=
+                LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
+
+            attr_set.opac_visible   :=
+                LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
+
+            attr_set.ref            :=
+                LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
+
+            attr_set.copy_number    := tmp_attr_set.cnum::INT; -- INT,
+            attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
+            attr_set.price          := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
+
+            attr_set.call_number    := tmp_attr_set.cn; -- TEXT
+            attr_set.barcode        := tmp_attr_set.bc; -- TEXT,
+            attr_set.circ_modifier  := tmp_attr_set.circ_mod; -- TEXT,
+            attr_set.circ_as_type   := tmp_attr_set.circ_as; -- TEXT,
+            attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
+            attr_set.pub_note       := tmp_attr_set.note; -- TEXT,
+            attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
+            attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
+
+            RETURN NEXT attr_set;
+
+        END LOOP;
+
+    END IF;
+
+    RETURN;
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
+DECLARE
+    attr_def    BIGINT;
+    item_data   vandelay.import_item%ROWTYPE;
+BEGIN
+
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
+
+    FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
+        INSERT INTO vandelay.import_item (
+            record,
+            definition,
+            owning_lib,
+            circ_lib,
+            call_number,
+            copy_number,
+            status,
+            location,
+            circulate,
+            deposit,
+            deposit_amount,
+            ref,
+            holdable,
+            price,
+            barcode,
+            circ_modifier,
+            circ_as_type,
+            alert_message,
+            pub_note,
+            priv_note,
+            opac_visible
+        ) VALUES (
+            NEW.id,
+            item_data.definition,
+            item_data.owning_lib,
+            item_data.circ_lib,
+            item_data.call_number,
+            item_data.copy_number,
+            item_data.status,
+            item_data.location,
+            item_data.circulate,
+            item_data.deposit,
+            item_data.deposit_amount,
+            item_data.ref,
+            item_data.holdable,
+            item_data.price,
+            item_data.barcode,
+            item_data.circ_modifier,
+            item_data.circ_as_type,
+            item_data.alert_message,
+            item_data.pub_note,
+            item_data.priv_note,
+            item_data.opac_visible
+        );
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER ingest_item_trigger
+    AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
+    FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_items();
+
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
index 5093d5f..34a0b90 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
@@ -1140,7 +1140,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        38,
+        39,
         TRUE,
         1,
         'Print Output for Queued Bib Records',
@@ -1182,8 +1182,8 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    38, 'attributes')
-    ,( 38, 'queue')
+    39, 'attributes')
+    ,( 39, 'queue')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -1198,7 +1198,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        39,
+        40,
         TRUE,
         1,
         'CSV Output for Queued Bib Records',
@@ -1217,8 +1217,8 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    39, 'attributes')
-    ,( 39, 'queue')
+    40, 'attributes')
+    ,( 40, 'queue')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -1233,7 +1233,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        40,
+        41,
         TRUE,
         1,
         'Email Output for Queued Bib Records',
@@ -1279,9 +1279,9 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    40, 'attributes')
-    ,( 40, 'queue')
-    ,( 40, 'queue.owner')
+    41, 'attributes')
+    ,( 41, 'queue')
+    ,( 41, 'queue.owner')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -1296,7 +1296,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        41,
+        42,
         TRUE,
         1,
         'Print Output for Queued Authority Records',
@@ -1324,8 +1324,8 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    41, 'attributes')
-    ,( 41, 'queue')
+    42, 'attributes')
+    ,( 42, 'queue')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -1340,7 +1340,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        42,
+        43,
         TRUE,
         1,
         'CSV Output for Queued Authority Records',
@@ -1359,8 +1359,8 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    42, 'attributes')
-    ,( 42, 'queue')
+    43, 'attributes')
+    ,( 43, 'queue')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -1375,7 +1375,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        43,
+        44,
         TRUE,
         1,
         'Email Output for Queued Authority Records',
@@ -1407,9 +1407,9 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    43, 'attributes')
-    ,( 43, 'queue')
-    ,( 43, 'queue.owner')
+    44, 'attributes')
+    ,( 44, 'queue')
+    ,( 44, 'queue.owner')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -1424,7 +1424,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        44,
+        45,
         TRUE,
         1,
         'Print Output for Import Items from Queued Bib Records',
@@ -1476,10 +1476,10 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    44, 'record')
-    ,( 44, 'record.attributes')
-    ,( 44, 'record.queue')
-    ,( 44, 'record.queue.owner')
+    45, 'record')
+    ,( 45, 'record.attributes')
+    ,( 45, 'record.queue')
+    ,( 45, 'record.queue.owner')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -1494,7 +1494,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        45,
+        46,
         TRUE,
         1,
         'CSV Output for Import Items from Queued Bib Records',
@@ -1513,10 +1513,10 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    45, 'record')
-    ,( 45, 'record.attributes')
-    ,( 45, 'record.queue')
-    ,( 45, 'record.queue.owner')
+    46, 'record')
+    ,( 46, 'record.attributes')
+    ,( 46, 'record.queue')
+    ,( 46, 'record.queue.owner')
 ;
 
 INSERT INTO action_trigger.event_definition (
@@ -1531,7 +1531,7 @@ INSERT INTO action_trigger.event_definition (
         granularity,
         template
     ) VALUES (
-        46,
+        47,
         TRUE,
         1,
         'Email Output for Import Items from Queued Bib Records',
@@ -1586,10 +1586,10 @@ $$
 ;
 
 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    46, 'record')
-    ,( 46, 'record.attributes')
-    ,( 46, 'record.queue')
-    ,( 46, 'record.queue.owner')
+    47, 'record')
+    ,( 47, 'record.attributes')
+    ,( 47, 'record.queue')
+    ,( 47, 'record.queue.owner')
 ;
 
 

commit aa2579147103fef0d17f249336c088d041b5b403
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue May 31 15:24:09 2011 -0400

    Logic error fixes in Vandelay
    
    * When counting import failures, also count records that have no import
    items attached
    
    * Fixed problem w/ not setting import time/as on newly imported records
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index b3e516b..c2811d1 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -489,7 +489,7 @@ sub retrieve_queued_records {
 
     if($$options{with_import_error}) {
 
-        $query->{from} = {$class => {vii => {type => 'right'}}};
+        $query->{from} = {$class => {vii => {type => 'left'}}};
         $query->{where}->{'-or'} = [
             {'+vqbr' => {import_error => {'!=' => undef}}},
             {'+vii' => {import_error => {'!=' => undef}}}
@@ -1121,6 +1121,7 @@ sub import_record_list_impl {
 
                     $logger->info("vl: successfully imported new $type record");
                     $rec->imported_as($record->id);
+                    $imported = 1;
                 }
             }
         }

commit 05df9c099d497a017a03f37f5116750691d01087
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue May 31 12:16:16 2011 -0400

    Repaired typo on schema and upgrade SQL
    
    ARRAY_ACUM is not a defined function.  Per Dan Scott, replaced with
    ARRAY_AGG instead of the correctly spelled ARRAY_ACCUM, since we'll be
    using that function in place of ARRAY_ACCUM going forward.
    
    Thanks, Dan.
    
    Minor syntax/cleanup repairs
    
    Remove tmp seed data file.  T'was replaced with upgrade script
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index 80b7a65..d9ad349 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -882,8 +882,8 @@ Upgrade script % can not be applied:
   deprecated by %
   superseded by %',
             my_db_patch,
-            deprecates,
-            supersedes,
+            ARRAY_AGG(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
+            ARRAY_AGG(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
             evergreen.upgrade_list_applied_deprecated(my_db_patch),
             evergreen.upgrade_list_applied_superseded(my_db_patch);
     END IF;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0526.schema.upgrade-dep-tracking.sql b/Open-ILS/src/sql/Pg/upgrade/0526.schema.upgrade-dep-tracking.sql
index 86b381d..16ff50d 100644
--- a/Open-ILS/src/sql/Pg/upgrade/0526.schema.upgrade-dep-tracking.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0526.schema.upgrade-dep-tracking.sql
@@ -101,8 +101,8 @@ Upgrade script % can not be applied:
   deprecated by %
   superseded by %',
             my_db_patch,
-            deprecates,
-            supersedes,
+            ARRAY_AGG(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
+            ARRAY_AGG(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
             evergreen.upgrade_list_applied_deprecated(my_db_patch),
             evergreen.upgrade_list_applied_superseded(my_db_patch);
     END IF;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
index 56154df..5093d5f 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
@@ -6,7 +6,7 @@ BEGIN;
 
 
 -- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+--SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
 
 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
 
@@ -53,7 +53,7 @@ CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_s
 
 -- ALTER TABLEs...
 ALTER TABLE vandelay.queue ADD COLUMN match_set INT REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE vandelay.queue_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
+ALTER TABLE vandelay.queued_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
 ALTER TABLE vandelay.bib_attr_definition DROP COLUMN ident;
 
 CREATE TABLE vandelay.import_error (
@@ -144,7 +144,6 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
-CREATE TYPE biblio.record_ff_map AS (record BIGINT, ff_name TEXT, ff_value TEXT);
 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
 DECLARE
     tag_data    TEXT;
@@ -180,7 +179,6 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
-CREATE TYPE biblio.marc21_physical_characteristics AS ( id INT, record BIGINT, ptype TEXT, subfield INT, value INT );
 CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
 DECLARE
     rowid   INT := 0;
diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
deleted file mode 100644
index 5396747..0000000
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ /dev/null
@@ -1,572 +0,0 @@
-BEGIN;
-
-INSERT INTO config.upgrade_log (version) VALUES ('test'); -- phasefx
-
-INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
-        'vandelay.queued_bib_record.print',
-        'vqbr', 
-        oils_i18n_gettext(
-            'vandelay.queued_bib_record.print',
-            'Print output has been requested for records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_bib_record.csv',
-        'vqbr', 
-        oils_i18n_gettext(
-            'vandelay.queued_bib_record.csv',
-            'CSV output has been requested for records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_bib_record.email',
-        'vqbr', 
-        oils_i18n_gettext(
-            'vandelay.queued_bib_record.email',
-            'An email has been requested for records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_auth_record.print',
-        'vqar', 
-        oils_i18n_gettext(
-            'vandelay.queued_auth_record.print',
-            'Print output has been requested for records in an Importer Authority Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_auth_record.csv',
-        'vqar', 
-        oils_i18n_gettext(
-            'vandelay.queued_auth_record.csv',
-            'CSV output has been requested for records in an Importer Authority Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_auth_record.email',
-        'vqar', 
-        oils_i18n_gettext(
-            'vandelay.queued_auth_record.email',
-            'An email has been requested for records in an Importer Authority Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.import_items.print',
-        'vii', 
-        oils_i18n_gettext(
-            'vandelay.import_items.print',
-            'Print output has been requested for Import Items from records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.import_items.csv',
-        'vii', 
-        oils_i18n_gettext(
-            'vandelay.import_items.csv',
-            'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.import_items.email',
-        'vii', 
-        oils_i18n_gettext(
-            'vandelay.import_items.email',
-            'An email has been requested for Import Items from records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        38,
-        TRUE,
-        1,
-        'Print Output for Queued Bib Records',
-        'vandelay.queued_bib_record.print',
-        'NOOP_True',
-        'ProcessTemplate',
-        'queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-<pre>
-Queue ID: [% target.0.queue.id %]
-Queue Name: [% target.0.queue.name %]
-Queue Type: [% target.0.queue.queue_type %]
-Complete? [% target.0.queue.complete %]
-
-    [% FOR vqbr IN target %]
-=-=-=
- Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
- Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
- Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
- Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
- ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
- ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
- Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
- Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
- TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
- TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
- Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
- Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
- Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
- Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
- Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
-
-    [% END %]
-</pre>
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    38, 'attributes')
-    ,( 38, 'queue')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        39,
-        TRUE,
-        1,
-        'CSV Output for Queued Bib Records',
-        'vandelay.queued_bib_record.csv',
-        'NOOP_True',
-        'ProcessTemplate',
-        'queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-"Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
-[% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_att
 r('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
-[% END %]
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    39, 'attributes')
-    ,( 39, 'queue')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        40,
-        TRUE,
-        1,
-        'Email Output for Queued Bib Records',
-        'vandelay.queued_bib_record.email',
-        'NOOP_True',
-        'SendEmail',
-        'queue.owner',
-        NULL,
-$$
-[%- USE date -%]
-[%- SET user = target.0.queue.owner -%]
-To: [%- params.recipient_email || user.email || 'root at localhost' %]
-From: [%- params.sender_email || default_sender %]
-Subject: Bibs from Import Queue
-
-Queue ID: [% target.0.queue.id %]
-Queue Name: [% target.0.queue.name %]
-Queue Type: [% target.0.queue.queue_type %]
-Complete? [% target.0.queue.complete %]
-
-    [% FOR vqbr IN target %]
-=-=-=
- Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
- Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
- Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
- Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
- ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
- ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
- Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
- Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
- TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
- TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
- Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
- Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
- Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
- Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
- Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
-
-    [% END %]
-
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    40, 'attributes')
-    ,( 40, 'queue')
-    ,( 40, 'queue.owner')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        41,
-        TRUE,
-        1,
-        'Print Output for Queued Authority Records',
-        'vandelay.queued_auth_record.print',
-        'NOOP_True',
-        'ProcessTemplate',
-        'queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-<pre>
-Queue ID: [% target.0.queue.id %]
-Queue Name: [% target.0.queue.name %]
-Queue Type: [% target.0.queue.queue_type %]
-Complete? [% target.0.queue.complete %]
-
-    [% FOR vqar IN target %]
-=-=-=
- Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
-
-    [% END %]
-</pre>
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    41, 'attributes')
-    ,( 41, 'queue')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        42,
-        TRUE,
-        1,
-        'CSV Output for Queued Authority Records',
-        'vandelay.queued_auth_record.csv',
-        'NOOP_True',
-        'ProcessTemplate',
-        'queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-"Record Identifier"
-[% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
-[% END %]
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    42, 'attributes')
-    ,( 42, 'queue')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        43,
-        TRUE,
-        1,
-        'Email Output for Queued Authority Records',
-        'vandelay.queued_auth_record.email',
-        'NOOP_True',
-        'SendEmail',
-        'queue.owner',
-        NULL,
-$$
-[%- USE date -%]
-[%- SET user = target.0.queue.owner -%]
-To: [%- params.recipient_email || user.email || 'root at localhost' %]
-From: [%- params.sender_email || default_sender %]
-Subject: Authorities from Import Queue
-
-Queue ID: [% target.0.queue.id %]
-Queue Name: [% target.0.queue.name %]
-Queue Type: [% target.0.queue.queue_type %]
-Complete? [% target.0.queue.complete %]
-
-    [% FOR vqar IN target %]
-=-=-=
- Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
-
-    [% END %]
-
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    43, 'attributes')
-    ,( 43, 'queue')
-    ,( 43, 'queue.owner')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        44,
-        TRUE,
-        1,
-        'Print Output for Import Items from Queued Bib Records',
-        'vandelay.import_items.print',
-        'NOOP_True',
-        'ProcessTemplate',
-        'record.queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-<pre>
-Queue ID: [% target.0.record.queue.id %]
-Queue Name: [% target.0.record.queue.name %]
-Queue Type: [% target.0.record.queue.queue_type %]
-Complete? [% target.0.record.queue.complete %]
-
-    [% FOR vii IN target %]
-=-=-=
- Import Item ID         | [% vii.id %]
- Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
- ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
- Attribute Definition   | [% vii.definition %]
- Import Error           | [% vii.import_error %]
- Import Error Detail    | [% vii.error_detail %]
- Owning Library         | [% vii.owning_lib %]
- Circulating Library    | [% vii.circ_lib %]
- Call Number            | [% vii.call_number %]
- Copy Number            | [% vii.copy_number %]
- Status                 | [% vii.status.name %]
- Shelving Location      | [% vii.location.name %]
- Circulate              | [% vii.circulate %]
- Deposit                | [% vii.deposit %]
- Deposit Amount         | [% vii.deposit_amount %]
- Reference              | [% vii.ref %]
- Holdable               | [% vii.holdable %]
- Price                  | [% vii.price %]
- Barcode                | [% vii.barcode %]
- Circulation Modifier   | [% vii.circ_modifier %]
- Circulate As MARC Type | [% vii.circ_as_type %]
- Alert Message          | [% vii.alert_message %]
- Public Note            | [% vii.pub_note %]
- Private Note           | [% vii.priv_note %]
- OPAC Visible           | [% vii.opac_visible %]
-
-    [% END %]
-</pre>
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    44, 'record')
-    ,( 44, 'record.attributes')
-    ,( 44, 'record.queue')
-    ,( 44, 'record.queue.owner')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        45,
-        TRUE,
-        1,
-        'CSV Output for Import Items from Queued Bib Records',
-        'vandelay.import_items.csv',
-        'NOOP_True',
-        'ProcessTemplate',
-        'record.queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-"Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
-[% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_
 as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
-[% END %]
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    45, 'record')
-    ,( 45, 'record.attributes')
-    ,( 45, 'record.queue')
-    ,( 45, 'record.queue.owner')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        46,
-        TRUE,
-        1,
-        'Email Output for Import Items from Queued Bib Records',
-        'vandelay.import_items.email',
-        'NOOP_True',
-        'SendEmail',
-        'record.queue.owner',
-        NULL,
-$$
-[%- USE date -%]
-[%- SET user = target.0.record.queue.owner -%]
-To: [%- params.recipient_email || user.email || 'root at localhost' %]
-From: [%- params.sender_email || default_sender %]
-Subject: Import Items from Import Queue
-
-Queue ID: [% target.0.record.queue.id %]
-Queue Name: [% target.0.record.queue.name %]
-Queue Type: [% target.0.record.queue.queue_type %]
-Complete? [% target.0.record.queue.complete %]
-
-    [% FOR vii IN target %]
-=-=-=
- Import Item ID         | [% vii.id %]
- Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
- ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
- Attribute Definition   | [% vii.definition %]
- Import Error           | [% vii.import_error %]
- Import Error Detail    | [% vii.error_detail %]
- Owning Library         | [% vii.owning_lib %]
- Circulating Library    | [% vii.circ_lib %]
- Call Number            | [% vii.call_number %]
- Copy Number            | [% vii.copy_number %]
- Status                 | [% vii.status.name %]
- Shelving Location      | [% vii.location.name %]
- Circulate              | [% vii.circulate %]
- Deposit                | [% vii.deposit %]
- Deposit Amount         | [% vii.deposit_amount %]
- Reference              | [% vii.ref %]
- Holdable               | [% vii.holdable %]
- Price                  | [% vii.price %]
- Barcode                | [% vii.barcode %]
- Circulation Modifier   | [% vii.circ_modifier %]
- Circulate As MARC Type | [% vii.circ_as_type %]
- Alert Message          | [% vii.alert_message %]
- Public Note            | [% vii.pub_note %]
- Private Note           | [% vii.priv_note %]
- OPAC Visible           | [% vii.opac_visible %]
-
-    [% END %]
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    46, 'record')
-    ,( 46, 'record.attributes')
-    ,( 46, 'record.queue')
-    ,( 46, 'record.queue.owner')
-;
-
-COMMIT;
-
--- BEGIN; DELETE FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45,46); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40,41,42,43,44,45,46); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40,41,42,43,44,45,46); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45,46))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45,46))); DELETE FROM config.upgrade_log WHERE version = 'test'; COMMIT;

commit 53ed31174c82d971e931682d80c54e77b07c15f8
Author: Mike Rylander <mrylander at gmail.com>
Date:   Thu May 26 16:23:18 2011 -0400

    Correct regression that was held over due to massive moving of functions
    
    upgrade script cleanup
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 9a5c3ef..56edddd 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -440,9 +440,10 @@ BEGIN
             attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
 
         ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
-            SELECT  value::TEXT INTO attr_value
-              FROM  vandelay.marc21_physical_characteristics(xml)
-              WHERE subfield = attr_def.phys_char_sf
+            SELECT  m.value::TEXT INTO attr_value
+              FROM  vandelay.marc21_physical_characteristics(xml) v
+                    JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
+              WHERE v.subfield = attr_def.phys_char_sf
               LIMIT 1; -- Just in case ...
 
         END IF;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
index fd0c694..56154df 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
@@ -52,47 +52,32 @@ CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_s
 
 
 -- ALTER TABLEs...
+ALTER TABLE vandelay.queue ADD COLUMN match_set INT REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE vandelay.queue_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
+ALTER TABLE vandelay.bib_attr_definition DROP COLUMN ident;
 
-    match_set       INT         REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-@@ -18,7 +60,8 @@ CREATE TABLE vandelay.queued_record (
--    marc		TEXT                        NOT NULL
-    marc		TEXT                        NOT NULL,
-    quality     INT                         NOT NULL DEFAULT 0
-@@ -31,8 +74,7 @@ CREATE TABLE vandelay.bib_attr_definition (
--	remove		TEXT	NOT NULL DEFAULT '',
--	ident		BOOL	NOT NULL DEFAULT FALSE
-	remove		TEXT	NOT NULL DEFAULT ''
-@@ -67,6 +109,11 @@ CREATE TABLE vandelay.import_item_attr_definition (
 CREATE TABLE vandelay.import_error (
     code        TEXT    PRIMARY KEY,
     description TEXT    NOT NULL -- i18n
 );
 
-@@ -75,9 +122,11 @@ CREATE TABLE vandelay.bib_queue (
--	queue		INT		NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
--	bib_source	INT		REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
--	imported_as	BIGINT	REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
-	queue		    INT		NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	bib_source	    INT		REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
-	imported_as 	BIGINT	REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
-	import_error	TEXT    REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	error_detail	TEXT
-@@ -92,17 +141,20 @@ CREATE INDEX queued_bib_record_attr_record_idx ON vandelay.queued_bib_record_att
--	field_type		TEXT		NOT NULL CHECK (field_type in ('isbn','tcn_value','id')),
--	matched_attr	INT			REFERENCES vandelay.queued_bib_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
--	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
-	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    quality         INT         NOT NULL DEFAULT 1,
-    match_score     INT         NOT NULL DEFAULT 0
---- DROP TABLE vandelay.import_item CASCADE;
-	import_error	TEXT        REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	error_detail	TEXT,
-    imported_as     BIGINT      REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
-    import_time	    TIMESTAMP WITH TIME ZONE,
-@@ -139,10 +191,592 @@ CREATE TABLE vandelay.merge_profile (
-    lwm_ratio       NUMERIC,
+ALTER TABLE vandelay.queued_bib_record
+    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    ADD COLUMN error_detail TEXT;
 
+ALTER TABLE vandelay.bib_match
+    DROP COLUMN field_type,
+    DROP COLUMN matched_attr,
+    ADD COLUMN quality INT NOT NULL DEFAULT 1,
+    ADD COLUMN match_score INT NOT NULL DEFAULT 0;
 
+ALTER TABLE vandelay.import_item
+    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    ADD COLUMN error_detail TEXT,
+    ADD COLUMN imported_as BIGINT REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
+    ADD COLUMN import_time TIMESTAMP WITH TIME ZONE;
+
+ALTER TABLE vandelay.merge_profile ADD COLUMN lwm_ratio NUMERIC;
 
 CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
 DECLARE
@@ -355,9 +340,10 @@ BEGIN
             attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
 
         ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
-            SELECT  value::TEXT INTO attr_value
-              FROM  vandelay.marc21_physical_characteristics(xml)
-              WHERE subfield = attr_def.phys_char_sf
+            SELECT  m.value::TEXT INTO attr_value
+              FROM  vandelay.marc21_physical_characteristics(xml) v
+                    JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
+              WHERE v.subfield = attr_def.phys_char_sf
               LIMIT 1; -- Just in case ...
 
         END IF;
@@ -929,39 +915,56 @@ BEGIN
 END;
 $$ LANGUAGE PLPGSQL;
 
-
 -- ALTER TABLEs...
 
--
--CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
--BEGIN
-@@ -1200,7 +1818,7 @@ CREATE TRIGGER ingest_item_trigger
--    AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
+DROP TRIGGER zz_match_bibs_trigger ON vandelay.queued_bib_record;
+CREATE TRIGGER zz_match_bibs_trigger
     BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
-@@ -1211,8 +1829,7 @@ CREATE TABLE vandelay.authority_attr_definition (
--	remove		TEXT	NOT NULL DEFAULT '',
--	ident		BOOL	NOT NULL DEFAULT FALSE
-	remove		TEXT	NOT NULL DEFAULT ''
-@@ -1223,7 +1840,9 @@ ALTER TABLE vandelay.authority_queue ADD PRIMARY KEY (id);
--	imported_as	INT	REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
-	imported_as	INT	REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
-	import_error	TEXT    REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	error_detail	TEXT
-@@ -1238,9 +1857,9 @@ CREATE INDEX queued_authority_record_attr_record_idx ON vandelay.queued_authorit
--	matched_attr	INT			REFERENCES vandelay.queued_authority_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
--	eg_record		BIGINT		REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
-	eg_record		BIGINT		REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
-    quality         INT         NOT NULL DEFAULT 0
-@@ -1249,6 +1868,10 @@ DECLARE
+    FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
+DECLARE
+    value   TEXT;
+    atype   TEXT;
+    adef    RECORD;
+BEGIN
     IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
         RETURN NEW;
     END IF;
 
-@@ -1264,6 +1887,10 @@ $$ LANGUAGE PLPGSQL;
+    FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
+
+        SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
+        IF (value IS NOT NULL AND value <> '') THEN
+            INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
+        END IF;
+
+    END LOOP;
+
+    RETURN NULL;
+END;
+$$ LANGUAGE PLPGSQL;
+
+ALTER TABLE vandelay.authority_attr_definition DROP COLUMN ident;
+ALTER TABLE vandelay.queued_authority_record
+    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    ADD COLUMN error_detail TEXT;
+
+ALTER TABLE vandelay.authority_match DROP COLUMN matched_attr;
+
+CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
+BEGIN
     IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
         RETURN NEW;
     END IF;
-@@ -372,6 +372,27 @@ CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( BIGINT ) RETURNS
+
+    DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
+    IF TG_OP = 'UPDATE' THEN
+        RETURN NEW;
+    END IF;
+    RETURN OLD;
+END;
+$$ LANGUAGE PLPGSQL;
 
 CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
 DECLARE
@@ -1005,29 +1008,6 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
-
-CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
-DECLARE
-	auth	authority.record_entry%ROWTYPE;
-	output	authority.full_rec%ROWTYPE;
-	field	RECORD;
-BEGIN
-	SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
-
-	FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
-		output.record := rid;
-		output.ind1 := field.ind1;
-		output.ind2 := field.ind2;
-		output.tag := field.tag;
-		output.subfield := field.subfield;
-		output.value := field.value;
-
-		RETURN NEXT output;
-	END LOOP;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-
 -----------------------------------------------
 -- Seed data for import errors
 -----------------------------------------------

commit 70ea48192396caf3051673162cc06a423d065e02
Author: berick <berick at esilibrary.com>
Date:   Thu May 26 10:32:15 2011 -0400

    seed data for vandlelay export templates

diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index 0ad9ad0..fe7618d 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -8823,4 +8823,573 @@ INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missin
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
+----------------------------------------------------------------
+-- Seed data for queued record/item exports
+----------------------------------------------------------------
+
+INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
+        'vandelay.queued_bib_record.print',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.print',
+            'Print output has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_bib_record.csv',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.csv',
+            'CSV output has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_bib_record.email',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.email',
+            'An email has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.print',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.print',
+            'Print output has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.csv',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.csv',
+            'CSV output has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.email',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.email',
+            'An email has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.print',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.print',
+            'Print output has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.csv',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.csv',
+            'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.email',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.email',
+            'An email has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        38,
+        TRUE,
+        1,
+        'Print Output for Queued Bib Records',
+        'vandelay.queued_bib_record.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqbr IN target %]
+=-=-=
+ Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
+ Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
+ Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
+ Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
+ ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
+ ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
+ Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
+ Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
+ TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
+ TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
+ Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
+ Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
+ Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
+ Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
+ Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    38, 'attributes')
+    ,( 38, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        39,
+        TRUE,
+        1,
+        'CSV Output for Queued Bib Records',
+        'vandelay.queued_bib_record.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
+[% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_att
 r('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    39, 'attributes')
+    ,( 39, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        40,
+        TRUE,
+        1,
+        'Email Output for Queued Bib Records',
+        'vandelay.queued_bib_record.email',
+        'NOOP_True',
+        'SendEmail',
+        'queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Bibs from Import Queue
+
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqbr IN target %]
+=-=-=
+ Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
+ Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
+ Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
+ Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
+ ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
+ ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
+ Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
+ Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
+ TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
+ TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
+ Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
+ Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
+ Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
+ Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
+ Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
+
+    [% END %]
+
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    40, 'attributes')
+    ,( 40, 'queue')
+    ,( 40, 'queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        41,
+        TRUE,
+        1,
+        'Print Output for Queued Authority Records',
+        'vandelay.queued_auth_record.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqar IN target %]
+=-=-=
+ Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    41, 'attributes')
+    ,( 41, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        42,
+        TRUE,
+        1,
+        'CSV Output for Queued Authority Records',
+        'vandelay.queued_auth_record.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Record Identifier"
+[% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    42, 'attributes')
+    ,( 42, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        43,
+        TRUE,
+        1,
+        'Email Output for Queued Authority Records',
+        'vandelay.queued_auth_record.email',
+        'NOOP_True',
+        'SendEmail',
+        'queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Authorities from Import Queue
+
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqar IN target %]
+=-=-=
+ Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
+
+    [% END %]
+
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    43, 'attributes')
+    ,( 43, 'queue')
+    ,( 43, 'queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        44,
+        TRUE,
+        1,
+        'Print Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'record.queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.record.queue.id %]
+Queue Name: [% target.0.record.queue.name %]
+Queue Type: [% target.0.record.queue.queue_type %]
+Complete? [% target.0.record.queue.complete %]
+
+    [% FOR vii IN target %]
+=-=-=
+ Import Item ID         | [% vii.id %]
+ Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
+ ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
+ Attribute Definition   | [% vii.definition %]
+ Import Error           | [% vii.import_error %]
+ Import Error Detail    | [% vii.error_detail %]
+ Owning Library         | [% vii.owning_lib %]
+ Circulating Library    | [% vii.circ_lib %]
+ Call Number            | [% vii.call_number %]
+ Copy Number            | [% vii.copy_number %]
+ Status                 | [% vii.status.name %]
+ Shelving Location      | [% vii.location.name %]
+ Circulate              | [% vii.circulate %]
+ Deposit                | [% vii.deposit %]
+ Deposit Amount         | [% vii.deposit_amount %]
+ Reference              | [% vii.ref %]
+ Holdable               | [% vii.holdable %]
+ Price                  | [% vii.price %]
+ Barcode                | [% vii.barcode %]
+ Circulation Modifier   | [% vii.circ_modifier %]
+ Circulate As MARC Type | [% vii.circ_as_type %]
+ Alert Message          | [% vii.alert_message %]
+ Public Note            | [% vii.pub_note %]
+ Private Note           | [% vii.priv_note %]
+ OPAC Visible           | [% vii.opac_visible %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    44, 'record')
+    ,( 44, 'record.attributes')
+    ,( 44, 'record.queue')
+    ,( 44, 'record.queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        45,
+        TRUE,
+        1,
+        'CSV Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'record.queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
+[% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_
 as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    45, 'record')
+    ,( 45, 'record.attributes')
+    ,( 45, 'record.queue')
+    ,( 45, 'record.queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        46,
+        TRUE,
+        1,
+        'Email Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.email',
+        'NOOP_True',
+        'SendEmail',
+        'record.queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.record.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Import Items from Import Queue
+
+Queue ID: [% target.0.record.queue.id %]
+Queue Name: [% target.0.record.queue.name %]
+Queue Type: [% target.0.record.queue.queue_type %]
+Complete? [% target.0.record.queue.complete %]
+
+    [% FOR vii IN target %]
+=-=-=
+ Import Item ID         | [% vii.id %]
+ Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
+ ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
+ Attribute Definition   | [% vii.definition %]
+ Import Error           | [% vii.import_error %]
+ Import Error Detail    | [% vii.error_detail %]
+ Owning Library         | [% vii.owning_lib %]
+ Circulating Library    | [% vii.circ_lib %]
+ Call Number            | [% vii.call_number %]
+ Copy Number            | [% vii.copy_number %]
+ Status                 | [% vii.status.name %]
+ Shelving Location      | [% vii.location.name %]
+ Circulate              | [% vii.circulate %]
+ Deposit                | [% vii.deposit %]
+ Deposit Amount         | [% vii.deposit_amount %]
+ Reference              | [% vii.ref %]
+ Holdable               | [% vii.holdable %]
+ Price                  | [% vii.price %]
+ Barcode                | [% vii.barcode %]
+ Circulation Modifier   | [% vii.circ_modifier %]
+ Circulate As MARC Type | [% vii.circ_as_type %]
+ Alert Message          | [% vii.alert_message %]
+ Public Note            | [% vii.pub_note %]
+ Private Note           | [% vii.priv_note %]
+ OPAC Visible           | [% vii.opac_visible %]
+
+    [% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    46, 'record')
+    ,( 46, 'record.attributes')
+    ,( 46, 'record.queue')
+    ,( 46, 'record.queue.owner')
+;
+
 

commit 55b5194b26062cef811f5fde9487d4b64158bcb5
Author: berick <berick at esilibrary.com>
Date:   Thu May 26 10:30:59 2011 -0400

    Initial upgrade script for vandelay improvements
    
    TODO:
        * Set up the ALTER TABLE statements
        * Further review
        * Testing

diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
new file mode 100644
index 0000000..fd0c694
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
@@ -0,0 +1,1618 @@
+-- Evergreen DB patch XXXX.vandelay-record-matching-and-quality.sql
+--
+-- FIXME: insert description of change, if needed
+--
+BEGIN;
+
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
+
+CREATE TABLE vandelay.match_set (
+    id      SERIAL  PRIMARY KEY,
+    name    TEXT        NOT NULL,
+    owner   INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
+    mtype   TEXT        NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
+    CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
+);
+
+-- Table to define match points, either FF via SVF or tag+subfield
+CREATE TABLE vandelay.match_set_point (
+    id          SERIAL  PRIMARY KEY,
+    match_set   INT     REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
+    parent      INT     REFERENCES vandelay.match_set_point (id),
+    bool_op     TEXT    CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
+    svf         TEXT    REFERENCES config.record_attr_definition (name),
+    tag         TEXT,
+    subfield    TEXT,
+    negate      BOOL    DEFAULT FALSE,
+    quality     INT     NOT NULL DEFAULT 1, -- higher is better
+    CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
+    CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
+        (tag IS NOT NULL AND svf IS NULL AND bool_op IS NULL) OR
+        (tag IS NULL AND svf IS NOT NULL AND bool_op IS NULL) OR
+        (tag IS NULL AND svf IS NULL AND bool_op IS NOT NULL)
+    )
+);
+
+CREATE TABLE vandelay.match_set_quality (
+    id          SERIAL  PRIMARY KEY,
+    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
+    svf         TEXT    REFERENCES config.record_attr_definition,
+    tag         TEXT,
+    subfield    TEXT,
+    value       TEXT    NOT NULL,
+    quality     INT     NOT NULL DEFAULT 1, -- higher is better
+    CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
+    CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
+);
+CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''), value);
+
+
+-- ALTER TABLEs...
+
+    match_set       INT         REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+@@ -18,7 +60,8 @@ CREATE TABLE vandelay.queued_record (
+-    marc		TEXT                        NOT NULL
+    marc		TEXT                        NOT NULL,
+    quality     INT                         NOT NULL DEFAULT 0
+@@ -31,8 +74,7 @@ CREATE TABLE vandelay.bib_attr_definition (
+-	remove		TEXT	NOT NULL DEFAULT '',
+-	ident		BOOL	NOT NULL DEFAULT FALSE
+	remove		TEXT	NOT NULL DEFAULT ''
+@@ -67,6 +109,11 @@ CREATE TABLE vandelay.import_item_attr_definition (
+CREATE TABLE vandelay.import_error (
+    code        TEXT    PRIMARY KEY,
+    description TEXT    NOT NULL -- i18n
+);
+
+@@ -75,9 +122,11 @@ CREATE TABLE vandelay.bib_queue (
+-	queue		INT		NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+-	bib_source	INT		REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
+-	imported_as	BIGINT	REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
+	queue		    INT		NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	bib_source	    INT		REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
+	imported_as 	BIGINT	REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
+	import_error	TEXT    REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	error_detail	TEXT
+@@ -92,17 +141,20 @@ CREATE INDEX queued_bib_record_attr_record_idx ON vandelay.queued_bib_record_att
+-	field_type		TEXT		NOT NULL CHECK (field_type in ('isbn','tcn_value','id')),
+-	matched_attr	INT			REFERENCES vandelay.queued_bib_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+-	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
+	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    quality         INT         NOT NULL DEFAULT 1,
+    match_score     INT         NOT NULL DEFAULT 0
+--- DROP TABLE vandelay.import_item CASCADE;
+	import_error	TEXT        REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	error_detail	TEXT,
+    imported_as     BIGINT      REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
+    import_time	    TIMESTAMP WITH TIME ZONE,
+@@ -139,10 +191,592 @@ CREATE TABLE vandelay.merge_profile (
+    lwm_ratio       NUMERIC,
+
+
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
+DECLARE
+    ldr         TEXT;
+    tval        TEXT;
+    tval_rec    RECORD;
+    bval        TEXT;
+    bval_rec    RECORD;
+    retval      config.marc21_rec_type_map%ROWTYPE;
+BEGIN
+    ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
+
+    IF ldr IS NULL OR ldr = '' THEN
+        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
+        RETURN retval;
+    END IF;
+
+    SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
+    SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
+
+
+    tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
+    bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
+
+    -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
+
+    SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
+
+
+    IF retval.code IS NULL THEN
+        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
+    END IF;
+
+    RETURN retval;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
+DECLARE
+    rtype       TEXT;
+    ff_pos      RECORD;
+    tag_data    RECORD;
+    val         TEXT;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
+        IF ff_pos.tag = 'ldr' THEN
+            val := oils_xpath_string('//*[local-name()="leader"]', marc);
+            IF val IS NOT NULL THEN
+                val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN val;
+            END IF;
+        ELSE
+            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+                val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN val;
+            END LOOP;
+        END IF;
+        val := REPEAT( ff_pos.default_val, ff_pos.length );
+        RETURN val;
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TYPE biblio.record_ff_map AS (record BIGINT, ff_name TEXT, ff_value TEXT);
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
+DECLARE
+    tag_data    TEXT;
+    rtype       TEXT;
+    ff_pos      RECORD;
+    output      biblio.record_ff_map%ROWTYPE;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
+        output.ff_name  := ff_pos.fixed_field;
+        output.ff_value := NULL;
+
+        IF ff_pos.tag = 'ldr' THEN
+            output.ff_value := oils_xpath_string('//*[local-name()="leader"]', marc);
+            IF output.ff_value IS NOT NULL THEN
+                output.ff_value := SUBSTRING( output.ff_value, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN NEXT output;
+                output.ff_value := NULL;
+            END IF;
+        ELSE
+            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+                output.ff_value := SUBSTRING( tag_data, ff_pos.start_pos + 1, ff_pos.length );
+                IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
+                RETURN NEXT output;
+                output.ff_value := NULL;
+            END LOOP;
+        END IF;
+    
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TYPE biblio.marc21_physical_characteristics AS ( id INT, record BIGINT, ptype TEXT, subfield INT, value INT );
+CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
+DECLARE
+    rowid   INT := 0;
+    _007    TEXT;
+    ptype   config.marc21_physical_characteristic_type_map%ROWTYPE;
+    psf     config.marc21_physical_characteristic_subfield_map%ROWTYPE;
+    pval    config.marc21_physical_characteristic_value_map%ROWTYPE;
+    retval  biblio.marc21_physical_characteristics%ROWTYPE;
+BEGIN
+
+    _007 := oils_xpath_string( '//*[@tag="007"]', marc );
+
+    IF _007 IS NOT NULL AND _007 <> '' THEN
+        SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
+
+        IF ptype.ptype_key IS NOT NULL THEN
+            FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
+                SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
+
+                IF pval.id IS NOT NULL THEN
+                    rowid := rowid + 1;
+                    retval.id := rowid;
+                    retval.ptype := ptype.ptype_key;
+                    retval.subfield := psf.id;
+                    retval.value := pval.id;
+                    RETURN NEXT retval;
+                END IF;
+
+            END LOOP;
+        END IF;
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
+CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
+
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+use MARC::Charset;
+use strict;
+
+MARC::Charset->assume_unicode(1);
+
+my $xml = shift;
+my $r = MARC::Record->new_from_xml( $xml );
+
+return_next( { tag => 'LDR', value => $r->leader } );
+
+for my $f ( $r->fields ) {
+    if ($f->is_control_field) {
+        return_next({ tag => $f->tag, value => $f->data });
+    } else {
+        for my $s ($f->subfields) {
+            return_next({
+                tag      => $f->tag,
+                ind1     => $f->indicator(1),
+                ind2     => $f->indicator(2),
+                subfield => $s->[0],
+                value    => $s->[1]
+            });
+
+            if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
+                my $trim = $f->indicator(2) || 0;
+                return_next({
+                    tag      => 'tnf',
+                    ind1     => $f->indicator(1),
+                    ind2     => $f->indicator(2),
+                    subfield => 'a',
+                    value    => substr( $s->[1], $trim )
+                });
+            }
+        }
+    }
+}
+
+return undef;
+
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION vandelay.flatten_marc ( marc TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
+DECLARE
+    output  vandelay.flat_marc%ROWTYPE;
+    field   RECORD;
+BEGIN
+    FOR field IN SELECT * FROM vandelay.flay_marc( marc ) LOOP
+        output.ind1 := field.ind1;
+        output.ind2 := field.ind2;
+        output.tag := field.tag;
+        output.subfield := field.subfield;
+        IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
+            output.value := naco_normalize(field.value, field.subfield);
+        ELSE
+            output.value := field.value;
+        END IF;
+
+        CONTINUE WHEN output.value IS NULL;
+
+        RETURN NEXT output;
+    END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
+DECLARE
+    transformed_xml TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_value      TEXT;
+    new_attrs       HSTORE := ''::HSTORE;
+    attr_def        config.record_attr_definition%ROWTYPE;
+BEGIN
+
+    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
+
+        IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+            SELECT  ARRAY_TO_STRING(ARRAY_ACCUM(x.value), COALESCE(attr_def.joiner,' ')) INTO attr_value
+              FROM  vandelay.flatten_marc(xml) AS x
+              WHERE x.tag LIKE attr_def.tag
+                    AND CASE
+                        WHEN attr_def.sf_list IS NOT NULL
+                            THEN POSITION(x.subfield IN attr_def.sf_list) > 0
+                        ELSE TRUE
+                        END
+              GROUP BY x.tag
+              ORDER BY x.tag
+              LIMIT 1;
+
+        ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+            attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
+
+        ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+            SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+
+            -- See if we can skip the XSLT ... it's expensive
+            IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                -- Can't skip the transform
+                IF xfrm.xslt <> '---' THEN
+                    transformed_xml := oils_xslt_process(xml,xfrm.xslt);
+                ELSE
+                    transformed_xml := xml;
+                END IF;
+
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            IF xfrm.name IS NULL THEN
+                -- just grab the marcxml (empty) transform
+                SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
+
+        ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+            SELECT  value::TEXT INTO attr_value
+              FROM  vandelay.marc21_physical_characteristics(xml)
+              WHERE subfield = attr_def.phys_char_sf
+              LIMIT 1; -- Just in case ...
+
+        END IF;
+
+        -- apply index normalizers to attr_value
+        FOR normalizer IN
+            SELECT  n.func AS func,
+                    n.param_count AS param_count,
+                    m.params AS params
+              FROM  config.index_normalizer n
+                    JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+              WHERE attr = attr_def.name
+              ORDER BY m.pos LOOP
+                EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    quote_literal( attr_value ) ||
+                    CASE
+                        WHEN normalizer.param_count > 0
+                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                            ELSE ''
+                        END ||
+                    ')' INTO attr_value;
+
+        END LOOP;
+
+        -- Add the new value to the hstore
+        new_attrs := new_attrs || hstore( attr_def.name, attr_value );
+
+    END LOOP;
+
+    RETURN new_attrs;
+END;
+$_$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
+    SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
+$_$ LANGUAGE SQL;
+
+-- Everything between this comment and the beginning of the definition of
+-- vandelay.match_bib_record() is strictly in service of that function.
+CREATE TYPE vandelay.match_set_test_result AS (record BIGINT, quality INTEGER);
+
+CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
+    match_set_id INTEGER, record_xml TEXT
+) RETURNS SETOF vandelay.match_set_test_result AS $$
+DECLARE
+    tags_rstore HSTORE;
+    svf_rstore  HSTORE;
+    coal        TEXT;
+    joins       TEXT;
+    query_      TEXT;
+    wq          TEXT;
+    qvalue      INTEGER;
+    rec         RECORD;
+BEGIN
+    tags_rstore := vandelay.flatten_marc_hstore(record_xml);
+    svf_rstore := vandelay.extract_rec_attrs(record_xml);
+
+    CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
+    CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
+
+    -- generate the where clause and return that directly (into wq), and as
+    -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
+    wq := vandelay.get_expr_from_match_set(match_set_id);
+
+    query_ := 'SELECT bre.id AS record, ';
+
+    -- qrows table is for the quality bits we add to the SELECT clause
+    SELECT ARRAY_TO_STRING(
+        ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
+    ) INTO coal FROM _vandelay_tmp_qrows;
+
+    -- our query string so far is the SELECT clause and the inital FROM.
+    -- no JOINs yet nor the WHERE clause
+    query_ := query_ || coal || ' AS quality ' || E'\n' ||
+        'FROM biblio.record_entry bre ';
+
+    -- jrows table is for the joins we must make (and the real text conditions)
+    SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
+        FROM _vandelay_tmp_jrows;
+
+    -- add those joins and the where clause to our query.
+    query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
+
+    -- this will return rows of record,quality
+    FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
+        RETURN NEXT rec;
+    END LOOP;
+
+    DROP TABLE _vandelay_tmp_qrows;
+    DROP TABLE _vandelay_tmp_jrows;
+    RETURN;
+END;
+
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
+    record_xml TEXT
+) RETURNS HSTORE AS $$
+BEGIN
+    RETURN (SELECT
+        HSTORE(
+            ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
+            ARRAY_ACCUM(value)
+        )
+        FROM (
+            SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
+                FROM vandelay.flatten_marc(record_xml)
+                GROUP BY tag, subfield ORDER BY tag, subfield
+        ) subquery
+    );
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set(
+    match_set_id INTEGER
+) RETURNS TEXT AS $$
+DECLARE
+    root    vandelay.match_set_point;
+BEGIN
+    SELECT * INTO root FROM vandelay.match_set_point
+        WHERE parent IS NULL AND match_set = match_set_id;
+
+    RETURN vandelay.get_expr_from_match_set_point(root);
+END;
+$$  LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
+    node vandelay.match_set_point
+) RETURNS TEXT AS $$
+DECLARE
+    q           TEXT;
+    i           INTEGER;
+    this_op     TEXT;
+    children    INTEGER[];
+    child       vandelay.match_set_point;
+BEGIN
+    SELECT ARRAY_ACCUM(id) INTO children FROM vandelay.match_set_point
+        WHERE parent = node.id;
+
+    IF ARRAY_LENGTH(children, 1) > 0 THEN
+        this_op := vandelay._get_expr_render_one(node);
+        q := '(';
+        i := 1;
+        WHILE children[i] IS NOT NULL LOOP
+            SELECT * INTO child FROM vandelay.match_set_point
+                WHERE id = children[i];
+            IF i > 1 THEN
+                q := q || ' ' || this_op || ' ';
+            END IF;
+            i := i + 1;
+            q := q || vandelay.get_expr_from_match_set_point(child);
+        END LOOP;
+        q := q || ')';
+        RETURN q;
+    ELSIF node.bool_op IS NULL THEN
+        PERFORM vandelay._get_expr_push_qrow(node);
+        PERFORM vandelay._get_expr_push_jrow(node);
+        RETURN vandelay._get_expr_render_one(node);
+    ELSE
+        RETURN '';
+    END IF;
+END;
+$$  LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_push_qrow(
+    node vandelay.match_set_point
+) RETURNS VOID AS $$
+DECLARE
+BEGIN
+    INSERT INTO _vandelay_tmp_qrows (q) VALUES (node.id);
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
+    node vandelay.match_set_point
+) RETURNS VOID AS $$
+DECLARE
+    jrow        TEXT;
+    my_alias    TEXT;
+    op          TEXT;
+    tagkey      TEXT;
+BEGIN
+    IF node.negate THEN
+        op := '<>';
+    ELSE
+        op := '=';
+    END IF;
+
+    IF node.tag IS NOT NULL THEN
+        tagkey := node.tag;
+        IF node.subfield IS NOT NULL THEN
+            tagkey := tagkey || node.subfield;
+        END IF;
+    END IF;
+
+    my_alias := 'n' || node.id::TEXT;
+
+    jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
+        ' AS quality FROM metabib.';
+    IF node.tag IS NOT NULL THEN
+        jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
+            my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
+            node.tag || '''';
+        IF node.subfield IS NOT NULL THEN
+            jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
+                node.subfield || '''';
+        END IF;
+        jrow := jrow || ' AND (' || my_alias || '.value ' || op ||
+            ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
+    ELSE    -- svf
+        jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
+            my_alias || '.id = bre.id AND (' ||
+            my_alias || '.attrs->''' || node.svf ||
+            ''' ' || op || ' $2->''' || node.svf || '''))';
+    END IF;
+    INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_render_one(
+    node vandelay.match_set_point
+) RETURNS TEXT AS $$
+DECLARE
+    s           TEXT;
+BEGIN
+    IF node.bool_op IS NOT NULL THEN
+        RETURN node.bool_op;
+    ELSE
+        RETURN '(n' || node.id::TEXT || '.id IS NOT NULL)';
+    END IF;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
+DECLARE
+    incoming_existing_id    TEXT;
+    test_result             vandelay.match_set_test_result%ROWTYPE;
+    tmp_rec                 BIGINT;
+    match_set               INT;
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
+
+    SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
+
+    IF match_set IS NOT NULL THEN
+        NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
+    END IF;
+
+    -- Perfect matches on 901$c exit early with a match with high quality.
+    incoming_existing_id :=
+        oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
+
+    IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
+        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
+        IF tmp_rec IS NOT NULL THEN
+            INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality) 
+                SELECT
+                    NEW.id, 
+                    b.id,
+                    9999,
+                    -- note: no match_set means quality==0
+                    vandelay.measure_record_quality( b.marc, match_set )
+                FROM biblio.record_entry b
+                WHERE id = incoming_existing_id::bigint;
+        END IF;
+    END IF;
+
+    IF match_set IS NULL THEN
+        RETURN NEW;
+    END IF;
+
+    FOR test_result IN SELECT * FROM
+        vandelay.match_set_test_marcxml(match_set, NEW.marc) LOOP
+
+        INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
+            SELECT  
+                NEW.id,
+                test_result.record,
+                test_result.quality,
+                vandelay.measure_record_quality( b.marc, match_set )
+	        FROM  biblio.record_entry b
+	        WHERE id = test_result.record;
+
+    END LOOP;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.measure_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
+DECLARE
+    out_q   INT := 0;
+    rvalue  TEXT;
+    test    vandelay.match_set_quality%ROWTYPE;
+BEGIN
+
+    FOR test IN SELECT * FROM vandelay.match_set_quality WHERE match_set = match_set_id LOOP
+        IF test.tag IS NOT NULL THEN
+            FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
+                IF test.value = rvalue THEN
+                    out_q := out_q + test.quality;
+                END IF;
+            END LOOP;
+        ELSE
+            IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) -> test.svf THEN
+                out_q := out_q + test.quality;
+            END IF;
+        END IF;
+    END LOOP;
+
+    RETURN out_q;
+END;
+$_$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION vandelay.overlay_bib_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
+DECLARE
+    merge_profile   vandelay.merge_profile%ROWTYPE;
+    dyn_profile     vandelay.compile_profile%ROWTYPE;
+    editor_string   TEXT;
+    editor_id       INT;
+    source_marc     TEXT;
+    target_marc     TEXT;
+    eg_marc         TEXT;
+    v_marc          TEXT;
+    replace_rule    TEXT;
+BEGIN
+
+    SELECT  q.marc INTO v_marc
+      FROM  vandelay.queued_record q
+            JOIN vandelay.bib_match m ON (m.queued_record = q.id AND q.id = import_id)
+      LIMIT 1;
+
+    IF v_marc IS NULL THEN
+        -- RAISE NOTICE 'no marc for vandelay or bib record';
+        RETURN FALSE;
+    END IF;
+
+    IF vandelay.template_overlay_bib_record( v_marc, eg_id, merge_profile_id) THEN
+        UPDATE  vandelay.queued_bib_record
+          SET   imported_as = eg_id,
+                import_time = NOW()
+          WHERE id = import_id;
+
+        editor_string := (oils_xpath('//*[@tag="905"]/*[@code="u"]/text()',v_marc))[1];
+
+        IF editor_string IS NOT NULL AND editor_string <> '' THEN
+            SELECT usr INTO editor_id FROM actor.card WHERE barcode = editor_string;
+
+            IF editor_id IS NULL THEN
+                SELECT id INTO editor_id FROM actor.usr WHERE usrname = editor_string;
+            END IF;
+
+            IF editor_id IS NOT NULL THEN
+                UPDATE biblio.record_entry SET editor = editor_id WHERE id = eg_id;
+            END IF;
+        END IF;
+
+        RETURN TRUE;
+    END IF;
+
+    -- RAISE NOTICE 'update of biblio.record_entry failed';
+
+    RETURN FALSE;
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
+DECLARE
+    eg_id           BIGINT;
+    lwm_ratio_value NUMERIC;
+BEGIN
+
+    lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
+
+    PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
+
+    IF FOUND THEN
+        -- RAISE NOTICE 'already imported, cannot auto-overlay'
+        RETURN FALSE;
+    END IF;
+
+    SELECT  m.eg_record INTO eg_id
+      FROM  vandelay.bib_match m
+            JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
+            JOIN vandelay.bib_queue q ON (qr.queue = q.id)
+            JOIN biblio.record_entry r ON (r.id = m.eg_record)
+      WHERE m.queued_record = import_id
+            AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
+      ORDER BY  m.match_score DESC, -- required match score
+                qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
+                m.id -- when in doubt, use the first match
+      LIMIT 1;
+
+    IF eg_id IS NULL THEN
+        -- RAISE NOTICE 'incoming record is not of high enough quality';
+        RETURN FALSE;
+    END IF;
+
+    RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
+DECLARE
+    eg_id           BIGINT;
+    lwm_ratio_value NUMERIC;
+BEGIN
+
+    lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
+
+    PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
+
+    IF FOUND THEN
+        -- RAISE NOTICE 'already imported, cannot auto-overlay'
+        RETURN FALSE;
+    END IF;
+
+    SELECT  m.eg_record INTO eg_id
+      FROM  vandelay.bib_match m
+            JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
+            JOIN vandelay.bib_queue q ON (qr.queue = q.id)
+            JOIN biblio.record_entry r ON (r.id = m.eg_record)
+      WHERE m.queued_record = import_id
+            AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
+      ORDER BY  m.match_score DESC, -- required match score
+                qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
+                m.id -- when in doubt, use the first match
+      LIMIT 1;
+
+    IF eg_id IS NULL THEN
+        -- RAISE NOTICE 'incoming record is not of high enough quality';
+        RETURN FALSE;
+    END IF;
+
+    RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( queue_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS SETOF BIGINT AS $$
+DECLARE
+    queued_record   vandelay.queued_bib_record%ROWTYPE;
+BEGIN
+
+    FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
+
+        IF vandelay.auto_overlay_bib_record_with_best( queued_record.id, merge_profile_id, lwm_ratio_value ) THEN
+            RETURN NEXT queued_record.id;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+    
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
+    SELECT vandelay.auto_overlay_bib_queue_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
+DECLARE
+    value   TEXT;
+    atype   TEXT;
+    adef    RECORD;
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
+
+        SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
+        IF (value IS NOT NULL AND value <> '') THEN
+            INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
+        END IF;
+
+    END LOOP;
+
+    RETURN NULL;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
+DECLARE
+    attr_def    BIGINT;
+    item_data   vandelay.import_item%ROWTYPE;
+BEGIN
+
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
+
+    FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
+        INSERT INTO vandelay.import_item (
+            record,
+            definition,
+            owning_lib,
+            circ_lib,
+            call_number,
+            copy_number,
+            status,
+            location,
+            circulate,
+            deposit,
+            deposit_amount,
+            ref,
+            holdable,
+            price,
+            barcode,
+            circ_modifier,
+            circ_as_type,
+            alert_message,
+            pub_note,
+            priv_note,
+            opac_visible
+        ) VALUES (
+            NEW.id,
+            item_data.definition,
+            item_data.owning_lib,
+            item_data.circ_lib,
+            item_data.call_number,
+            item_data.copy_number,
+            item_data.status,
+            item_data.location,
+            item_data.circulate,
+            item_data.deposit,
+            item_data.deposit_amount,
+            item_data.ref,
+            item_data.holdable,
+            item_data.price,
+            item_data.barcode,
+            item_data.circ_modifier,
+            item_data.circ_as_type,
+            item_data.alert_message,
+            item_data.pub_note,
+            item_data.priv_note,
+            item_data.opac_visible
+        );
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
+    DELETE FROM vandelay.import_item WHERE record = OLD.id;
+
+    IF TG_OP = 'UPDATE' THEN
+        RETURN NEW;
+    END IF;
+    RETURN OLD;
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+-- ALTER TABLEs...
+
+-
+-CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
+-BEGIN
+@@ -1200,7 +1818,7 @@ CREATE TRIGGER ingest_item_trigger
+-    AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
+    BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
+@@ -1211,8 +1829,7 @@ CREATE TABLE vandelay.authority_attr_definition (
+-	remove		TEXT	NOT NULL DEFAULT '',
+-	ident		BOOL	NOT NULL DEFAULT FALSE
+	remove		TEXT	NOT NULL DEFAULT ''
+@@ -1223,7 +1840,9 @@ ALTER TABLE vandelay.authority_queue ADD PRIMARY KEY (id);
+-	imported_as	INT	REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
+	imported_as	INT	REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
+	import_error	TEXT    REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	error_detail	TEXT
+@@ -1238,9 +1857,9 @@ CREATE INDEX queued_authority_record_attr_record_idx ON vandelay.queued_authorit
+-	matched_attr	INT			REFERENCES vandelay.queued_authority_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+-	eg_record		BIGINT		REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
+	eg_record		BIGINT		REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
+    quality         INT         NOT NULL DEFAULT 0
+@@ -1249,6 +1868,10 @@ DECLARE
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+@@ -1264,6 +1887,10 @@ $$ LANGUAGE PLPGSQL;
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+@@ -372,6 +372,27 @@ CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( BIGINT ) RETURNS
+
+CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
+DECLARE
+	auth	authority.record_entry%ROWTYPE;
+	output	authority.full_rec%ROWTYPE;
+	field	RECORD;
+BEGIN
+	SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
+
+	FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
+		output.record := rid;
+		output.ind1 := field.ind1;
+		output.ind2 := field.ind2;
+		output.tag := field.tag;
+		output.subfield := field.subfield;
+		output.value := field.value;
+
+		RETURN NEXT output;
+	END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
+DECLARE
+	bib	biblio.record_entry%ROWTYPE;
+	output	metabib.full_rec%ROWTYPE;
+	field	RECORD;
+BEGIN
+	SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
+
+	FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
+		output.record := rid;
+		output.ind1 := field.ind1;
+		output.ind2 := field.ind2;
+		output.tag := field.tag;
+		output.subfield := field.subfield;
+		output.value := field.value;
+
+		RETURN NEXT output;
+	END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
+DECLARE
+	auth	authority.record_entry%ROWTYPE;
+	output	authority.full_rec%ROWTYPE;
+	field	RECORD;
+BEGIN
+	SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
+
+	FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
+		output.record := rid;
+		output.ind1 := field.ind1;
+		output.ind2 := field.ind2;
+		output.tag := field.tag;
+		output.subfield := field.subfield;
+		output.value := field.value;
+
+		RETURN NEXT output;
+	END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+-----------------------------------------------
+-- Seed data for import errors
+-----------------------------------------------
+
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.circ_modifier', oils_i18n_gettext('import.item.invalid.circ_modifier', 'Import failed due to invalid circulation modifier', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.location', oils_i18n_gettext('import.item.invalid.location', 'Import failed due to invalid copy location', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.tcn', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.record.quality', oils_i18n_gettext('overlay.record.quality', 'New record had insufficient quality', 'vie', 'description') );
+
+
+----------------------------------------------------------------
+-- Seed data for queued record/item exports
+----------------------------------------------------------------
+
+INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
+        'vandelay.queued_bib_record.print',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.print',
+            'Print output has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_bib_record.csv',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.csv',
+            'CSV output has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_bib_record.email',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.email',
+            'An email has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.print',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.print',
+            'Print output has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.csv',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.csv',
+            'CSV output has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.email',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.email',
+            'An email has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.print',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.print',
+            'Print output has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.csv',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.csv',
+            'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.email',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.email',
+            'An email has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        38,
+        TRUE,
+        1,
+        'Print Output for Queued Bib Records',
+        'vandelay.queued_bib_record.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqbr IN target %]
+=-=-=
+ Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
+ Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
+ Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
+ Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
+ ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
+ ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
+ Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
+ Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
+ TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
+ TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
+ Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
+ Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
+ Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
+ Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
+ Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    38, 'attributes')
+    ,( 38, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        39,
+        TRUE,
+        1,
+        'CSV Output for Queued Bib Records',
+        'vandelay.queued_bib_record.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
+[% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_att
 r('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    39, 'attributes')
+    ,( 39, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        40,
+        TRUE,
+        1,
+        'Email Output for Queued Bib Records',
+        'vandelay.queued_bib_record.email',
+        'NOOP_True',
+        'SendEmail',
+        'queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Bibs from Import Queue
+
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqbr IN target %]
+=-=-=
+ Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
+ Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
+ Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
+ Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
+ ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
+ ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
+ Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
+ Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
+ TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
+ TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
+ Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
+ Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
+ Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
+ Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
+ Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
+
+    [% END %]
+
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    40, 'attributes')
+    ,( 40, 'queue')
+    ,( 40, 'queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        41,
+        TRUE,
+        1,
+        'Print Output for Queued Authority Records',
+        'vandelay.queued_auth_record.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqar IN target %]
+=-=-=
+ Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    41, 'attributes')
+    ,( 41, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        42,
+        TRUE,
+        1,
+        'CSV Output for Queued Authority Records',
+        'vandelay.queued_auth_record.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Record Identifier"
+[% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    42, 'attributes')
+    ,( 42, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        43,
+        TRUE,
+        1,
+        'Email Output for Queued Authority Records',
+        'vandelay.queued_auth_record.email',
+        'NOOP_True',
+        'SendEmail',
+        'queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Authorities from Import Queue
+
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqar IN target %]
+=-=-=
+ Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
+
+    [% END %]
+
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    43, 'attributes')
+    ,( 43, 'queue')
+    ,( 43, 'queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        44,
+        TRUE,
+        1,
+        'Print Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'record.queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.record.queue.id %]
+Queue Name: [% target.0.record.queue.name %]
+Queue Type: [% target.0.record.queue.queue_type %]
+Complete? [% target.0.record.queue.complete %]
+
+    [% FOR vii IN target %]
+=-=-=
+ Import Item ID         | [% vii.id %]
+ Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
+ ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
+ Attribute Definition   | [% vii.definition %]
+ Import Error           | [% vii.import_error %]
+ Import Error Detail    | [% vii.error_detail %]
+ Owning Library         | [% vii.owning_lib %]
+ Circulating Library    | [% vii.circ_lib %]
+ Call Number            | [% vii.call_number %]
+ Copy Number            | [% vii.copy_number %]
+ Status                 | [% vii.status.name %]
+ Shelving Location      | [% vii.location.name %]
+ Circulate              | [% vii.circulate %]
+ Deposit                | [% vii.deposit %]
+ Deposit Amount         | [% vii.deposit_amount %]
+ Reference              | [% vii.ref %]
+ Holdable               | [% vii.holdable %]
+ Price                  | [% vii.price %]
+ Barcode                | [% vii.barcode %]
+ Circulation Modifier   | [% vii.circ_modifier %]
+ Circulate As MARC Type | [% vii.circ_as_type %]
+ Alert Message          | [% vii.alert_message %]
+ Public Note            | [% vii.pub_note %]
+ Private Note           | [% vii.priv_note %]
+ OPAC Visible           | [% vii.opac_visible %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    44, 'record')
+    ,( 44, 'record.attributes')
+    ,( 44, 'record.queue')
+    ,( 44, 'record.queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        45,
+        TRUE,
+        1,
+        'CSV Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'record.queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
+[% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_
 as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    45, 'record')
+    ,( 45, 'record.attributes')
+    ,( 45, 'record.queue')
+    ,( 45, 'record.queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        46,
+        TRUE,
+        1,
+        'Email Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.email',
+        'NOOP_True',
+        'SendEmail',
+        'record.queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.record.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Import Items from Import Queue
+
+Queue ID: [% target.0.record.queue.id %]
+Queue Name: [% target.0.record.queue.name %]
+Queue Type: [% target.0.record.queue.queue_type %]
+Complete? [% target.0.record.queue.complete %]
+
+    [% FOR vii IN target %]
+=-=-=
+ Import Item ID         | [% vii.id %]
+ Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
+ ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
+ Attribute Definition   | [% vii.definition %]
+ Import Error           | [% vii.import_error %]
+ Import Error Detail    | [% vii.error_detail %]
+ Owning Library         | [% vii.owning_lib %]
+ Circulating Library    | [% vii.circ_lib %]
+ Call Number            | [% vii.call_number %]
+ Copy Number            | [% vii.copy_number %]
+ Status                 | [% vii.status.name %]
+ Shelving Location      | [% vii.location.name %]
+ Circulate              | [% vii.circulate %]
+ Deposit                | [% vii.deposit %]
+ Deposit Amount         | [% vii.deposit_amount %]
+ Reference              | [% vii.ref %]
+ Holdable               | [% vii.holdable %]
+ Price                  | [% vii.price %]
+ Barcode                | [% vii.barcode %]
+ Circulation Modifier   | [% vii.circ_modifier %]
+ Circulate As MARC Type | [% vii.circ_as_type %]
+ Alert Message          | [% vii.alert_message %]
+ Public Note            | [% vii.pub_note %]
+ Private Note           | [% vii.priv_note %]
+ OPAC Visible           | [% vii.opac_visible %]
+
+    [% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    46, 'record')
+    ,( 46, 'record.attributes')
+    ,( 46, 'record.queue')
+    ,( 46, 'record.queue.owner')
+;
+
+
+COMMIT;

commit b79724aa8ad7ea1783b8f100e19effa493b39b34
Author: berick <berick at esilibrary.com>
Date:   Tue May 24 09:16:10 2011 -0400

    repair selector column width (move to width attr) to free up space in queue display

diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index 783ffa5..466ee4f 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -116,7 +116,8 @@
                     field='+row_selector'
                     get='vlQueueGridDrawSelectBox'
                     formatter='vlQueueGridFormatSelectBox'
-                    styles='text-align: center;width:30px;'
+                    width='16'
+                    styles='text-align: center;'
                     nonSelectable='true'>
                         <input id="vl-queue-grid-row-selector" type="checkbox" onclick="vlToggleQueueGridSelect();"></input>
                 </th>

commit a513654859fd4eeb188097355a34074406a85479
Author: berick <berick at esilibrary.com>
Date:   Tue May 24 09:15:13 2011 -0400

    disable sorting on selector column in queue grid

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index de1361e..97be36b 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -893,6 +893,9 @@ var vlQueueGridLayout = null;
 function buildRecordGrid(type) {
     displayGlobalDiv('vl-queue-div');
 
+    vlBibQueueGrid.canSort = function(col){ if(Math.abs(col) == 1) { return false; } else { return true; } }; 
+    vlAuthQueueGrid.canSort = function(col){ if(Math.abs(col) == 1) { return false; } else { return true; } }; 
+
     if(type == 'bib') {
         openils.Util.show('vl-bib-queue-grid-wrapper');
         openils.Util.hide('vl-auth-queue-grid-wrapper');

commit 19ab89718293f1ae761a0237cea47ecbfda76f9c
Author: berick <berick at esilibrary.com>
Date:   Mon May 23 14:17:08 2011 -0400

    avoid pile-up of dojo-attached event handlers for queue upload inputs

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 57e220b..de1361e 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -1032,9 +1032,13 @@ function vlFetchQueueSummary(qId, type, onload) {
     );
 }
 
+var _importCancelHandler;
+var _importGoHandler;
 function vlHandleQueueItemsAction(action) {
 
-    dojo.connect(
+    if(_importCancelHandler) dojo.disconnect(_importCancelHandler);
+
+    _importCancelHandler = dojo.connect(
         queueItemsImportCancelButton, 
         'onClick', 
         function() {
@@ -1042,7 +1046,10 @@ function vlHandleQueueItemsAction(action) {
         }
     );
 
-    dojo.connect(
+    if(_importGoHandler)
+        dojo.disconnect(_importGoHandler);
+
+    _importGoHandler = dojo.connect(
         queueItemsImportGoButton,
         'onClick', 
         function() {

commit 4c0d9bcacb0cb29c2a2c33032fed8392299353b5
Author: berick <berick at esilibrary.com>
Date:   Mon May 23 14:16:32 2011 -0400

    set imported_as / import_time on import items after successful import

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index c4ca18c..b3e516b 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1550,6 +1550,16 @@ sub import_record_asset_list_impl {
                 next;
             }
 
+            # set the import data on the import item
+            $item->imported_as($copy->id); # $copy->id is set by create_copy() ^--
+            $item->import_time('now');
+
+            unless($e->update_vandelay_import_item($item)) {
+                $$report_args{evt} = $e->die_event;
+                respond_with_status($report_args);
+                next;
+            }
+
             # --------------------------------------------------------------------------------
             # Item import succeeded
             # --------------------------------------------------------------------------------

commit 669d12b7b58da33fa889fc0faa8b1ea40f4733d8
Author: berick <berick at esilibrary.com>
Date:   Mon May 23 11:51:05 2011 -0400

    Yet more Vandelay fixes
    
    * Remove the deprecated .nomatch API call.  no-match is now a runtime
    option
    
    * Re-fetch queued record after auto-import to avoid clobbering
    imported_as as set by the DB during auto import

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 9dd923e..c4ca18c 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -765,7 +765,13 @@ __PACKAGE__->register_method(
     argc        => 2,
     stream      => 1,
     max_chunk_size => 0,
-    record_type => 'bib'
+    record_type => 'bib',
+    signature => {
+        desc => q/
+            Attempts to import all non-imported records for the selected queue.
+            Will also attempt import of all non-imported items.
+        /
+    }
 );
 
 __PACKAGE__->register_method(  
@@ -777,31 +783,7 @@ __PACKAGE__->register_method(
     max_chunk_size => 0,
     record_type => 'auth'
 );
-__PACKAGE__->register_method(  
-    api_name    => "open-ils.vandelay.bib_queue.nomatch.import",
-    method      => 'import_queue',
-    api_level   => 1,
-    argc        => 2,
-    stream      => 1,
-    signature   => {
-        desc => q/Only import records that have no collisions/
-    },
-    max_chunk_size => 0,
-    record_type => 'bib'
-);
 
-__PACKAGE__->register_method(  
-    api_name    => "open-ils.vandelay.auth_queue.nomatch.import",
-    method      => 'import_queue',
-    api_level   => 1,
-    argc        => 2,
-    stream      => 1,
-    signature   => {
-        desc => q/Only import records that have no collisions/
-    },
-    max_chunk_size => 0,
-    record_type => 'auth'
-);
 sub import_queue {
     my($self, $conn, $auth, $q_id, $options) = @_;
     my $e = new_editor(authtoken => $auth, xact => 1);
@@ -810,16 +792,31 @@ sub import_queue {
     my $type = $self->{record_type};
     my $class = ($type eq 'bib') ? 'vqbr' : 'vqar';
 
+    # First, collect the not-yet-imported records
     my $query = {queue => $q_id, import_time => undef};
+    my $search = ($type eq 'bib') ? 
+        'search_vandelay_queued_bib_record' : 
+        'search_vandelay_queued_authority_record';
+    my $rec_ids = $e->$search($query, {idlist => 1});
 
-    if($self->api_name =~ /nomatch/) {
-        my $matched_recs = queued_records_with_matches($e, $type, $q_id, undef, undef, {import_time => undef});
-        $query->{id} = {'not in' => $matched_recs} if @$matched_recs;
+    # Now add any imported records that have un-imported items
+
+    if($type eq 'bib') {
+        my $item_recs = $e->json_query({
+            select => {vqbr => ['id']},
+            from => {vqbr => 'vii'},
+            where => {
+                '+vqbr' => {
+                    queue => $q_id,
+                    import_time => {'!=' => undef}
+                },
+                '+vii' => {import_time => undef}
+            },
+            distinct => 1
+        });
+        push(@$rec_ids, map {$_->{id}} @$item_recs);
     }
 
-    my $search = ($type eq 'bib') ? 
-        'search_vandelay_queued_bib_record' : 'search_vandelay_queued_authority_record';
-    my $rec_ids = $e->$search($query, {idlist => 1});
     my $err = import_record_list_impl($self, $conn, $rec_ids, $e->requestor, $options);
     try {$e->rollback} otherwise {}; # only using this to make the read authoritative -- don't die from it
     return $err if $err;
@@ -828,6 +825,7 @@ sub import_queue {
 
 # returns a list of queued record IDs for a given queue that 
 # have at least one entry in the match table
+# XXX DEPRECATED?
 sub queued_records_with_matches {
     my($e, $type, $q_id, $limit, $offset, $filter) = @_;
 
@@ -943,13 +941,15 @@ sub import_record_list_impl {
             next;
         }
 
-        $$report_args{rec} = $rec;
-
         if($rec->import_time) {
+            # if the record is already imported, that means it may have 
+            # un-imported copies.  Add to success list for later processing.
+            push(@success_rec_ids, $rec_id);
             $e->rollback;
             next;
         }
 
+        $$report_args{rec} = $rec;
         $queues{$rec->queue} = 1;
 
         my $record;
@@ -1009,6 +1009,12 @@ sub import_record_list_impl {
                         if($res->{$auto_overlay_best_func} eq 't') {
                             $logger->info("vl: $type overlay-1match succeeded for queued rec $rec_id");
                             $imported = 1;
+
+                            # re-fetch the record to pick up the imported_as value from the DB
+                            $$report_args{rec} = $rec = $e->$retrieve_func([
+                                $rec_id, {flesh => 1, flesh_fields => {$rec_class => ['matches']}}]);
+
+
                         } else {
                             $$report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
                             $logger->info("vl: $type overlay-1match failed for queued rec $rec_id");
@@ -1041,6 +1047,11 @@ sub import_record_list_impl {
                     if($res->{$auto_overlay_func} eq 't') {
                         $logger->info("vl: $type auto-overlay succeeded for queued rec $rec_id");
                         $imported = 1;
+
+                        # re-fetch the record to pick up the imported_as value from the DB
+                        $$report_args{rec} = $rec = $e->$retrieve_func([
+                            $rec_id, {flesh => 1, flesh_fields => {$rec_class => ['matches']}}]);
+
                     } else {
                         $logger->info("vl: $type auto-overlay failed for queued rec $rec_id");
                     }
@@ -1071,6 +1082,11 @@ sub import_record_list_impl {
                     if($res->{$auto_overlay_best_func} eq 't') {
                         $logger->info("vl: $type auto-overlay-best succeeded for queued rec $rec_id");
                         $imported = 1;
+
+                        # re-fetch the record to pick up the imported_as value from the DB
+                        $$report_args{rec} = $rec = $e->$retrieve_func([
+                            $rec_id, {flesh => 1, flesh_fields => {$rec_class => ['matches']}}]);
+
                     } else {
                         $$report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
                         $logger->info("vl: $type auto-overlay-best failed for queued rec $rec_id");
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index d9a26a1..57e220b 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -1122,7 +1122,6 @@ function vlImportRecordQueue(type, queueId, recList, onload) {
     displayGlobalDiv('vl-generic-progress-with-total');
 
     /* set up options */
-    var mergeOpt = false;
     var options = {overlay_map : currentOverlayRecordsMap};
 
     if(vlUploadQueueImportNoMatch.checked) {
@@ -1133,21 +1132,18 @@ function vlImportRecordQueue(type, queueId, recList, onload) {
     if(vlUploadQueueAutoOverlayExact.checked) {
         options.auto_overlay_exact = true;
         vlUploadQueueAutoOverlayExact.checked = false;
-        mergeOpt = true;
     }
 
     if(vlUploadQueueAutoOverlayBestMatch.checked) {
         options.auto_overlay_best_match = true;
         vlUploadQueueAutoOverlayBestMatch.checked = false;
         options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
-        mergeOpt = true;
     }
 
     if(vlUploadQueueAutoOverlay1Match.checked) {
         options.auto_overlay_1match = true;
         vlUploadQueueAutoOverlay1Match.checked = false;
         options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
-        mergeOpt = true;
     }
 
     var profile = vlUploadMergeProfile.attr('value');
@@ -1161,13 +1157,6 @@ function vlImportRecordQueue(type, queueId, recList, onload) {
     if(type == 'auth')
         method = method.replace('bib', 'auth');
 
-    if(!mergeOpt) {
-        // in the interest of speed, if no merge options are 
-        // chosen, tell the back-end code to only process records
-        // that have no matches
-        method = method.replace(/\.import/, '.nomatch.import');
-    }
-
     var params = [authtoken, queueId, options];
     if(recList) {
         method = 'open-ils.vandelay.'+currentType+'_record.list.import';

commit 0612489eb8d325b30c4fa1bf7afa961cd9c45979
Author: Mike Rylander <mrylander at gmail.com>
Date:   Sat May 21 09:05:04 2011 -0400

    Spacing and code comments to keep future-miker from becoming confused again by the lack thereof
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index d88e3a4..9dd923e 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1383,6 +1383,7 @@ sub retrieve_queue_summary {
 
     if($type eq 'bib') {
         
+        # count of all items attached to records in the queue in question
         my $query = {
             select => {vii => [{alias => 'count', column => 'id', transform => 'count', aggregate => 1}]},
             from => 'vii',
@@ -1396,10 +1397,13 @@ sub retrieve_queue_summary {
                 }
             }
         };
-
         $summary->{total_items} = $e->json_query($query)->[0]->{count};
+
+        # count of items we attempted to import, but errored, attached to records in the queue in question
         $query->{where}->{import_error} = {'!=' => undef};
         $summary->{item_import_errors} = $e->json_query($query)->[0]->{count};
+
+        # count of items we successfully imported attached to records in the queue in question
         delete $query->{where}->{import_error};
         $query->{where}->{import_time} = {'!=' => undef};
         $summary->{total_items_imported} = $e->json_query($query)->[0]->{count};

commit bae1ec5dd89662529127eab766ba100afe53c618
Author: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>
Date:   Fri May 20 18:12:58 2011 -0400

    Show the "match score," a hopefully clearer term for match point quality,
    
    in the expression tree UI
    
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
index 92851d2..be2c79b 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
@@ -11,5 +11,6 @@
     "FAULTY_MARC": "A MARC tag must be identified by three digits, and the subfield must be one non-whitespace, non-control character.",
     "WORKING_MP_HERE": "Choose from among the three buttons above to add a new match point.",
     "WORKING_QM_HERE": "Use buttons above and to the right to add new quality metrics.",
-    "SVF": "Record Attribute"
+    "SVF": "Record Attribute",
+    "MATCH_SCORE": "Match score ${0}"
 }
diff --git a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
index 28f30b5..87656a3 100644
--- a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
@@ -339,19 +339,27 @@ function find_crad_by_name(name) {
 }
 
 function render_vmsp_label(point, minimal) {
-    /* "minimal" has this implication:
+    /* "minimal" has these implications:
      * for svf, only show the code, not the longer label.
+     * no quality display
      */
     if (point.bool_op()) {
         return point.bool_op();
     } else if (point.svf()) {
         return (openils.Util.isTrue(point.negate()) ? "NOT " : "") + (
             minimal ?  point.svf() :
-                (point.svf() + " / " + find_crad_by_name(point.svf()).label())
+                (point.svf() + " / " + find_crad_by_name(point.svf()).label()) +
+                " | " + dojo.string.substitute(
+                    localeStrings.MATCH_SCORE, [point.quality()]
+                )
         );
     } else {
         return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
-            point.tag() + " \u2021" + point.subfield();
+            point.tag() + " \u2021" + point.subfield() + (minimal ? "" : " | " +
+                dojo.string.substitute(
+                    localeStrings.MATCH_SCORE, [point.quality()]
+                )
+            );
     }
 }
 
diff --git a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
index 99367c0..cc5c788 100644
--- a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
@@ -61,7 +61,7 @@
         <td>
             <label for="quality-input"
                 title="A relative number representing the impact of this expression on the score of the overall record match"><!-- XXX tooltipize -->
-                Math Score
+                Match Score
             </label>
         </td>
         <td>

commit 3b250d8c958b209e0739eda15bb58b1456e2ff5a
Author: berick <berick at esilibrary.com>
Date:   Fri May 20 17:13:13 2011 -0400

    Add items-imported to VL queue summary
    
    Added total_items_imported value to queue summary API call.  Displaying
    value in vandelay queue summary area.

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 0647c88..d88e3a4 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1400,6 +1400,9 @@ sub retrieve_queue_summary {
         $summary->{total_items} = $e->json_query($query)->[0]->{count};
         $query->{where}->{import_error} = {'!=' => undef};
         $summary->{item_import_errors} = $e->json_query($query)->[0]->{count};
+        delete $query->{where}->{import_error};
+        $query->{where}->{import_time} = {'!=' => undef};
+        $summary->{total_items_imported} = $e->json_query($query)->[0]->{count};
     }
 
     return $summary;
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 1c03486..d9a26a1 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -1010,6 +1010,7 @@ var handleRetrieveRecords = function() {
             dojo.byId('vl-queue-summary-total-count').innerHTML = summary.total +'';
             dojo.byId('vl-queue-summary-import-count').innerHTML = summary.imported + '';
             dojo.byId('vl-queue-summary-import-item-count').innerHTML = summary.total_items + '';
+            dojo.byId('vl-queue-summary-import-item-imported-count').innerHTML = summary.total_items_imported + '';
             dojo.byId('vl-queue-summary-rec-error-count').innerHTML = summary.rec_import_errors + '';
             dojo.byId('vl-queue-summary-item-error-count').innerHTML = summary.item_import_errors + '';
         }
diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index c34c57f..783ffa5 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -28,6 +28,7 @@
                                 <tr><td>&vandelay.queue.imported;</td><td> <span style='font-weight:bold;' id='vl-queue-summary-import-count'/></td></tr>
                                 <tr><td>Record Import Failures</td><td> <span style='font-weight:bold;' id='vl-queue-summary-rec-error-count'/></td></tr>
                                 <tr><td>Items in Queue</td><td> <span style='font-weight:bold;' id='vl-queue-summary-import-item-count'/></td></tr>
+                                <tr><td>Items Imported</td><td> <span style='font-weight:bold;' id='vl-queue-summary-import-item-imported-count'/></td></tr>
                                 <tr><td>Item Import Failures</td><td> <span style='font-weight:bold;' id='vl-queue-summary-item-error-count'/></td></tr>
                             </tbody>
                         </table>

commit a56d95c3218ed1a4c78434430df3206e752c1a54
Author: berick <berick at esilibrary.com>
Date:   Fri May 20 17:06:05 2011 -0400

    Track import time and target copy on import items
    
    Added import_time and imported_as columns to vandelay.import_item
    
    For you branch trackers:
    
    ALTER TABLE vandelay.import_item
        ADD import_time TIMESTAMP WITH TIME ZONE;
    
    ALTER TABLE vandelay.import_item
        ADD imported_as BIGINT REFERENCES
        asset.copy (id) DEFERRABLE INITIALLY DEFERRED;

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 06cf384..27cdcd7 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -189,6 +189,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Attribute Definition" name="definition" reporter:datatype="link"/>
 			<field reporter:label="Import Error" name="import_error" reporter:datatype="link"/>
 			<field reporter:label="Import Error Detail" name="error_detail" reporter:datatype="text"/>
+			<field reporter:label="Final Target Copy" name="imported_as" reporter:datatype="link"/>
+			<field reporter:label="Import Time" name="import_time" reporter:datatype="timestamp"/>
 			<field reporter:label="Owning Library" name="owning_lib" reporter:datatype="int"/>
 			<field reporter:label="Circulating Library" name="circ_lib" reporter:datatype="int"/>
 			<field reporter:label="Call Number" name="call_number" reporter:datatype="text"/>
@@ -213,6 +215,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
 			<link field="record" reltype="has_a" key="id" map="" class="vqbr"/>
 			<link field="definition" reltype="has_a" key="id" map="" class="viiad"/>
+			<link field="imported_as" reltype="has_a" key="id" map="" class="acp"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index d461d4f..9a5c3ef 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -153,6 +153,8 @@ CREATE TABLE vandelay.import_item (
     definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	import_error	TEXT        REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	error_detail	TEXT,
+    imported_as     BIGINT      REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
+    import_time	    TIMESTAMP WITH TIME ZONE,
     owning_lib      INT,
     circ_lib        INT,
     call_number     TEXT,

commit 205675af130077e3fe74325983f22c68d74267a9
Author: berick <berick at esilibrary.com>
Date:   Fri May 20 13:45:52 2011 -0400

    more delineation of quality vs. score in match set config

diff --git a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
index d7bf224..99367c0 100644
--- a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
@@ -60,8 +60,8 @@
     <tr consistent-controls="1">
         <td>
             <label for="quality-input"
-                title="A relative number representing the impact of this expression on the quality of the overall record match"><!-- XXX tooltipize -->
-                Math Score Value
+                title="A relative number representing the impact of this expression on the score of the overall record match"><!-- XXX tooltipize -->
+                Math Score
             </label>
         </td>
         <td>

commit 780e59e5baa189e7c7fa01c41378cd51cbbfb834
Author: berick <berick at esilibrary.com>
Date:   Fri May 20 13:26:03 2011 -0400

    Match set point quality terminology change
    
    Change "Quality" in the context of a match set
    point to "Match Score Value" to avoid overriding the term "quality".

diff --git a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
index 8ad1eea..d7bf224 100644
--- a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
@@ -61,7 +61,7 @@
         <td>
             <label for="quality-input"
                 title="A relative number representing the impact of this expression on the quality of the overall record match"><!-- XXX tooltipize -->
-                Quality:
+                Math Score Value
             </label>
         </td>
         <td>

commit f2218fa2015df835718d4bba856c3365e84333f3
Author: berick <berick at esilibrary.com>
Date:   Fri May 20 13:23:08 2011 -0400

    Return to dijit.form.Button for VL upload form
    
    Otherwise the form is not correctly POSTed

diff --git a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2 b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
index 40aae32..b432252 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
@@ -88,7 +88,7 @@
             <td colspan='5'>
                 <span id="vl-file-label">&vandelay.file.to.upload;</span>
                 <input size='48' style='border:1px solid #888;' type="file" name="marc_upload"/>
-                <span style='margin-left:10px;'><button onclick="batchUpload()">&vandelay.upload;</button></span>
+                <span style='margin-left:10px;'><button dojoType="dijit.form.Button" onclick="batchUpload()">&vandelay.upload;</button></span>
             </td>
         </tr>
     </table>

commit f18a260e1f75c090e1a1567173c53d82fd651fb2
Author: berick <berick at esilibrary.com>
Date:   Fri May 20 13:20:40 2011 -0400

    show activity dialog on vandelay item export

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index e0a7eaf..1c03486 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -404,6 +404,7 @@ function vlExportInit() {
         if(!value) return;
         if(!confirm('Export as "' + value + '"?')) return; // TODO: i18n
 
+        displayGlobalDiv('vl-generic-progress');
         var method = 'open-ils.vandelay.import_item.queue.export.' + value + '.atomic';
 
         fieldmapper.standardRequest(
@@ -422,6 +423,7 @@ function vlExportInit() {
 }
 
 function exportHandler(type, response) {
+    displayGlobalDiv('vl-import-error-div');
     try {
         var content = openils.Util.readResponse(response);
         if (type=='email') {

commit 05c990e3c61a140b5036ea1b55f3274cd5cb04a4
Author: berick <berick at esilibrary.com>
Date:   Fri May 20 09:07:44 2011 -0400

    More Vandelay text and style cleanup
    
    * More consistent use of the word "match"
    * Make upload input more obvious w/ a border
    * Move some strings into DTD

diff --git a/Open-ILS/web/opac/locale/en-US/vandelay.dtd b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
index 7928620..68eb92e 100644
--- a/Open-ILS/web/opac/locale/en-US/vandelay.dtd
+++ b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
@@ -1,7 +1,8 @@
 <!ENTITY vandelay.add.existing.queue "or Add to an Existing Queue">
 <!ENTITY vandelay.auth.attrs "Authority attributes">
 <!ENTITY vandelay.auth.records "Authority Records">
-<!ENTITY vandelay.auto.import.noncolliding "Import Non-Colliding Records">
+<!ENTITY vandelay.import.actions "Record Import Actions">
+<!ENTITY vandelay.auto.import.noncolliding "Import Non-Matching Records">
 <!ENTITY vandelay.auto.import.auto_overlay_exact "Merge On Exact Match (901c)">
 <!ENTITY vandelay.auto.import.auto_overlay_1match "Merge On Single Match">
 <!ENTITY vandelay.auto.import.auto_overlay_best "Merge On Best Match">
diff --git a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2 b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
index f742104..40aae32 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
@@ -44,7 +44,9 @@
                     dojoType='dijit.form.FilteringSelect' labelAttr='source' searchAttr='source'/>
             </td>
         </tr>
-        <tr><td colspan='2' style='margin-top:10px;border-bottom:1px solid #888;border-top:1px solid #888'><b>Import Actions</b></td></tr>
+        <tr><td colspan='2' style='margin-top:10px;border-bottom:1px solid #888;border-top:2px solid #888'>
+            <b>&vandelay.import.actions;</b>
+        </td></tr>
         <tr>
             <td>&vandelay.auto.import.merge_profile;</td>
             <td colspan='4'>
@@ -80,13 +82,13 @@
                 <span style='padding-left: 10px; font-size:90%'>(&vandelay.auto.import.auto_overlay_best_ratio.desc;)</span>
             </td>
         </tr>
-
-        <tr><td colspan='2' style='border-bottom:1px solid #888'></td></tr>
+        <tr><td colspan='2' style='border-bottom:2px solid #888;'></td></tr>
+        <tr><td colspan='2' style='padding-bottom: 10px;'></td></tr>
         <tr>
             <td colspan='5'>
                 <span id="vl-file-label">&vandelay.file.to.upload;</span>
-                <input size='48' type="file" name="marc_upload"/>
-                <span style='margin-left:10px;'><button dojoType="dijit.form.Button" onclick="batchUpload()">&vandelay.upload;</button></span>
+                <input size='48' style='border:1px solid #888;' type="file" name="marc_upload"/>
+                <span style='margin-left:10px;'><button onclick="batchUpload()">&vandelay.upload;</button></span>
             </td>
         </tr>
     </table>

commit 70f893c2d049f17ae7ff35d86c2f48cae01e1503
Author: berick <berick at esilibrary.com>
Date:   Thu May 19 15:40:30 2011 -0400

    Flexible TCN match configuration
    
    If users do not want records with duplicate TCN values imported, users
    should rely on match-sets to enforce this policy instead of a hard-coded
    duplicate tcn block.  This is done by automatically calling the 'override'
    version of bib XML import.  The only tcn-related failure that can now
    occur is if no free, non-dupe TCN value can be extracted from the record.

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 65a0013..0647c88 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1089,14 +1089,16 @@ sub import_record_list_impl {
             
                 $logger->info("vl: creating new $type record for queued record $rec_id");
                 if($type eq 'bib') {
-                    $record = OpenILS::Application::Cat::BibCommon->biblio_record_xml_import($e, $rec->marc, $bib_sources{$rec->bib_source});
+                    $record = OpenILS::Application::Cat::BibCommon->biblio_record_xml_import(
+                        $e, $rec->marc, $bib_sources{$rec->bib_source}, undef, 1);
                 } else {
 
                     $record = OpenILS::Application::Cat::AuthCommon->import_authority_record($e, $rec->marc); #$source);
                 }
 
                 if($U->event_code($record)) {
-                    $$report_args{import_error} = 'import.duplicate.tcn' if $record->{textcode} eq 'TCN_EXISTS';
+                    $$report_args{import_error} = 'import.duplicate.tcn' 
+                        if $record->{textcode} eq 'OPEN_TCN_NOT_FOUND';
                     $$report_args{evt} = $record;
 
                 } else {

commit 12029c8f696863957ddbbdb784c4dda0a8e94f56
Author: berick <berick at esilibrary.com>
Date:   Thu May 19 15:26:49 2011 -0400

    Vandelay import improvements / bug fixes
    
     * Allow for quality ratio control during import-on-1match
     * Allow for 901c matching when no match-set is selected
     * Tidy up some display strings
     * Numerous small bug fixes

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 4a0bf99..65a0013 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -927,8 +927,8 @@ sub import_record_list_impl {
         $e->requestor($requestor);
 
         $$report_args{e} = $e;
-        $$report_args{import_error} = undef;
         $$report_args{evt} = undef;
+        $$report_args{import_error} = undef;
 
         my $rec = $e->$retrieve_func([
             $rec_id,
@@ -962,7 +962,7 @@ sub import_record_list_impl {
                 {
                     from => [
                         $overlay_func,
-                        $rec->id, 
+                        $rec_id,
                         $overlay_target, 
                         $merge_profile
                     ]
@@ -972,8 +972,8 @@ sub import_record_list_impl {
             if($res and ($res = $res->[0])) {
 
                 if($res->{$overlay_func} eq 't') {
-                    $logger->info("vl: $type direct overlay succeeded for queued rec " . 
-                        $rec->id . " and overlay target $overlay_target");
+                    $logger->info("vl: $type direct overlay succeeded for queued rec ".
+                        "$rec_id and overlay target $overlay_target");
                     $imported = 1;
                 }
 
@@ -984,34 +984,39 @@ sub import_record_list_impl {
 
         } else {
 
-            if($auto_overlay_1match) { 
-                # caller says to overlay if there is exactly 1 match
+            if($auto_overlay_1match) { # overlay if there is exactly 1 match
 
                 my %match_recs = map { $_->eg_record => 1 } @{$rec->matches};
 
                 if( scalar(keys %match_recs) == 1) { # all matches point to the same record
 
+                    # $auto_overlay_best_func will find the 1 match and 
+                    # overlay if the quality ratio allows it
+
                     my $res = $e->json_query(
                         {
                             from => [
-                                $overlay_func,
-                                $rec->id, 
-                                $rec->matches->[0]->eg_record,
-                                $merge_profile
+                                $auto_overlay_best_func,
+                                $rec_id, 
+                                $merge_profile,
+                                $match_quality_ratio
                             ]
                         }
                     );
 
                     if($res and ($res = $res->[0])) {
     
-                        if($res->{$overlay_func} eq 't') {
-                            $logger->info("vl: $type overlay-1match succeeded for queued rec " . $rec->id);
+                        if($res->{$auto_overlay_best_func} eq 't') {
+                            $logger->info("vl: $type overlay-1match succeeded for queued rec $rec_id");
                             $imported = 1;
+                        } else {
+                            $$report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
+                            $logger->info("vl: $type overlay-1match failed for queued rec $rec_id");
                         }
 
                     } else {
                         $error = 1;
-                        $logger->error("vl: Error attempting overlay with func=$overlay_func, profile=$merge_profile, record=$rec_id");
+                        $logger->error("vl: Error attempting overlay with func=$auto_overlay_best_func, profile=$merge_profile, record=$rec_id");
                     }
                 }
             }
@@ -1019,12 +1024,13 @@ sub import_record_list_impl {
             if(!$imported and !$error and $auto_overlay_exact and scalar(@{$rec->matches}) == 1 ) {
                 
                 # caller says to overlay if there is an /exact/ match
+                # $auto_overlay_func only proceeds and returns true on exact matches
 
                 my $res = $e->json_query(
                     {
                         from => [
                             $auto_overlay_func,
-                            $rec->id, 
+                            $rec_id,
                             $merge_profile
                         ]
                     }
@@ -1033,10 +1039,10 @@ sub import_record_list_impl {
                 if($res and ($res = $res->[0])) {
 
                     if($res->{$auto_overlay_func} eq 't') {
-                        $logger->info("vl: $type auto-overlay succeeded for queued rec " . $rec->id);
+                        $logger->info("vl: $type auto-overlay succeeded for queued rec $rec_id");
                         $imported = 1;
                     } else {
-                        $logger->info("vl: $type auto-overlay failed for queued rec " . $rec->id);
+                        $logger->info("vl: $type auto-overlay failed for queued rec $rec_id");
                     }
 
                 } else {
@@ -1053,7 +1059,7 @@ sub import_record_list_impl {
                     {
                         from => [
                             $auto_overlay_best_func,
-                            $rec->id, 
+                            $rec_id,
                             $merge_profile,
                             $match_quality_ratio
                         ]
@@ -1063,11 +1069,11 @@ sub import_record_list_impl {
                 if($res and ($res = $res->[0])) {
 
                     if($res->{$auto_overlay_best_func} eq 't') {
-                        $logger->info("vl: $type auto-overlay-best succeeded for queued rec " . $rec->id);
+                        $logger->info("vl: $type auto-overlay-best succeeded for queued rec $rec_id");
                         $imported = 1;
                     } else {
                         $$report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
-                        $logger->info("vl: $type auto-overlay-best failed for queued rec " . $rec->id);
+                        $logger->info("vl: $type auto-overlay-best failed for queued rec $rec_id");
                     }
 
                 } else {
@@ -1097,20 +1103,28 @@ sub import_record_list_impl {
 
                     $logger->info("vl: successfully imported new $type record");
                     $rec->imported_as($record->id);
-                    $rec->import_time('now');
-                    $rec->clear_import_error;
-                    $rec->clear_error_detail;
-                    $imported = 1 if $e->$update_func($rec);
                 }
             }
         }
 
         if($imported) {
-            push @success_rec_ids, $rec_id;
-            finish_rec_import_attempt($report_args);
 
-        } else {
-            # Send an update whenever there's an error
+            $rec->import_time('now');
+            $rec->clear_import_error;
+            $rec->clear_error_detail;
+
+            if($e->$update_func($rec)) {
+
+                push @success_rec_ids, $rec_id;
+                finish_rec_import_attempt($report_args);
+
+            } else {
+                $imported = 0;
+            }
+        }
+
+        if(!$imported) {
+            $logger->info("vl: record $rec_id was not imported");
             $$report_args{evt} = $e->event unless $$report_args{evt};
             finish_rec_import_attempt($report_args);
         }
@@ -1137,7 +1151,7 @@ sub import_record_list_impl {
     }
 
     # import the copies
-    import_record_asset_list_impl($conn, \@success_rec_ids, $requestor);
+    import_record_asset_list_impl($conn, \@success_rec_ids, $requestor) if @success_rec_ids;
 
     $conn->respond({total => $$report_args{total}, progress => $$report_args{progress}});
     return undef;
@@ -1402,7 +1416,10 @@ sub import_record_asset_list_impl {
     $rec_ids = $roe->json_query({
         select => {vqbr => ['id']},
         from => {vqbr => 'vii'},
-        where => {'+vqbr' => {import_time => {'!=' => undef}}},
+        where => {'+vqbr' => {
+            id => $rec_ids,
+            import_time => {'!=' => undef}
+        }},
         distinct => 1
     });
     $rec_ids = [map {$_->{id}} @$rec_ids];
@@ -1555,7 +1572,9 @@ sub respond_with_status {
 
     if($$args{report_all} or ($$args{progress} % $$args{step}) == 0) {
         $$args{conn}->respond({
-            map { $_ => $args->{$_} } qw/total progress success_count/,
+            total => $$args{total},
+            progress => $$args{progress},
+            success_count => $$args{success_count},
             err_event => $evt
         });
         $$args{step} *= 2 unless $$args{step} == 256;
diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index b136e8e..d461d4f 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -677,9 +677,9 @@ $$ LANGUAGE PLPGSQL;
 CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
 DECLARE
     incoming_existing_id    TEXT;
-    my_bib_queue            vandelay.bib_queue%ROWTYPE;
     test_result             vandelay.match_set_test_result%ROWTYPE;
     tmp_rec                 BIGINT;
+    match_set               INT;
 BEGIN
     IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
         RETURN NEW;
@@ -687,14 +687,12 @@ BEGIN
 
     DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
 
-    SELECT * INTO my_bib_queue FROM vandelay.bib_queue WHERE id = NEW.queue;
+    SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
 
-    IF my_bib_queue.match_set IS NULL THEN
-        RETURN NEW;
+    IF match_set IS NOT NULL THEN
+        NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
     END IF;
 
-    NEW.quality := vandelay.measure_record_quality( NEW.marc, my_bib_queue.match_set );
-
     -- Perfect matches on 901$c exit early with a match with high quality.
     incoming_existing_id :=
         oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
@@ -702,22 +700,33 @@ BEGIN
     IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
         SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
         IF tmp_rec IS NOT NULL THEN
-            INSERT INTO vandelay.bib_match (queued_record, eg_record, quality) VALUES ( NEW.id, incoming_existing_id::bigint, 9999);
-            RETURN NEW;
+            INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality) 
+                SELECT
+                    NEW.id, 
+                    b.id,
+                    9999,
+                    -- note: no match_set means quality==0
+                    vandelay.measure_record_quality( b.marc, match_set )
+                FROM biblio.record_entry b
+                WHERE id = incoming_existing_id::bigint;
         END IF;
     END IF;
 
+    IF match_set IS NULL THEN
+        RETURN NEW;
+    END IF;
 
     FOR test_result IN SELECT * FROM
-        vandelay.match_set_test_marcxml(my_bib_queue.match_set, NEW.marc) LOOP
+        vandelay.match_set_test_marcxml(match_set, NEW.marc) LOOP
 
         INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
-	SELECT  NEW.id,
-	        test_result.record,
+            SELECT  
+                NEW.id,
+                test_result.record,
                 test_result.quality,
-		vandelay.measure_record_quality( b.marc, my_bib_queue.match_set )
-	  FROM  biblio.record_entry b
-	  WHERE id = test_result.record;
+                vandelay.measure_record_quality( b.marc, match_set )
+	        FROM  biblio.record_entry b
+	        WHERE id = test_result.record;
 
     END LOOP;
 
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index b67352a..e0a7eaf 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -1143,6 +1143,7 @@ function vlImportRecordQueue(type, queueId, recList, onload) {
     if(vlUploadQueueAutoOverlay1Match.checked) {
         options.auto_overlay_1match = true;
         vlUploadQueueAutoOverlay1Match.checked = false;
+        options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
         mergeOpt = true;
     }
 
diff --git a/Open-ILS/web/opac/locale/en-US/vandelay.dtd b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
index d2d1789..7928620 100644
--- a/Open-ILS/web/opac/locale/en-US/vandelay.dtd
+++ b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
@@ -2,12 +2,12 @@
 <!ENTITY vandelay.auth.attrs "Authority attributes">
 <!ENTITY vandelay.auth.records "Authority Records">
 <!ENTITY vandelay.auto.import.noncolliding "Import Non-Colliding Records">
-<!ENTITY vandelay.auto.import.auto_overlay_exact "Merge/Overlay Exact Matches (901c)">
-<!ENTITY vandelay.auto.import.auto_overlay_1match "Merge/Overlay Single Matches">
-<!ENTITY vandelay.auto.import.auto_overlay_best "Merge/Overlay Best Match">
-<!ENTITY vandelay.auto.import.auto_overlay_best_ratio "Best Match Minimum Quality Ratio">
+<!ENTITY vandelay.auto.import.auto_overlay_exact "Merge On Exact Match (901c)">
+<!ENTITY vandelay.auto.import.auto_overlay_1match "Merge On Single Match">
+<!ENTITY vandelay.auto.import.auto_overlay_best "Merge On Best Match">
+<!ENTITY vandelay.auto.import.auto_overlay_best_ratio "Best/Single Match Minimum Quality Ratio">
 <!ENTITY vandelay.auto.import.auto_overlay_best_ratio.desc "New Record Quaility / Quality of Best Match">
-<!ENTITY vandelay.auto.import.merge_profile "Merge/Overlay Profile">
+<!ENTITY vandelay.auto.import.merge_profile "Merge Profile">
 <!ENTITY vandelay.auto.width "Auto Width">
 <!ENTITY vandelay.back.to.import.queue "Back To Import Queue">
 <!ENTITY vandelay.bib.attrs "Bibliographic attributes">
@@ -52,8 +52,8 @@
 <!ENTITY vandelay.marc.record "MARC Record">
 <!ENTITY vandelay.matches "Matches">
 <!ENTITY vandelay.next.page "Next &#187;">
-<!ENTITY vandelay.overlay.selected.record "Overlay selected record with imported record">
-<!ENTITY vandelay.overlay.target "Overlay Target">
+<!ENTITY vandelay.overlay.selected.record "Merge selected record with imported record">
+<!ENTITY vandelay.overlay.target "Merge Target">
 <!ENTITY vandelay.page "Page">
 <!ENTITY vandelay.powered.by.evergreen "Powered by Evergreen!">
 <!ENTITY vandelay.prev.page "&#171; Previous">

commit 7f913b210b523d68dd4bb7395b37dd5f824e1e22
Author: berick <berick at esilibrary.com>
Date:   Thu May 19 12:37:52 2011 -0400

    Clean up match_set data on delete
    
    Delete via cascade match_set_point's and match_set_quality entries
    for a given match_set on delete

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index d6810f6..b136e8e 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -15,7 +15,7 @@ CREATE TABLE vandelay.match_set (
 -- Table to define match points, either FF via SVF or tag+subfield
 CREATE TABLE vandelay.match_set_point (
     id          SERIAL  PRIMARY KEY,
-    match_set   INT     REFERENCES vandelay.match_set (id),
+    match_set   INT     REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
     parent      INT     REFERENCES vandelay.match_set_point (id),
     bool_op     TEXT    CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
     svf         TEXT    REFERENCES config.record_attr_definition (name),
@@ -33,7 +33,7 @@ CREATE TABLE vandelay.match_set_point (
 
 CREATE TABLE vandelay.match_set_quality (
     id          SERIAL  PRIMARY KEY,
-    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id),
+    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
     svf         TEXT    REFERENCES config.record_attr_definition,
     tag         TEXT,
     subfield    TEXT,

commit 0cf7d27b68cba7d10942db2ee8d8ae3ff8fc17ec
Author: berick <berick at esilibrary.com>
Date:   Thu May 19 10:23:36 2011 -0400

    Display org shortname in merge profile editor
    
    ...instead of org unit ID

diff --git a/Open-ILS/web/templates/default/vandelay/inc/profiles.tt2 b/Open-ILS/web/templates/default/vandelay/inc/profiles.tt2
index ca67f3a..0220d6a 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/profiles.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/profiles.tt2
@@ -19,5 +19,10 @@
             fmClass='vmp'
             showPaginator='true'
             editOnEnter='true'>
+        <thead>
+            <tr>
+                <th field='owner' get='vlGetOrg'/>
+            </tr>
+        </thead>
     </table>
 </div>

commit 10c443b05e002ba1cf3c917c228b77e312f35c59
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Sat May 14 13:44:56 2011 -0400

    return after Email sent alert

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index e78ebcc..b67352a 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -425,7 +425,7 @@ function exportHandler(type, response) {
     try {
         var content = openils.Util.readResponse(response);
         if (type=='email') {
-            if (content==1) { alert('Email sent.'); }
+            if (content==1) { alert('Email sent.'); return; }
             throw(content);
         }
         /* handle .atomic versus non-atomic method calls */

commit e9c64425a8df9e91c9f14051e87138edd5d84aeb
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Sat May 14 13:40:36 2011 -0400

    This function also gets used with authority queues, so don't assume import_items()

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index ea06944..e78ebcc 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -675,7 +675,10 @@ function vlGetViewErrors(rowIdx, item) {
         // id:rec_error:item_import_error_count
         return id + ':' + 
             (rec.import_error() ? 1 : '') + ':' + 
-            rec.import_items().filter(function(i) {return i.import_error()}).length;
+            (typeof rec.import_items == 'function'
+                ? rec.import_items().filter(function(i) {return i.import_error()}).length
+                :''
+            );
     }
     return -1
 }

commit c094b362e18e9fed3551c2c2fa9375dbb0627789
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Sat May 14 13:30:29 2011 -0400

    email template for import items export

diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
index 1b64567..5396747 100644
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -494,7 +494,79 @@ INSERT INTO action_trigger.environment ( event_def, path) VALUES (
     ,( 45, 'record.queue.owner')
 ;
 
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        46,
+        TRUE,
+        1,
+        'Email Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.email',
+        'NOOP_True',
+        'SendEmail',
+        'record.queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.record.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Import Items from Import Queue
+
+Queue ID: [% target.0.record.queue.id %]
+Queue Name: [% target.0.record.queue.name %]
+Queue Type: [% target.0.record.queue.queue_type %]
+Complete? [% target.0.record.queue.complete %]
+
+    [% FOR vii IN target %]
+=-=-=
+ Import Item ID         | [% vii.id %]
+ Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
+ ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
+ Attribute Definition   | [% vii.definition %]
+ Import Error           | [% vii.import_error %]
+ Import Error Detail    | [% vii.error_detail %]
+ Owning Library         | [% vii.owning_lib %]
+ Circulating Library    | [% vii.circ_lib %]
+ Call Number            | [% vii.call_number %]
+ Copy Number            | [% vii.copy_number %]
+ Status                 | [% vii.status.name %]
+ Shelving Location      | [% vii.location.name %]
+ Circulate              | [% vii.circulate %]
+ Deposit                | [% vii.deposit %]
+ Deposit Amount         | [% vii.deposit_amount %]
+ Reference              | [% vii.ref %]
+ Holdable               | [% vii.holdable %]
+ Price                  | [% vii.price %]
+ Barcode                | [% vii.barcode %]
+ Circulation Modifier   | [% vii.circ_modifier %]
+ Circulate As MARC Type | [% vii.circ_as_type %]
+ Alert Message          | [% vii.alert_message %]
+ Public Note            | [% vii.pub_note %]
+ Private Note           | [% vii.priv_note %]
+ OPAC Visible           | [% vii.opac_visible %]
+
+    [% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    46, 'record')
+    ,( 46, 'record.attributes')
+    ,( 46, 'record.queue')
+    ,( 46, 'record.queue.owner')
+;
 
 COMMIT;
 
--- BEGIN; DELETE FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40,41,42,43,44,45); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40,41,42,43,44,45); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45))); DELETE FROM config.upgrade_log WHERE version = 'test'; COMMIT;
+-- BEGIN; DELETE FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45,46); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40,41,42,43,44,45,46); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40,41,42,43,44,45,46); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45,46))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45,46))); DELETE FROM config.upgrade_log WHERE version = 'test'; COMMIT;

commit 40a118a3ddf858ad88f5696406b6b3abac9dcc0f
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Sat May 14 13:21:02 2011 -0400

    tweak formatting for CSV export templates, and add headers

diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
index b4f7b3b..1b64567 100644
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -183,7 +183,10 @@ INSERT INTO action_trigger.event_definition (
         'queue.owner',
         'print-on-demand',
 $$
-[%- USE date -%][%- FOR vqbr IN target -%]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.
 get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"[%- END -%]
+[%- USE date -%]
+"Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
+[% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_att
 r('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
+[% END %]
 $$
     )
 ;
@@ -322,7 +325,10 @@ INSERT INTO action_trigger.event_definition (
         'queue.owner',
         'print-on-demand',
 $$
-[%- USE date -%][%- FOR vqar IN target -%]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"[%- END -%]
+[%- USE date -%]
+"Record Identifier"
+[% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
+[% END %]
 $$
     )
 ;
@@ -473,7 +479,9 @@ INSERT INTO action_trigger.event_definition (
         'record.queue.owner',
         'print-on-demand',
 $$
-[%- USE date -%][% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %
 ]","[% vii.circ_as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
+[%- USE date -%]
+"Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
+[% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_
 as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
 [% END %]
 $$
     )

commit 98d5a7532ec67ce24c385648ba4cc8a24fdefb26
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Sat May 14 13:15:15 2011 -0400

    CSV template for import items export

diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
index 92340b1..b4f7b3b 100644
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -451,6 +451,42 @@ INSERT INTO action_trigger.environment ( event_def, path) VALUES (
     ,( 44, 'record.queue.owner')
 ;
 
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        45,
+        TRUE,
+        1,
+        'CSV Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'record.queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%][% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %
 ]","[% vii.circ_as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    45, 'record')
+    ,( 45, 'record.attributes')
+    ,( 45, 'record.queue')
+    ,( 45, 'record.queue.owner')
+;
+
+
 COMMIT;
 
--- BEGIN; DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44))); DELETE FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40,41,42,43,44); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40,41,42,43,44); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test'; COMMIT;
+-- BEGIN; DELETE FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40,41,42,43,44,45); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40,41,42,43,44,45); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44,45))); DELETE FROM config.upgrade_log WHERE version = 'test'; COMMIT;

commit a65d906c7591a7c1d6312d3772b44272f61627b2
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Sat May 14 12:58:31 2011 -0400

    robustify exportHandler, assume response of 1 is success for email methods, and make import_item export methods atomic

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 7c96ed5..ea06944 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -404,7 +404,7 @@ function vlExportInit() {
         if(!value) return;
         if(!confirm('Export as "' + value + '"?')) return; // TODO: i18n
 
-        var method = 'open-ils.vandelay.import_item.queue.export.' + value;
+        var method = 'open-ils.vandelay.import_item.queue.export.' + value + '.atomic';
 
         fieldmapper.standardRequest(
             ['open-ils.vandelay', method],
@@ -425,10 +425,13 @@ function exportHandler(type, response) {
     try {
         var content = openils.Util.readResponse(response);
         if (type=='email') {
-            if (content) { throw(content); }
-            return;
+            if (content==1) { alert('Email sent.'); }
+            throw(content);
         }
-        content = content[0].template_output().data();
+        /* handle .atomic versus non-atomic method calls */
+        content = content.constructor == Array
+            ? content[0].template_output().data()
+            : content.template_output().data();
         switch(type) {
             case 'print':
                 openils.Util.printHtmlString(content);

commit bd61d29819665315ba2f8ac2de9e162e864dabd0
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Thu May 12 23:18:57 2011 -0400

    toward Import Item templates for export.  May want to consider providing IDL links for some of these fields so we can flesh them in the environment

diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
index fbbda48..92340b1 100644
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -381,6 +381,76 @@ INSERT INTO action_trigger.environment ( event_def, path) VALUES (
     ,( 43, 'queue.owner')
 ;
 
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        44,
+        TRUE,
+        1,
+        'Print Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'record.queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.record.queue.id %]
+Queue Name: [% target.0.record.queue.name %]
+Queue Type: [% target.0.record.queue.queue_type %]
+Complete? [% target.0.record.queue.complete %]
+
+    [% FOR vii IN target %]
+=-=-=
+ Import Item ID         | [% vii.id %]
+ Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
+ ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
+ Attribute Definition   | [% vii.definition %]
+ Import Error           | [% vii.import_error %]
+ Import Error Detail    | [% vii.error_detail %]
+ Owning Library         | [% vii.owning_lib %]
+ Circulating Library    | [% vii.circ_lib %]
+ Call Number            | [% vii.call_number %]
+ Copy Number            | [% vii.copy_number %]
+ Status                 | [% vii.status.name %]
+ Shelving Location      | [% vii.location.name %]
+ Circulate              | [% vii.circulate %]
+ Deposit                | [% vii.deposit %]
+ Deposit Amount         | [% vii.deposit_amount %]
+ Reference              | [% vii.ref %]
+ Holdable               | [% vii.holdable %]
+ Price                  | [% vii.price %]
+ Barcode                | [% vii.barcode %]
+ Circulation Modifier   | [% vii.circ_modifier %]
+ Circulate As MARC Type | [% vii.circ_as_type %]
+ Alert Message          | [% vii.alert_message %]
+ Public Note            | [% vii.pub_note %]
+ Private Note           | [% vii.priv_note %]
+ OPAC Visible           | [% vii.opac_visible %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    44, 'record')
+    ,( 44, 'record.attributes')
+    ,( 44, 'record.queue')
+    ,( 44, 'record.queue.owner')
+;
+
 COMMIT;
 
--- DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43))); DELETE FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40,41,42,43); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40,41,42,43); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test';
+-- BEGIN; DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44))); DELETE FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43,44); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40,41,42,43,44); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40,41,42,43,44); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test'; COMMIT;

commit 6e2fb189b6c199f0ec08662352803044b228abe7
Author: Mike Rylander <mrylander at gmail.com>
Date:   Thu May 12 23:05:35 2011 -0400

    Remove excised field from IDL

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 3f93a59..06cf384 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -511,14 +511,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.authority_match_id_seq">
 			<field reporter:label="Match ID" name="id" reporter:datatype="id"/>
 			<field reporter:label="Queued Record" name="queued_record" reporter:datatype="link"/>
-			<field reporter:label="Matched Attribute" name="matched_attr" reporter:datatype="link"/>
 			<field reporter:label="Evergreen Record" name="eg_record" reporter:datatype="link"/>
 			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 		</fields>
 		<links>
 			<link field="queued_record" reltype="has_a" key="id" map="" class="vqbr"/>
 			<link field="eg_record" reltype="has_a" key="id" map="" class="bre"/>
-			<link field="matched_attr" reltype="has_a" key="id" map="" class="vqbra"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>

commit 78366c9c9a039c722fd5b1187a6a9d431f3a61e0
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Thu May 12 22:35:22 2011 -0400

    fix variable name for Import Items Export

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 46b063f..7c96ed5 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -415,7 +415,7 @@ function vlExportInit() {
                     {with_import_error: (vlImportItemsShowErrors.checked) ? 1 : null}
                 ],
                 async : true,
-                oncomplete : function(r) {exportHandler(type, r)}
+                oncomplete : function(r) {exportHandler(value, r)}
             }
         );
     }

commit 93aef19351dcb1538c9a4ddf2d5bfcbd37284015
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Thu May 12 21:42:43 2011 -0400

    change the group field for these templates, though they weren't breaking anything before (these are called with fire_object_event and instead of create_events_for_hook, if that makes a difference)

diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
index 0310127..fbbda48 100644
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -122,7 +122,7 @@ INSERT INTO action_trigger.event_definition (
         'vandelay.queued_bib_record.print',
         'NOOP_True',
         'ProcessTemplate',
-        'usr',
+        'queue.owner',
         'print-on-demand',
 $$
 [%- USE date -%]
@@ -180,7 +180,7 @@ INSERT INTO action_trigger.event_definition (
         'vandelay.queued_bib_record.csv',
         'NOOP_True',
         'ProcessTemplate',
-        'usr',
+        'queue.owner',
         'print-on-demand',
 $$
 [%- USE date -%][%- FOR vqbr IN target -%]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.
 get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"[%- END -%]
@@ -275,7 +275,7 @@ INSERT INTO action_trigger.event_definition (
         'vandelay.queued_auth_record.print',
         'NOOP_True',
         'ProcessTemplate',
-        'usr',
+        'queue.owner',
         'print-on-demand',
 $$
 [%- USE date -%]
@@ -319,7 +319,7 @@ INSERT INTO action_trigger.event_definition (
         'vandelay.queued_auth_record.csv',
         'NOOP_True',
         'ProcessTemplate',
-        'usr',
+        'queue.owner',
         'print-on-demand',
 $$
 [%- USE date -%][%- FOR vqar IN target -%]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"[%- END -%]

commit 6162208b18d9351870770648d70e68575d7d68a2
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Thu May 12 21:38:54 2011 -0400

    templates for exporting authority queues, but needs testing.  blockers here include importing authority records being broken, and possibly selection of an authority queue to inspect being broken

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
index 6442531..cfeeca6 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
@@ -173,6 +173,32 @@ my $_TT_helpers = {
         }
         return;
     },
+
+    get_queued_auth_attr => sub {
+        my $name = shift or return;     # the first arg is always the name
+        my ($attr) = @_;
+        # use Data::Dumper; $logger->warn("get_queued_auth_attr: " . Dumper($attr));
+        ($name and @$attr) or return;
+
+        my $query = {
+            select => {'vqarad' => ['id']},
+            from => 'vqarad',
+            where => {code => $name}
+        };
+
+        my $def_ids = new_editor()->json_query($query);
+        @$def_ids or return;
+
+        my $length;
+        $name =~ s/^(\D+)_(\d+)$/$1/ and $length = $2;
+        foreach (@$attr) {
+            $_->field eq @{$def_ids}[0]->{id} or next;
+            next if $length and $length != length($_->attr_value);
+            return $_->attr_value;
+        }
+        return;
+    },
+
 };
 
 
diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
index 6e59409..0310127 100644
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -255,6 +255,132 @@ INSERT INTO action_trigger.environment ( event_def, path) VALUES (
     ,( 40, 'queue')
     ,( 40, 'queue.owner')
 ;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        41,
+        TRUE,
+        1,
+        'Print Output for Queued Authority Records',
+        'vandelay.queued_auth_record.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'usr',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqar IN target %]
+=-=-=
+ Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    41, 'attributes')
+    ,( 41, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        42,
+        TRUE,
+        1,
+        'CSV Output for Queued Authority Records',
+        'vandelay.queued_auth_record.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'usr',
+        'print-on-demand',
+$$
+[%- USE date -%][%- FOR vqar IN target -%]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"[%- END -%]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    42, 'attributes')
+    ,( 42, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        43,
+        TRUE,
+        1,
+        'Email Output for Queued Authority Records',
+        'vandelay.queued_auth_record.email',
+        'NOOP_True',
+        'SendEmail',
+        'queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Authorities from Import Queue
+
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqar IN target %]
+=-=-=
+ Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
+
+    [% END %]
+
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    43, 'attributes')
+    ,( 43, 'queue')
+    ,( 43, 'queue.owner')
+;
+
 COMMIT;
 
--- DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40))); DELETE FROM action_trigger.event WHERE event_def IN (38,39,40); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test';
+-- DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43))); DELETE FROM action_trigger.event WHERE event_def IN (38,39,40,41,42,43); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40,41,42,43); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40,41,42,43); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test';

commit eb4fbe43bb76aefc210bb92137996474887da0bf
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Thu May 12 19:50:08 2011 -0400

    email template for queued bib records

diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
index 4560389..6e59409 100644
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -193,6 +193,68 @@ INSERT INTO action_trigger.environment ( event_def, path) VALUES (
     ,( 39, 'queue')
 ;
 
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        40,
+        TRUE,
+        1,
+        'Email Output for Queued Bib Records',
+        'vandelay.queued_bib_record.email',
+        'NOOP_True',
+        'SendEmail',
+        'queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root at localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Bibs from Import Queue
+
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqbr IN target %]
+=-=-=
+ Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
+ Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
+ Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
+ Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
+ ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
+ ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
+ Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
+ Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
+ TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
+ TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
+ Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
+ Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
+ Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
+ Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
+ Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
+
+    [% END %]
+
+$$
+    )
+;
 
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    40, 'attributes')
+    ,( 40, 'queue')
+    ,( 40, 'queue.owner')
+;
 COMMIT;
--- DELETE FROM action_trigger.event WHERE event_def IN (38,39); DELETE FROM action_trigger.environment WHERE event_def IN (38,39); DELETE FROM action_trigger.event_definition WHERE id IN (38,39); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test';
+
+-- DELETE FROM action_trigger.event_output WHERE id IN ((SELECT template_output FROM action_trigger.event WHERE event_def IN (38,39,40))UNION(SELECT error_output FROM action_trigger.event WHERE event_def IN (38,39,40))); DELETE FROM action_trigger.event WHERE event_def IN (38,39,40); DELETE FROM action_trigger.environment WHERE event_def IN (38,39,40); DELETE FROM action_trigger.event_definition WHERE id IN (38,39,40); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test';

commit 9cc978be2ed4741410ce4d95913f4cf53cb93ba7
Author: senator <lebbeous at esilibrary.com>
Date:   Wed May 11 11:44:02 2011 -0400

    In the event that a bib queue is not using a match_set, bail out of...
    
    ... match_set_test_marcxml() early.  Needs tested.  Also may need
    special check for non-null but empty match_sets.

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 2009df6..d6810f6 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -689,6 +689,10 @@ BEGIN
 
     SELECT * INTO my_bib_queue FROM vandelay.bib_queue WHERE id = NEW.queue;
 
+    IF my_bib_queue.match_set IS NULL THEN
+        RETURN NEW;
+    END IF;
+
     NEW.quality := vandelay.measure_record_quality( NEW.marc, my_bib_queue.match_set );
 
     -- Perfect matches on 901$c exit early with a match with high quality.

commit 72522570c89283573ebf914a604c12c016761208
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Wed May 11 02:42:30 2011 -0400

    CSV template and file saving worked out

diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
index 42e8aec..4560389 100644
--- a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -161,5 +161,38 @@ INSERT INTO action_trigger.environment ( event_def, path) VALUES (
     ,( 38, 'queue')
 ;
 
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        39,
+        TRUE,
+        1,
+        'CSV Output for Queued Bib Records',
+        'vandelay.queued_bib_record.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'usr',
+        'print-on-demand',
+$$
+[%- USE date -%][%- FOR vqbr IN target -%]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.
 get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"[%- END -%]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    39, 'attributes')
+    ,( 39, 'queue')
+;
+
+
 COMMIT;
--- DELETE FROM action_trigger.event WHERE event_def IN (38); DELETE FROM action_trigger.environment WHERE event_def IN (38); DELETE FROM action_trigger.event_definition WHERE id IN (38); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test';
+-- DELETE FROM action_trigger.event WHERE event_def IN (38,39); DELETE FROM action_trigger.environment WHERE event_def IN (38,39); DELETE FROM action_trigger.event_definition WHERE id IN (38,39); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test';
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 9565869..46b063f 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -423,10 +423,22 @@ function vlExportInit() {
 
 function exportHandler(type, response) {
     try {
-        var content = openils.Util.readResponse(response)[0].template_output().data();
+        var content = openils.Util.readResponse(response);
+        if (type=='email') {
+            if (content) { throw(content); }
+            return;
+        }
+        content = content[0].template_output().data();
         switch(type) {
-            case 'print': openils.Util.printHtmlString(content); break;
-            default: alert('response = ' + response + ' content:\n' + content);
+            case 'print':
+                openils.Util.printHtmlString(content);
+            break;
+            case 'csv':
+                //content = content.replace(/\\t/g,'\t'); // if we really wanted to do .tsv instead
+                openils.XUL.contentToFileSaveDialog(content);
+            break;
+            default:
+                alert('response = ' + response + '\tcontent:\n' + content);
         }
     } catch(E) {
         alert('Error exporting data: ' + E);

commit 0853fdbf76f0ceadc77be39c7a5961dde409d756
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Wed May 11 01:39:32 2011 -0400

    first template and supporting code, printing queued bib records

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
index 4251519..6442531 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
@@ -148,6 +148,31 @@ my $_TT_helpers = {
         }
         return;
     },
+
+    get_queued_bib_attr => sub {
+        my $name = shift or return;     # the first arg is always the name
+        my ($attr) = @_;
+        # use Data::Dumper; $logger->warn("get_queued_bib_attr: " . Dumper($attr));
+        ($name and @$attr) or return;
+
+        my $query = {
+            select => {'vqbrad' => ['id']},
+            from => 'vqbrad',
+            where => {code => $name}
+        };
+
+        my $def_ids = new_editor()->json_query($query);
+        @$def_ids or return;
+
+        my $length;
+        $name =~ s/^(\D+)_(\d+)$/$1/ and $length = $2;
+        foreach (@$attr) {
+            $_->field eq @{$def_ids}[0]->{id} or next;
+            next if $length and $length != length($_->attr_value);
+            return $_->attr_value;
+        }
+        return;
+    },
 };
 
 
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 4134e83..4a0bf99 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -517,15 +517,18 @@ sub retrieve_queued_records {
 
     my $retrieve = ($type eq 'bib') ? 
         'retrieve_vandelay_queued_bib_record' : 'retrieve_vandelay_queued_authority_record';
+    my $search = ($type eq 'bib') ? 
+        'search_vandelay_queued_bib_record' : 'search_vandelay_queued_authority_record';
 
     if ($self->api_name =~ /export/) {
+        my $rec_list = $e->$search({id => [map { $_->{id} } @$record_ids]});
         if ($self->api_name =~ /print/) {
 
             $e->rollback;
             return $U->fire_object_event(
                 undef,
                 'vandelay.queued_'.$type.'_record.print',
-                [map {$_->{id}} @$record_ids],
+                $rec_list,
                 $e->requestor->ws_ou
             );
 
@@ -535,7 +538,7 @@ sub retrieve_queued_records {
             return $U->fire_object_event(
                 undef,
                 'vandelay.queued_'.$type.'_record.csv',
-                [map {$_->{id}} @$record_ids],
+                $rec_list,
                 $e->requestor->ws_ou
             );
 
@@ -543,10 +546,10 @@ sub retrieve_queued_records {
 
             $conn->respond_complete(1);
 
-            for my $rec_id (@$record_ids) {
+            for my $rec (@$rec_list) {
                 $U->create_events_for_hook(
                     'vandelay.queued_'.$type.'_record.email',
-                    $rec_id->{id},
+                    $rec,
                     $e->requestor->home_ou,
                     undef,
                     undef,
@@ -665,13 +668,14 @@ sub retrieve_queue_import_items {
         if $$options{with_import_error};
 
     my $items = $e->json_query($query);
+    my $item_list = $e->search_vandelay_import_item({id => [map { $_->{id} } @$items]});
     if ($self->api_name =~ /export/) {
         if ($self->api_name =~ /print/) {
 
             return $U->fire_object_event(
                 undef,
                 'vandelay.import_items.print',
-                [map {$_->{id}} @$items],
+                $item_list,
                 $e->requestor->ws_ou
             );
 
@@ -680,7 +684,7 @@ sub retrieve_queue_import_items {
             return $U->fire_object_event(
                 undef,
                 'vandelay.import_items.csv',
-                [map {$_->{id}} @$items],
+                $item_list,
                 $e->requestor->ws_ou
             );
 
@@ -688,10 +692,10 @@ sub retrieve_queue_import_items {
 
             $conn->respond_complete(1);
 
-            for my $item (@$items) {
+            for my $item (@$item_list) {
                 $U->create_events_for_hook(
                     'vandelay.import_items.email',
-                    $item->{id},
+                    $item,
                     $e->requestor->home_ou,
                     undef,
                     undef,
@@ -701,8 +705,8 @@ sub retrieve_queue_import_items {
 
         }
     } else {
-        for my $item (@$items) {
-            $conn->respond($e->retrieve_vandelay_import_item($item->{id}));
+        for my $item (@$item_list) {
+            $conn->respond($item);
         }
     }
 
diff --git a/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
new file mode 100644
index 0000000..42e8aec
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/renumber_me.sql
@@ -0,0 +1,165 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('test'); -- phasefx
+
+INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
+        'vandelay.queued_bib_record.print',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.print',
+            'Print output has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_bib_record.csv',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.csv',
+            'CSV output has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_bib_record.email',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.email',
+            'An email has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.print',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.print',
+            'Print output has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.csv',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.csv',
+            'CSV output has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.email',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.email',
+            'An email has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.print',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.print',
+            'Print output has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.csv',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.csv',
+            'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.email',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.email',
+            'An email has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        38,
+        TRUE,
+        1,
+        'Print Output for Queued Bib Records',
+        'vandelay.queued_bib_record.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'usr',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqbr IN target %]
+=-=-=
+ Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
+ Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
+ Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
+ Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
+ ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
+ ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
+ Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
+ Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
+ TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
+ TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
+ Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
+ Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
+ Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
+ Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
+ Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    38, 'attributes')
+    ,( 38, 'queue')
+;
+
+COMMIT;
+-- DELETE FROM action_trigger.event WHERE event_def IN (38); DELETE FROM action_trigger.environment WHERE event_def IN (38); DELETE FROM action_trigger.event_definition WHERE id IN (38); DELETE FROM action_trigger.hook WHERE key IN ('vandelay.queued_bib_record.print','vandelay.queued_bib_record.csv','vandelay.queued_bib_record.email','vandelay.queued_auth_record.print','vandelay.queued_auth_record.csv','vandelay.queued_auth_record.email','vandelay.import_items.print','vandelay.import_items.csv','vandelay.import_items.email'); DELETE FROM config.upgrade_log WHERE version = 'test';
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index bc9a153..9565869 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -422,11 +422,15 @@ function vlExportInit() {
 }
 
 function exportHandler(type, response) {
-    var content = openils.Util.readResponse(response);
-    if(type == 'print') {
-        // TODO print the content
+    try {
+        var content = openils.Util.readResponse(response)[0].template_output().data();
+        switch(type) {
+            case 'print': openils.Util.printHtmlString(content); break;
+            default: alert('response = ' + response + ' content:\n' + content);
+        }
+    } catch(E) {
+        alert('Error exporting data: ' + E);
     }
-    alert('response = ' + response);
 }
 
 function retrieveQueuedRecords(type, queueId, onload, doExport) {
@@ -465,7 +469,7 @@ function retrieveQueuedRecords(type, queueId, onload, doExport) {
         {   async: true,
             params: params,
             oncomplete: function(r){
-                if(doExport) return onload();
+                if(doExport) return onload(r);
                 var recs = r.recv().content();
                 if(e = openils.Event.parse(recs[0]))
                     return alert(e);

commit 97f5bf78b56c673678404eec7c8961e49316cb35
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Tue May 10 13:18:18 2011 -0400

    remove .atomic. from these method names

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 3185f9b..4134e83 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -370,7 +370,7 @@ __PACKAGE__->register_method(
     record_type => 'bib'
 );
 __PACKAGE__->register_method(
-    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.atomic.export.print",
+    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.export.print",
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
@@ -378,7 +378,7 @@ __PACKAGE__->register_method(
     record_type => 'bib'
 );
 __PACKAGE__->register_method(
-    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.atomic.export.csv",
+    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.export.csv",
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
@@ -386,7 +386,7 @@ __PACKAGE__->register_method(
     record_type => 'bib'
 );
 __PACKAGE__->register_method(
-    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.atomic.export.email",
+    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.export.email",
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
@@ -403,7 +403,7 @@ __PACKAGE__->register_method(
     record_type => 'auth'
 );
 __PACKAGE__->register_method(
-    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.atomic.export.print",
+    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.export.print",
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
@@ -411,7 +411,7 @@ __PACKAGE__->register_method(
     record_type => 'auth'
 );
 __PACKAGE__->register_method(
-    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.atomic.export.csv",
+    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.export.csv",
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
@@ -419,7 +419,7 @@ __PACKAGE__->register_method(
     record_type => 'auth'
 );
 __PACKAGE__->register_method(
-    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.atomic.export.email",
+    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.export.email",
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,

commit d49ebdc0a9995bfcf7829cd3f5e0b0c2a88509cb
Author: berick <berick at esilibrary.com>
Date:   Tue May 10 10:54:37 2011 -0400

    add .atomic to the end of the queue export API calls where it belongs

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index d2405cc..bc9a153 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -441,12 +441,14 @@ function retrieveQueuedRecords(type, queueId, onload, doExport) {
     if(!queueId) queueId = currentQueueId;
     if(!onload) onload = handleRetrieveRecords;
 
-    var method = 'open-ils.vandelay.'+type+'_queue.records.retrieve.atomic';
+    var method = 'open-ils.vandelay.'+type+'_queue.records.retrieve';
 
     if(doExport) method += '.export.' + doExport;
     if(vlQueueGridShowMatches.checked)
         method = method.replace('records', 'records.matches');
 
+    method += '.atomic';
+
     var sel = dojo.byId('vl-queue-display-limit-selector');
     var limit = parseInt(sel.options[sel.selectedIndex].value);
     var offset = limit * parseInt(vlQueueDisplayPage.attr('value')-1);

commit 43e5b25daf7eecea247aebaf76fd2e2c722ab5af
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Tue May 10 00:29:56 2011 -0400

    wire in the event firing, though we still need the templates.  Put streaming back in for the print/csv/email export methods, since we can respond_complete for email and it to doesn't hurt the other two.

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 7745030..3185f9b 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -374,6 +374,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     record_type => 'bib'
 );
 __PACKAGE__->register_method(
@@ -381,6 +382,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     record_type => 'bib'
 );
 __PACKAGE__->register_method(
@@ -388,6 +390,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     record_type => 'bib'
 );
 
@@ -404,6 +407,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     record_type => 'auth'
 );
 __PACKAGE__->register_method(
@@ -411,6 +415,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     record_type => 'auth'
 );
 __PACKAGE__->register_method(
@@ -418,6 +423,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queued_records',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     record_type => 'auth'
 );
 
@@ -512,13 +518,52 @@ sub retrieve_queued_records {
     my $retrieve = ($type eq 'bib') ? 
         'retrieve_vandelay_queued_bib_record' : 'retrieve_vandelay_queued_authority_record';
 
-    for my $rec_id (@$record_ids) {
-        my $flesh = ['attributes', 'matches'];
-        push(@$flesh, 'import_items') if $$options{flesh_import_items};
-        my $params = {flesh => 1, flesh_fields => {$class => $flesh}};
-        my $rec = $e->$retrieve([$rec_id->{id}, $params]);
-        $rec->clear_marc if $$options{clear_marc};
-        $conn->respond($rec);
+    if ($self->api_name =~ /export/) {
+        if ($self->api_name =~ /print/) {
+
+            $e->rollback;
+            return $U->fire_object_event(
+                undef,
+                'vandelay.queued_'.$type.'_record.print',
+                [map {$_->{id}} @$record_ids],
+                $e->requestor->ws_ou
+            );
+
+        } elsif ($self->api_name =~ /csv/) {
+
+            $e->rollback;
+            return $U->fire_object_event(
+                undef,
+                'vandelay.queued_'.$type.'_record.csv',
+                [map {$_->{id}} @$record_ids],
+                $e->requestor->ws_ou
+            );
+
+        } elsif ($self->api_name =~ /email/) {
+
+            $conn->respond_complete(1);
+
+            for my $rec_id (@$record_ids) {
+                $U->create_events_for_hook(
+                    'vandelay.queued_'.$type.'_record.email',
+                    $rec_id->{id},
+                    $e->requestor->home_ou,
+                    undef,
+                    undef,
+                    1
+                );
+            }
+
+        }
+    } else {
+        for my $rec_id (@$record_ids) {
+            my $flesh = ['attributes', 'matches'];
+            push(@$flesh, 'import_items') if $$options{flesh_import_items};
+            my $params = {flesh => 1, flesh_fields => {$class => $flesh}};
+            my $rec = $e->$retrieve([$rec_id->{id}, $params]);
+            $rec->clear_marc if $$options{clear_marc};
+            $conn->respond($rec);
+        }
     }
 
     $e->rollback;
@@ -543,6 +588,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queue_import_items',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     authoritative => 1,
     signature => q/
         Returns template-generated printable output of Import Item (vii) objects for the selected queue.
@@ -555,6 +601,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queue_import_items',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     authoritative => 1,
     signature => q/
         Returns template-generated CSV output of Import Item (vii) objects for the selected queue.
@@ -567,6 +614,7 @@ __PACKAGE__->register_method(
     method      => 'retrieve_queue_import_items',
     api_level   => 1,
     argc        => 2,
+    stream      => 1,
     authoritative => 1,
     signature => q/
         Emails template-generated output of Import Item (vii) objects for the selected queue.
@@ -617,8 +665,45 @@ sub retrieve_queue_import_items {
         if $$options{with_import_error};
 
     my $items = $e->json_query($query);
-    for my $item (@$items) {
-        $conn->respond($e->retrieve_vandelay_import_item($item->{id}));
+    if ($self->api_name =~ /export/) {
+        if ($self->api_name =~ /print/) {
+
+            return $U->fire_object_event(
+                undef,
+                'vandelay.import_items.print',
+                [map {$_->{id}} @$items],
+                $e->requestor->ws_ou
+            );
+
+        } elsif ($self->api_name =~ /csv/) {
+
+            return $U->fire_object_event(
+                undef,
+                'vandelay.import_items.csv',
+                [map {$_->{id}} @$items],
+                $e->requestor->ws_ou
+            );
+
+        } elsif ($self->api_name =~ /email/) {
+
+            $conn->respond_complete(1);
+
+            for my $item (@$items) {
+                $U->create_events_for_hook(
+                    'vandelay.import_items.email',
+                    $item->{id},
+                    $e->requestor->home_ou,
+                    undef,
+                    undef,
+                    1
+                );
+            }
+
+        }
+    } else {
+        for my $item (@$items) {
+            $conn->respond($e->retrieve_vandelay_import_item($item->{id}));
+        }
     }
 
     return undef;

commit f9ef894ebd6d76f1a85c67d82473c6af31ad4ac3
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Mon May 9 23:50:31 2011 -0400

    register the methods we're expecting for the print/csv/email exports in Vandelay, and remove limit/offset options for such methods, and expectation of streaming

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 94b7a32..7745030 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -369,6 +369,28 @@ __PACKAGE__->register_method(
     stream      => 1,
     record_type => 'bib'
 );
+__PACKAGE__->register_method(
+    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.atomic.export.print",
+    method      => 'retrieve_queued_records',
+    api_level   => 1,
+    argc        => 2,
+    record_type => 'bib'
+);
+__PACKAGE__->register_method(
+    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.atomic.export.csv",
+    method      => 'retrieve_queued_records',
+    api_level   => 1,
+    argc        => 2,
+    record_type => 'bib'
+);
+__PACKAGE__->register_method(
+    api_name    => "open-ils.vandelay.bib_queue.records.retrieve.atomic.export.email",
+    method      => 'retrieve_queued_records',
+    api_level   => 1,
+    argc        => 2,
+    record_type => 'bib'
+);
+
 __PACKAGE__->register_method(  
     api_name    => "open-ils.vandelay.auth_queue.records.retrieve",
     method      => 'retrieve_queued_records',
@@ -377,6 +399,27 @@ __PACKAGE__->register_method(
     stream      => 1,
     record_type => 'auth'
 );
+__PACKAGE__->register_method(
+    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.atomic.export.print",
+    method      => 'retrieve_queued_records',
+    api_level   => 1,
+    argc        => 2,
+    record_type => 'auth'
+);
+__PACKAGE__->register_method(
+    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.atomic.export.csv",
+    method      => 'retrieve_queued_records',
+    api_level   => 1,
+    argc        => 2,
+    record_type => 'auth'
+);
+__PACKAGE__->register_method(
+    api_name    => "open-ils.vandelay.auth_queue.records.retrieve.atomic.export.email",
+    method      => 'retrieve_queued_records',
+    api_level   => 1,
+    argc        => 2,
+    record_type => 'auth'
+);
 
 __PACKAGE__->register_method(  
     api_name    => "open-ils.vandelay.bib_queue.records.matches.retrieve",
@@ -431,6 +474,10 @@ sub retrieve_queued_records {
         limit => $limit,
         offset => $offset,
     };
+    if($self->api_name =~ /export/) {
+        delete $query->{limit};
+        delete $query->{offset};
+    }
 
     $query->{where}->{import_time} = undef if $$options{non_imported};
 
@@ -491,6 +538,42 @@ __PACKAGE__->register_method(
             with_import_error : only return items that failed to import
     /
 );
+__PACKAGE__->register_method(
+    api_name    => 'open-ils.vandelay.import_item.queue.export.print',
+    method      => 'retrieve_queue_import_items',
+    api_level   => 1,
+    argc        => 2,
+    authoritative => 1,
+    signature => q/
+        Returns template-generated printable output of Import Item (vii) objects for the selected queue.
+        Filter options:
+            with_import_error : only return items that failed to import
+    /
+);
+__PACKAGE__->register_method(
+    api_name    => 'open-ils.vandelay.import_item.queue.export.csv',
+    method      => 'retrieve_queue_import_items',
+    api_level   => 1,
+    argc        => 2,
+    authoritative => 1,
+    signature => q/
+        Returns template-generated CSV output of Import Item (vii) objects for the selected queue.
+        Filter options:
+            with_import_error : only return items that failed to import
+    /
+);
+__PACKAGE__->register_method(
+    api_name    => 'open-ils.vandelay.import_item.queue.export.email',
+    method      => 'retrieve_queue_import_items',
+    api_level   => 1,
+    argc        => 2,
+    authoritative => 1,
+    signature => q/
+        Emails template-generated output of Import Item (vii) objects for the selected queue.
+        Filter options:
+            with_import_error : only return items that failed to import
+    /
+);
 
 sub retrieve_queue_import_items {
     my($self, $conn, $auth, $q_id, $options) = @_;
@@ -525,6 +608,10 @@ sub retrieve_queue_import_items {
         limit => $limit,
         offset => $offset
     };
+    if($self->api_name =~ /export/) {
+        delete $query->{limit};
+        delete $query->{offset};
+    }
 
     $query->{where} = {'+vii' => {import_error => {'!=' => undef}}}
         if $$options{with_import_error};

commit 57692a847e30c2a95c568f2b15ed0cc6669a392c
Author: berick <berick at esilibrary.com>
Date:   Fri May 6 15:37:32 2011 -0400

    initial export drop-down for items UI

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 193056c..d2405cc 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -377,6 +377,8 @@ function processSpool(key, queueId, type, onload) {
 }
 
 function vlExportInit() {
+
+    // queue export
     var qsel = dojo.byId('vl-queue-export-options');
     qsel.onchange = function(newVal) {
         var value = qsel.options[qsel.selectedIndex].value;
@@ -386,10 +388,37 @@ function vlExportInit() {
         retrieveQueuedRecords(
             currentType, 
             currentQueueId, 
-            function(r) { exportHandler(value, r) },
+            function(r) { 
+                exportHandler(value, r);
+                displayGlobalDiv('vl-queue-div');
+            },
             value
         );
     }
+
+    // item export
+    var isel = dojo.byId('vl-item-export-options');
+    isel.onchange = function(newVal) {
+        var value = isel.options[isel.selectedIndex].value;
+        isel.selectedIndex = 0;
+        if(!value) return;
+        if(!confirm('Export as "' + value + '"?')) return; // TODO: i18n
+
+        var method = 'open-ils.vandelay.import_item.queue.export.' + value;
+
+        fieldmapper.standardRequest(
+            ['open-ils.vandelay', method],
+            {
+                params : [
+                    authtoken, 
+                    currentQueueId, 
+                    {with_import_error: (vlImportItemsShowErrors.checked) ? 1 : null}
+                ],
+                async : true,
+                oncomplete : function(r) {exportHandler(type, r)}
+            }
+        );
+    }
 }
 
 function exportHandler(type, response) {
diff --git a/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2 b/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
index f72630b..5554f00 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
@@ -47,6 +47,15 @@
         <h1>Import Items</h1><br/>
         <input dojoType='dijit.form.CheckBox' jsId='vlImportItemsShowErrors' onchange='vlLoadErrorUIAll();'/>
         <span>Limit to Import Failures</span>
+        <table width='100%'><tr><td width='100%' align='right'>
+            <select id='vl-item-export-options' style='margin-right: 10px;'>
+                <!-- TODO I18N -->
+                <option value=''>Export Items As...</option>
+                <option value='print'>Print</option>
+                <option value='csv'>CSV</option>
+                <option value='email'>Email</option>
+            </select>
+        </td></tr></table>
         <table  jsId="vlAllImportErrorGrid"
                 dojoType="openils.widget.AutoGrid"
                 autoHeight='true'

commit e4f5a23f414a1afe91a758e563bed1112d0bc9d6
Author: berick <berick at esilibrary.com>
Date:   Fri May 6 14:46:25 2011 -0400

    initial export drop-down for queue UI

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index f076da8..193056c 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -185,6 +185,7 @@ function vlInit() {
     );
 
     vlAttrEditorInit();
+    vlExportInit();
 }
 
 
@@ -375,20 +376,45 @@ function processSpool(key, queueId, type, onload) {
     );
 }
 
-function retrieveQueuedRecords(type, queueId, onload) {
+function vlExportInit() {
+    var qsel = dojo.byId('vl-queue-export-options');
+    qsel.onchange = function(newVal) {
+        var value = qsel.options[qsel.selectedIndex].value;
+        qsel.selectedIndex = 0;
+        if(!value) return;
+        if(!confirm('Export as "' + value + '"?')) return; // TODO: i18n
+        retrieveQueuedRecords(
+            currentType, 
+            currentQueueId, 
+            function(r) { exportHandler(value, r) },
+            value
+        );
+    }
+}
+
+function exportHandler(type, response) {
+    var content = openils.Util.readResponse(response);
+    if(type == 'print') {
+        // TODO print the content
+    }
+    alert('response = ' + response);
+}
+
+function retrieveQueuedRecords(type, queueId, onload, doExport) {
     displayGlobalDiv('vl-generic-progress');
     queuedRecords = [];
     queuedRecordsMap = {};
     currentOverlayRecordsMap = {};
     currentOverlayRecordsMapGid = {};
     selectableGridRecords = {};
-    //resetVlQueueGridLayout();
 
     if(!type) type = currentType;
     if(!queueId) queueId = currentQueueId;
     if(!onload) onload = handleRetrieveRecords;
 
     var method = 'open-ils.vandelay.'+type+'_queue.records.retrieve.atomic';
+
+    if(doExport) method += '.export.' + doExport;
     if(vlQueueGridShowMatches.checked)
         method = method.replace('records', 'records.matches');
 
@@ -407,18 +433,8 @@ function retrieveQueuedRecords(type, queueId, onload) {
         ['open-ils.vandelay', method],
         {   async: true,
             params: params,
-            /*
-            onresponse: function(r) {
-                console.log("ONREPONSE");
-                var rec = r.recv().content();
-                if(e = openils.Event.parse(rec))
-                    return alert(e);
-                console.log("got record " + rec.id());
-                queuedRecords.push(rec);
-                queuedRecordsMap[rec.id()] = rec;
-            },
-            */
             oncomplete: function(r){
+                if(doExport) return onload();
                 var recs = r.recv().content();
                 if(e = openils.Event.parse(recs[0]))
                     return alert(e);
diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index b340264..c34c57f 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -69,6 +69,13 @@
             <table id='vl-queue-paging-table' class='queue-nav-table'>
                 <tbody>
                     <tr><td valign='bottom' align='right'>
+                        <select id='vl-queue-export-options' style='margin-right: 10px;'>
+                            <!-- TODO I18N -->
+                            <option value=''>Export Queue As...</option>
+                            <option value='print'>Print</option>
+                            <option value='csv'>CSV</option>
+                            <option value='email'>Email</option>
+                        </select>
                         <span style='padding-right:5px;'>&vandelay.results.per.page;</span>
                         <span class='queue-pager-span'>
                             <select jsId='vlQueueDisplayLimit' id='vl-queue-display-limit-selector'

commit 0f725603bf94f71bf71de57f9c9e8c42f445cc0c
Author: berick <berick at esilibrary.com>
Date:   Thu May 5 14:56:39 2011 -0400

    more import error tracking and reporting bug fixes

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 8c95b70..94b7a32 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -922,6 +922,8 @@ sub import_record_list_impl {
                     $logger->info("vl: successfully imported new $type record");
                     $rec->imported_as($record->id);
                     $rec->import_time('now');
+                    $rec->clear_import_error;
+                    $rec->clear_error_detail;
                     $imported = 1 if $e->$update_func($rec);
                 }
             }
@@ -996,9 +998,7 @@ sub finish_rec_import_attempt {
             $e->$method($rec) and $e->commit or $e->rollback;
 
         } else {
-            # successful import
-            $rec->clear_import_error;
-            $rec->clear_error_detail;
+            # commit the successful import
             $e->commit;
         }
 
@@ -1226,17 +1226,18 @@ sub import_record_asset_list_impl {
     $rec_ids = $roe->json_query({
         select => {vqbr => ['id']},
         from => {vqbr => 'vii'},
-        where => {'+vqbr' => {import_time => {'!=' => undef}}}
+        where => {'+vqbr' => {import_time => {'!=' => undef}}},
+        distinct => 1
     });
     $rec_ids = [map {$_->{id}} @$rec_ids];
 
-    my %report_args = (
+    my $report_args = {
         conn => $conn,
         total => scalar(@$rec_ids),
         step => 1, # how often to respond
         progress => 1,
         in_count => 0,
-    );
+    };
 
     for my $rec_id (@$rec_ids) {
         my $rec = $roe->retrieve_vandelay_queued_bib_record($rec_id);
@@ -1245,10 +1246,10 @@ sub import_record_asset_list_impl {
         for my $item_id (@$item_ids) {
             my $e = new_editor(requestor => $requestor, xact => 1);
             my $item = $e->retrieve_vandelay_import_item($item_id);
-            $report_args{import_item} = $item;
-            $report_args{e} = $e;
-            $report_args{import_error} = undef;
-            $report_args{evt} = undef;
+            $$report_args{import_item} = $item;
+            $$report_args{e} = $e;
+            $$report_args{import_error} = undef;
+            $$report_args{evt} = undef;
 
             # --------------------------------------------------------------------------------
             # Find or create the volume
@@ -1258,7 +1259,9 @@ sub import_record_asset_list_impl {
                     $e, $item->call_number, $rec->imported_as, $item->owning_lib);
 
             if($evt) {
-                respond_with_status(%report_args, evt => $evt);
+
+                $$report_args{evt} = $evt;
+                respond_with_status($report_args);
                 next;
             }
 
@@ -1287,30 +1290,24 @@ sub import_record_asset_list_impl {
             # see if a valid circ_modifier was provided
             # --------------------------------------------------------------------------------
             if($copy->circ_modifier and not $e->search_config_circ_modifier({code=>$item->circ_modifier})->[0]) {
-                respond_with_status(
-                    %report_args, 
-                    evt => $e->die_event,
-                    import_error => 'import.item.invalid.circ_modifier'
-                );
+                $$report_args{evt} = $e->die_event;
+                $$report_args{import_error} = 'import.item.invalid.circ_modifier';
+                respond_with_status($report_args);
                 next;
             }
 
             if($copy->location and not $e->retrieve_asset_copy_location($copy->location)) {
-                respond_with_status(
-                    %report_args, 
-                    evt => $e->die_event,
-                    import_error => 'import.item.invalid.location'
-                );
+                $$report_args{evt} = $e->die_event;
+                $$report_args{import_error} = 'import.item.invalid.location';
+                respond_with_status($report_args);
                 next;
             }
 
             if($evt = OpenILS::Application::Cat::AssetCommon->create_copy($e, $vol, $copy)) {
-                respond_with_status(
-                    %report_args,
-                    evt => $evt,
-                    import_error => ($evt->{textcode} eq 'ITEM_BARCODE_EXISTS') ? 
-                        'import.item.duplicate.barcode' : undef
-                );
+                $$report_args{evt} = $evt;
+                $$report_args{import_error} = 'import.item.duplicate.barcode'
+                    if $evt->{textcode} eq 'ITEM_BARCODE_EXISTS';
+                respond_with_status($report_args);
                 next;
             }
 
@@ -1321,7 +1318,8 @@ sub import_record_asset_list_impl {
                 $e, $copy, '', $item->pub_note, 1) if $item->pub_note;
 
             if($evt) {
-                respond_with_status(%report_args, evt => $evt);
+                $$report_args{evt} = $evt;
+                respond_with_status($report_args);
                 next;
             }
 
@@ -1329,7 +1327,8 @@ sub import_record_asset_list_impl {
                 $e, $copy, '', $item->priv_note, 1) if $item->priv_note;
 
             if($evt) {
-                respond_with_status(%report_args, evt => $evt);
+                $$report_args{evt} = $evt;
+                respond_with_status($report_args);
                 next;
             }
 
@@ -1337,8 +1336,8 @@ sub import_record_asset_list_impl {
             # Item import succeeded
             # --------------------------------------------------------------------------------
             $e->commit;
-            $report_args{in_count}++;
-            respond_with_status(%report_args, imported_as => $copy->id);
+            $$report_args{in_count}++;
+            respond_with_status($report_args);
             $logger->info("vl: successfully imported item " . $item->barcode);
         }
 

commit 6baadbc2fcd5baf128d6ad7768726addae1d9a2b
Author: berick <berick at esilibrary.com>
Date:   Thu May 5 14:11:10 2011 -0400

    run zz_match_bibs_trigger before update/insert so NEW.quality:= will work

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index cc02d17..2009df6 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -1866,7 +1866,7 @@ CREATE TRIGGER ingest_item_trigger
     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_items();
 
 CREATE TRIGGER zz_match_bibs_trigger
-    AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
+    BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
     FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
 
 

commit 64e04af74bc1d40a168dd6e4244b0769cfafcb44
Author: berick <berick at esilibrary.com>
Date:   Thu May 5 13:21:56 2011 -0400

    expose new import options to queue interface importer dialog; mucho import bug fixes in the middle layer code; expose queued rec quality in matches interface

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index d2c2f1c..3f93a59 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -329,6 +329,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Attributes" name="attributes" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Matches" name="matches" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Import Items" name="import_items" oils_persist:virtual="true" reporter:datatype="link"/>
+			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 		</fields>
 		<links>
 			<link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
@@ -447,6 +448,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Purpose" name="purpose" reporter:datatype="text"/>
 			<field reporter:label="Attributes" name="attributes" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Matches" name="matches" oils_persist:virtual="true" reporter:datatype="link"/>
+			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 		</fields>
 		<links>
 			<link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 3755ad2..8c95b70 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -576,7 +576,7 @@ sub import_record_list {
     return $e->die_event unless $e->checkauth;
     $args ||= {};
     my $err = import_record_list_impl($self, $conn, $rec_ids, $e->requestor, $args);
-    $e->rollback;
+    try {$e->rollback} otherwise {}; 
     return $err if $err;
     return {complete => 1};
 }
@@ -698,13 +698,13 @@ sub import_record_list_impl {
     my $type = $self->{record_type};
     my %queues;
 
-    my %report_args = (
-        progress => 0,
+    my $report_args = {
+        progress => 1,
         step => 1,
         conn => $conn,
         total => scalar(@$rec_ids),
         report_all => $$args{report_all}
-    );
+    };
 
     my $auto_overlay_exact = $$args{auto_overlay_exact};
     my $auto_overlay_1match = $$args{auto_overlay_1match};
@@ -750,10 +750,9 @@ sub import_record_list_impl {
         my $e = new_editor(xact => 1);
         $e->requestor($requestor);
 
-        $report_args{progress}++;
-        $report_args{e} = $e;
-        $report_args{import_error} = undef;
-        $report_args{evt} = undef;
+        $$report_args{e} = $e;
+        $$report_args{import_error} = undef;
+        $$report_args{evt} = undef;
 
         my $rec = $e->$retrieve_func([
             $rec_id,
@@ -763,11 +762,12 @@ sub import_record_list_impl {
         ]);
 
         unless($rec) {
-            finish_rec_import_attempt(%report_args, evt => $e->event);
+            $$report_args{evt} = $e->event;
+            finish_rec_import_attempt($report_args);
             next;
         }
 
-        $report_args{rec} = $rec;
+        $$report_args{rec} = $rec;
 
         if($rec->import_time) {
             $e->rollback;
@@ -890,7 +890,7 @@ sub import_record_list_impl {
                         $logger->info("vl: $type auto-overlay-best succeeded for queued rec " . $rec->id);
                         $imported = 1;
                     } else {
-                        $report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
+                        $$report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
                         $logger->info("vl: $type auto-overlay-best failed for queued rec " . $rec->id);
                     }
 
@@ -914,8 +914,8 @@ sub import_record_list_impl {
                 }
 
                 if($U->event_code($record)) {
-                    $report_args{import_error} = 'import.duplicate.tcn' if $record->{textcode} eq 'TCN_EXISTS';
-                    $report_args{evt} = $record;
+                    $$report_args{import_error} = 'import.duplicate.tcn' if $record->{textcode} eq 'TCN_EXISTS';
+                    $$report_args{evt} = $record;
 
                 } else {
 
@@ -929,11 +929,12 @@ sub import_record_list_impl {
 
         if($imported) {
             push @success_rec_ids, $rec_id;
-            finish_rec_import_attempt(%report_args);
+            finish_rec_import_attempt($report_args);
 
         } else {
             # Send an update whenever there's an error
-            finish_rec_import_attempt(%report_args, evt => $e->event);
+            $$report_args{evt} = $e->event unless $$report_args{evt};
+            finish_rec_import_attempt($report_args);
         }
     }
 
@@ -960,18 +961,18 @@ sub import_record_list_impl {
     # import the copies
     import_record_asset_list_impl($conn, \@success_rec_ids, $requestor);
 
-    $conn->respond({total => $report_args{total}, progress => $report_args{progress}});
+    $conn->respond({total => $$report_args{total}, progress => $$report_args{progress}});
     return undef;
 }
 
 # tracks any import errors, commits the current xact, responds to the client
 sub finish_rec_import_attempt {
-    my %args = @_;
-    my $evt = $args{evt};
-    my $rec = $args{rec};
-    my $e = $args{e};
+    my $args = shift;
+    my $evt = $$args{evt};
+    my $rec = $$args{rec};
+    my $e = $$args{e};
 
-    my $error = $args{import_error};
+    my $error = $$args{import_error};
     $error = 'general.unknown' if $evt and not $error;
 
     # error tracking
@@ -991,7 +992,7 @@ sub finish_rec_import_attempt {
             }
 
             my $method = 'update_vandelay_queued_bib_record';
-            $method =~ s/bib/authority/ if $args{type} eq 'auth';
+            $method =~ s/bib/authority/ if $$args{type} eq 'auth';
             $e->$method($rec) and $e->commit or $e->rollback;
 
         } else {
@@ -1007,18 +1008,17 @@ sub finish_rec_import_attempt {
     }
         
     # respond to client
-    if($args{report_all} or ($args{progress} % $args{step}) == 0) {
-
-        $args{conn}->respond({
-            total => $args{total}, 
-            progress => $args{progress}, 
+    if($$args{report_all} or ($$args{progress} % $$args{step}) == 0) {
+        $$args{conn}->respond({
+            total => $$args{total}, 
+            progress => $$args{progress}, 
             imported => ($rec) ? $rec->id : undef,
             err_event => $evt
         });
-
-        # report often at first, climb quickly, then hold steady
-        $args{step} *= 2 unless $args{step} == 256;
+        $$args{step} *= 2 unless $$args{step} == 256;
     }
+
+    $$args{progress}++;
 }
 
 
@@ -1221,25 +1221,34 @@ sub import_record_asset_list_impl {
 
     my $roe = new_editor(xact=> 1, requestor => $requestor);
 
+    # for speed, filter out any records have not been 
+    # imported or have no import items to load
+    $rec_ids = $roe->json_query({
+        select => {vqbr => ['id']},
+        from => {vqbr => 'vii'},
+        where => {'+vqbr' => {import_time => {'!=' => undef}}}
+    });
+    $rec_ids = [map {$_->{id}} @$rec_ids];
+
     my %report_args = (
         conn => $conn,
         total => scalar(@$rec_ids),
         step => 1, # how often to respond
-        progress => 0,
+        progress => 1,
         in_count => 0,
     );
 
     for my $rec_id (@$rec_ids) {
         my $rec = $roe->retrieve_vandelay_queued_bib_record($rec_id);
-        next unless $rec and $rec->import_time;
         my $item_ids = $roe->search_vandelay_import_item({record => $rec->id}, {idlist=>1});
 
         for my $item_id (@$item_ids) {
             my $e = new_editor(requestor => $requestor, xact => 1);
             my $item = $e->retrieve_vandelay_import_item($item_id);
-            $report_args{progress}++;
             $report_args{import_item} = $item;
             $report_args{e} = $e;
+            $report_args{import_error} = undef;
+            $report_args{evt} = undef;
 
             # --------------------------------------------------------------------------------
             # Find or create the volume
@@ -1332,24 +1341,26 @@ sub import_record_asset_list_impl {
             respond_with_status(%report_args, imported_as => $copy->id);
             $logger->info("vl: successfully imported item " . $item->barcode);
         }
+
     }
+
     $roe->rollback;
     return undef;
 }
 
 
 sub respond_with_status {
-    my %args = @_;
-    my $e = $args{e};
+    my $args = shift;
+    my $e = $$args{e};
 
     #  If the import failed, track the failure reason
 
-    my $error = $args{import_error};
-    my $evt = $args{evt};
+    my $error = $$args{import_error};
+    my $evt = $$args{evt};
 
     if($error or $evt) {
 
-        my $item = $args{import_item};
+        my $item = $$args{import_item};
         $logger->info("vl: unable to import item " . $item->barcode);
 
         $error ||= 'general.unknown';
@@ -1367,13 +1378,15 @@ sub respond_with_status {
         $e->commit;
     }
 
-    return unless $args{report_all} or ($args{progress} % $args{step}) == 0;
-    $args{step} *= 2 unless $args{step} == 256;
+    if($$args{report_all} or ($$args{progress} % $$args{step}) == 0) {
+        $$args{conn}->respond({
+            map { $_ => $args->{$_} } qw/total progress success_count/,
+            err_event => $evt
+        });
+        $$args{step} *= 2 unless $$args{step} == 256;
+    }
 
-    $args{conn}->respond({
-        map { $_ => $args{$_} } qw/total progress success_count/,
-        err_event => $evt
-    });
+    $$args{progress}++;
 }
 
 __PACKAGE__->register_method(  
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index b72438c..f076da8 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -435,7 +435,8 @@ function retrieveQueuedRecords(type, queueId, onload) {
 
 function vlLoadMatchUI(recId) {
     displayGlobalDiv('vl-generic-progress');
-    var matches = queuedRecordsMap[recId].matches();
+    var queuedRec = queuedRecordsMap[recId];
+    var matches = queuedRec.matches();
     var records = [];
     currentImportRecId = recId;
     for(var i = 0; i < matches.length; i++)
@@ -465,7 +466,7 @@ function vlLoadMatchUI(recId) {
 
                 // build the data store of records with match information
                 var dataStore = bre.toStoreData(recs, null, 
-                    {virtualFields:['_id', 'match_score', 'match_quality']});
+                    {virtualFields:['_id', 'match_score', 'match_quality', 'rec_quality']});
                 dataStore.identifier = '_id';
 
                 var matchSeenMap = {};
@@ -479,6 +480,7 @@ function vlLoadMatchUI(recId) {
                             if(match.match_score)
                                 item.match_score = match.match_score();
                             item.match_quality = match.quality();
+                            item.rec_quality = queuedRec.quality();
                             matchSeenMap[match.id()] = 1;
                             break;
                         }
@@ -979,6 +981,8 @@ function vlHandleQueueItemsAction(action) {
             vlUploadQueueAutoOverlayExact.attr('value',  vlUploadQueueAutoOverlayExact2.attr('value'));
             vlUploadQueueAutoOverlay1Match.attr('value',  vlUploadQueueAutoOverlay1Match2.attr('value'));
             vlUploadMergeProfile.attr('value',  vlUploadMergeProfile2.attr('value'));
+            vlUploadQueueAutoOverlayBestMatch.attr('value',  vlUploadQueueAutoOverlayBestMatch2.attr('value'));
+            vlUploadQueueAutoOverlayBestMatchRatio.attr('value',  vlUploadQueueAutoOverlayBestMatchRatio2.attr('value'));
 
             if(action == 'import') {
                 vlImportSelectedRecords();
@@ -995,6 +999,10 @@ function vlHandleQueueItemsAction(action) {
             vlUploadQueueAutoOverlay1Match2.attr('value', false);
             vlUploadMergeProfile.attr('value', '');
             vlUploadMergeProfile2.attr('value', '');
+            vlUploadQueueAutoOverlayBestMatch.attr('value', false);
+            vlUploadQueueAutoOverlayBestMatch2.attr('value', false);
+            vlUploadQueueAutoOverlayBestMatchRatio.attr('value', '0.0');
+            vlUploadQueueAutoOverlayBestMatchRatio2.attr('value', '0.0');
         }
     );
 
@@ -1002,8 +1010,8 @@ function vlHandleQueueItemsAction(action) {
 }
     
 
+/* import user-selected records */
 function vlImportSelectedRecords() {
-    displayGlobalDiv('vl-generic-progress-with-total');
     var records = [];
 
     for(var id in selectableGridRecords) {
@@ -1015,55 +1023,35 @@ function vlImportSelectedRecords() {
         }
     }
 
-    var options = {overlay_map : currentOverlayRecordsMap};
-
-    if(vlUploadQueueAutoOverlayExact.checked) {
-        options.auto_overlay_exact = true;
-        vlUploadQueueAutoOverlayExact.checked = false;
-    }
-
-    if(vlUploadQueueAutoOverlay1Match.checked) {
-        options.auto_overlay_1match = true;
-        vlUploadQueueAutoOverlay1Match.checked = false;
-    }
-    
-    var profile = vlUploadMergeProfile.attr('value');
-    if(profile != null && profile != '') {
-        options.merge_profile = profile;
-    }
-
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', 'open-ils.vandelay.'+currentType+'_record.list.import'],
-        {   async: true,
-            params: [authtoken, records, options],
-            onresponse: function(r) {
-                var resp = r.recv().content();
-                if(e = openils.Event.parse(resp))
-                    return alert(e);
-                if(resp.complete) {
-                    return retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-                } else {
-                    vlControlledProgressBar.update({maximum:resp.total, progress:resp.progress});
-                }
-            }, 
+    vlImportRecordQueue(
+        currentType, 
+        currentQueueId, 
+        records,
+        function(){
+            retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
         }
     );
 }
 
+/* import all (non-imported) queue records */
 function vlImportAllRecords() {
-    vlImportRecordQueue(currentType, currentQueueId, false,
-        function(){displayGlobalDiv('vl-queue-div');});
+    vlImportRecordQueue(
+        currentType, 
+        currentQueueId, 
+        null,
+        function(){
+            retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+        }
+    );
 }
 
-function vlImportRecordQueue(type, queueId, onload) {
+/* if recList has values, import only those records */
+function vlImportRecordQueue(type, queueId, recList, onload) {
     displayGlobalDiv('vl-generic-progress-with-total');
-    var method = 'open-ils.vandelay.bib_queue.import';
-    if(type == 'auth')
-        method = method.replace('bib', 'auth');
-
 
+    /* set up options */
     var mergeOpt = false;
-    var options = {};
+    var options = {overlay_map : currentOverlayRecordsMap};
 
     if(vlUploadQueueImportNoMatch.checked) {
         options.import_no_match = true;
@@ -1089,22 +1077,34 @@ function vlImportRecordQueue(type, queueId, onload) {
         mergeOpt = true;
     }
 
+    var profile = vlUploadMergeProfile.attr('value');
+    if(profile != null && profile != '') {
+        options.merge_profile = profile;
+    }
+
+    /* determine which method we're calling */
+
+    var method = 'open-ils.vandelay.bib_queue.import';
+    if(type == 'auth')
+        method = method.replace('bib', 'auth');
+
     if(!mergeOpt) {
         // in the interest of speed, if no merge options are 
         // chosen, tell the back-end code to only process records
         // that have no matches
-        method = method.replace('.import', 'nomatch.import');
+        method = method.replace(/\.import/, '.nomatch.import');
     }
-    
-    var profile = vlUploadMergeProfile.attr('value');
-    if(profile != null && profile != '') {
-        options.merge_profile = profile;
+
+    var params = [authtoken, queueId, options];
+    if(recList) {
+        method = 'open-ils.vandelay.'+currentType+'_record.list.import';
+        params[1] = recList;
     }
 
     fieldmapper.standardRequest(
         ['open-ils.vandelay', method],
         {   async: true,
-            params: [authtoken, queueId, options],
+            params: params,
             onresponse: function(r) {
                 var resp = r.recv().content();
                 if(e = openils.Event.parse(resp))
@@ -1134,6 +1134,7 @@ function batchUpload() {
                 vlImportRecordQueue(
                     currentType, 
                     currentQueueId, 
+                    null,
                     function() {
                         retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
                     }
@@ -1239,6 +1240,17 @@ function vlShowUploadForm() {
                vlUploadQueueAutoOverlayBestMatchRatio.attr('value', profile.lwm_ratio()+''); 
         }
     );
+    dojo.connect(
+        vlUploadMergeProfile2, 
+        'onChange',
+        function(val) {
+            if(!val) return;
+            var profile = mergeProfiles.filter(function(p) { return (p.id() == val); })[0];
+            if(profile.lwm_ratio() != null)
+               vlUploadQueueAutoOverlayBestMatchRatio2.attr('value', profile.lwm_ratio()+''); 
+        }
+    );
+
 }
 
 function vlShowQueueSelect() {
diff --git a/Open-ILS/web/templates/default/vandelay/inc/matches.tt2 b/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
index 40d6a80..2376dbc 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
@@ -15,6 +15,7 @@
                     formatter : vlFormatViewMatchMARC
                 },
                 {name: 'Match Score', field:'match_score'},
+                {name: 'Queued Record Quality', field:'rec_quality'},
                 {name: 'Matched Record Quality', field:'match_quality'},
                 {name: '&vandelay.creator;', get: vlGetCreator},
                 {name: '&vandelay.create.date;', field:'create_date', get: vlGetDateTimeField},
diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index 00c812d..b340264 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -9,10 +9,10 @@
                         <table class='queue-nav-table'>
                             <thead><tr><th colspan='2' class='queue-nav-table-label'>Queue Actions</th></tr></thead>
                             <tbody>
-                                <tr><td><a href='#' onclick='vlHandleQueueItemsAction("import")'>&vandelay.import.selected;</a></td></tr>
-                                <tr><td><a href='#' onclick='vlHandleQueueItemsAction("import_all")'>&vandelay.import.all;</a></td></tr>
-                                <tr><td><a href='#' onclick='vlLoadErrorUIAll();'>View Import Items</a></td></tr>
-                                <tr><td><a href='#' onclick='
+                                <tr><td><a href='javascript:;' onclick='vlHandleQueueItemsAction("import")'>&vandelay.import.selected;</a></td></tr>
+                                <tr><td><a href='javascript:;' onclick='vlHandleQueueItemsAction("import_all")'>&vandelay.import.all;</a></td></tr>
+                                <tr><td><a href='javascript:;' onclick='vlLoadErrorUIAll();'>View Import Items</a></td></tr>
+                                <tr><td><a href='javascript:;' onclick='
                                     if(confirm("&vandelay.sure.to.delete.queue;")) {
                                         vlDeleteQueue(currentType, currentQueueId, 
                                             function() { displayGlobalDiv("vl-marc-upload-div"); });}'>&vandelay.delete.queue;</a></td></tr>
@@ -186,9 +186,16 @@
         <table class='form_table'>
             <tbody>
                 <tr>
+                    <td>&vandelay.auto.import.merge_profile;</td>
+                    <td colspan='4'>
+                        <div jsId='vlUploadMergeProfile2' 
+                            dojoType='dijit.form.FilteringSelect' required='false' labelAttr='name' searchAttr='name'/>
+                    </td>
+                </tr>
+                <tr>
                     <td>&vandelay.auto.import.noncolliding;</td>
                     <td colspan='4'>
-                        <input jsId='vlUploadQueueAutoImport2' dojoType='dijit.form.CheckBox'/>
+                        <input jsId='vlUploadQueueImportNoMatch2' dojoType='dijit.form.CheckBox'/>
                     </td>
                 </tr>
                 <tr>
@@ -204,10 +211,14 @@
                     </td>
                 </tr>
                 <tr>
-                    <td>&vandelay.auto.import.merge_profile;</td>
+                    <td>&vandelay.auto.import.auto_overlay_best;</td>
+                    <td colspan='4'><input jsId='vlUploadQueueAutoOverlayBestMatch2' dojoType='dijit.form.CheckBox'/></td>
+                </tr>
+                <tr>
+                    <td>&vandelay.auto.import.auto_overlay_best_ratio;</td>
                     <td colspan='4'>
-                        <div jsId='vlUploadMergeProfile2' 
-                            dojoType='dijit.form.FilteringSelect' required='false' labelAttr='name' searchAttr='name'/>
+                        <input style='width:3em' value='0.0' jsId='vlUploadQueueAutoOverlayBestMatchRatio2' dojoType='dijit.form.TextBox'/>
+                        <span style='padding-left: 10px; font-size:90%'>(&vandelay.auto.import.auto_overlay_best_ratio.desc;)</span>
                     </td>
                 </tr>
                 <tr>

commit 207dd51b5dae6f5c4f88f576f79d6736b16ef559
Author: berick <berick at esilibrary.com>
Date:   Thu May 5 10:28:15 2011 -0400

    added edit support for lwm_ratio on merge_profile; default ratio to that of the merge profile if selected and if a ratio is set on the profile

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 4aeef8f..b72438c 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -87,6 +87,7 @@ var vlQueueGridColumePicker = {};
 var vlBibSources = [];
 var importItemDefs = [];
 var matchSets = {};
+var mergeProfiles = [];
 
 /**
   * Grab initial data
@@ -106,13 +107,13 @@ function vlInit() {
             runStartupCommands();
     }
 
-    var profiles = new openils.PermaCrud().retrieveAll('vmp');
-    vlUploadMergeProfile.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(profiles)});
+    mergeProfiles = new openils.PermaCrud().retrieveAll('vmp');
+    vlUploadMergeProfile.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
     vlUploadMergeProfile.labelAttr = 'name';
     vlUploadMergeProfile.searchAttr = 'name';
     vlUploadMergeProfile.startup();
 
-    vlUploadMergeProfile2.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(profiles)});
+    vlUploadMergeProfile2.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
     vlUploadMergeProfile2.labelAttr = 'name';
     vlUploadMergeProfile2.searchAttr = 'name';
     vlUploadMergeProfile2.startup();
@@ -1226,6 +1227,18 @@ function vlShowUploadForm() {
     vlUploadQueueHoldingsImportProfile.store = 
         new dojo.data.ItemFileReadStore({data:viiad.toStoreData(importItemDefs)});
     vlUpdateMatchSetSelector(vlUploadRecordType.getValue());
+
+    // use ratio from the merge profile if it's set
+    dojo.connect(
+        vlUploadMergeProfile, 
+        'onChange',
+        function(val) {
+            if(!val) return;
+            var profile = mergeProfiles.filter(function(p) { return (p.id() == val); })[0];
+            if(profile.lwm_ratio() != null)
+               vlUploadQueueAutoOverlayBestMatchRatio.attr('value', profile.lwm_ratio()+''); 
+        }
+    );
 }
 
 function vlShowQueueSelect() {
diff --git a/Open-ILS/web/templates/default/vandelay/inc/profiles.tt2 b/Open-ILS/web/templates/default/vandelay/inc/profiles.tt2
index 5bd4d4d..ca67f3a 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/profiles.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/profiles.tt2
@@ -13,9 +13,9 @@
     </div>
     <table  jsId="pGrid"
             dojoType="openils.widget.AutoGrid"
-            fieldOrder="['name', 'owner', 'preserve_spec', 'replace_spec', 'add_spec', 'strip_spec']"
+            fieldOrder="['name', 'owner', 'preserve_spec', 'replace_spec', 'add_spec', 'strip_spec', 'lwm_ratio']"
             query="{id: '*'}"
-            defaultCellWidth='"auto"'
+            defaultCellWidth='"14%"'
             fmClass='vmp'
             showPaginator='true'
             editOnEnter='true'>

commit ff796bd6fc1ae0744a7f7efc73a0bdb11b9975e8
Author: berick <berick at esilibrary.com>
Date:   Thu May 5 10:27:31 2011 -0400

    added missing lwm_ratio field to vandelay.merge_profile

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 97c2865..d2c2f1c 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -148,6 +148,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Replace Specification" name="replace_spec" reporter:datatype="text"/>
 			<field reporter:label="Remove Specification" name="strip_spec" reporter:datatype="text"/>
 			<field reporter:label="Preserve Specification" name="preserve_spec" reporter:datatype="text"/>
+			<field reporter:label="Min. Quality Ratio" name="lwm_ratio" reporter:datatype="float"/>
 		</fields>
 		<links>
 			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>

commit 130a5dcf1f0c8bdc79dc617873e20033dc433311
Author: berick <berick at esilibrary.com>
Date:   Thu May 5 10:01:22 2011 -0400

    record import error handling improvements

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 759a624..3755ad2 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -752,6 +752,8 @@ sub import_record_list_impl {
 
         $report_args{progress}++;
         $report_args{e} = $e;
+        $report_args{import_error} = undef;
+        $report_args{evt} = undef;
 
         my $rec = $e->$retrieve_func([
             $rec_id,
@@ -912,15 +914,14 @@ sub import_record_list_impl {
                 }
 
                 if($U->event_code($record)) {
-                    my $import_error = 'import.duplicate.tcn' if $record->{textcode} eq 'TCN_EXISTS';
-                    finish_rec_import_attempt(%report_args, import_error => $import_error, evt => $record);
+                    $report_args{import_error} = 'import.duplicate.tcn' if $record->{textcode} eq 'TCN_EXISTS';
+                    $report_args{evt} = $record;
 
                 } else {
 
                     $logger->info("vl: successfully imported new $type record");
                     $rec->imported_as($record->id);
                     $rec->import_time('now');
-
                     $imported = 1 if $e->$update_func($rec);
                 }
             }

commit 815e3e2f48614f1cead864d4151f188c62a88d95
Author: senator <lebbeous at esilibrary.com>
Date:   Wed May 4 17:33:29 2011 -0400

    the vandelay match set editor now does quality metrics too

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index f7ad9e3..97c2865 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -599,10 +599,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
-				<create permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
-				<retrieve permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
-				<update permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
-				<delete permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
+				<create permission="ADMIN_IMPORT_MATCH_SET">
+                    <context link="match_set" field="owner"/>
+				</create>
+				<retrieve permission="ADMIN_IMPORT_MATCH_SET">
+                    <context link="match_set" field="owner"/>
+				</retrieve>
+				<update permission="ADMIN_IMPORT_MATCH_SET">
+                    <context link="match_set" field="owner"/>
+				</update>
+				<delete permission="ADMIN_IMPORT_MATCH_SET">
+                    <context link="match_set" field="owner"/>
+				</delete>
 			</actions>
 		</permacrud>
 	</class>
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
index 7e76613..92851d2 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
@@ -6,8 +6,10 @@
     "ENTER_REPLACE_MODE": "Enter Replace Mode",
     "NO_CAN_DO": "An error has occurred. Close this interface and try again.",
     "OK": "Ok",
-    "POINT_NEEDS_ONE": "A match point must be exactly one of the following: boolean operator, MARC tag/subfield pair, single-value-field.",
+    "CANCEL": "Cancel",
+    "POINT_NEEDS_ONE": "You have not entered valid data",
     "FAULTY_MARC": "A MARC tag must be identified by three digits, and the subfield must be one non-whitespace, non-control character.",
     "WORKING_MP_HERE": "Choose from among the three buttons above to add a new match point.",
+    "WORKING_QM_HERE": "Use buttons above and to the right to add new quality metrics.",
     "SVF": "Record Attribute"
 }
diff --git a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
index e07eeca..28f30b5 100644
--- a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
@@ -11,7 +11,7 @@ dojo.require("openils.PermaCrud");
 dojo.require("openils.widget.ProgressDialog");
 dojo.require("openils.widget.AutoGrid");
 
-var localeStrings, node_editor, _crads, CGI, tree, match_set;
+var localeStrings, node_editor, qnode_editor, _crads, CGI, tree, match_set;
 
 var NodeEditorAbstract = {
     "_svf_select_template": null,
@@ -25,11 +25,7 @@ var NodeEditorAbstract = {
     },
     "is_sensible": function(thing) {
         var need_one = 0;
-        var list = openils.Util.objectProperties(this._factories_by_type);
-        console.log(list.join(" "));
-        openils.Util.objectProperties(this._factories_by_type).forEach(
-            function(field) { if (thing[field]()) need_one++; }
-        );
+        this.foi.forEach(function(field) { if (thing[field]()) need_one++; });
 
         if (need_one != 1) {
             alert(localeStrings.POINT_NEEDS_ONE);
@@ -56,7 +52,6 @@ var NodeEditorAbstract = {
             this._consistent_controls = [];
             for (var i = 0; i < trs.length; i++)
                 this._consistent_controls[i] = dojo.clone(trs[i]);
-            dojo.empty(trs[0].parentNode);
         }
 
         this._consistent_controls.forEach(
@@ -124,39 +119,53 @@ var NodeEditorAbstract = {
                 }, dojo.create("td", null, rows[1])
             );
             return rows;
+        },
+        "bool_op": function() {
+            var tr = dojo.create("tr");
+            dojo.create(
+                "label",
+                {"for": "operator-select", "innerHTML": "Operator:"},
+                dojo.create("td", null, tr)
+            );
+            var select = dojo.create(
+                "select", {"fmfield": "bool_op", "id": "operator-select"},
+                dojo.create("td", null, tr)
+            );
+            dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
+            dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
+
+            return [tr];
         }
     }
 };
 
-function _find_crad_by_name(name) {
-    for (var i = 0; i < _crads.length; i++) {
-        if (_crads[i].name() == name)
-            return _crads[i];
-    }
-    return null;
+function apply_base_class(cls, basecls) {
+    openils.Util.objectProperties(basecls).forEach(
+        function(m) { cls[m] = basecls[m]; }
+    );
 }
 
 function QualityNodeEditor() {
     var self = this;
+    this.foi = ["tag", "svf"]; /* Fields of Interest - starting points for UI */
 
-    this._init = function(dnd_source, qnode_editor_container) {
+    this._init = function(qnode_editor_container) {
         this._consistent_controls_query =
             "[consistent-controls], [quality-controls]";
-        this.dnd_source = dnd_source;
         this.qnode_editor_container = dojo.byId(qnode_editor_container);
+        this.clear();
     };
 
     this.clear = function() {
-        this.dnd_source.selectAll().deleteSelectedNodes();
         dojo.create(
-            "em", {"innerHTML": localeStrings.WORKING_MP_HERE},
+            "em", {"innerHTML": localeStrings.WORKING_QM_HERE},
             this.qnode_editor_container, "only"
         );
-        this.dnd_source._ready = false;
     };
 
     this.build_vmsq = function() {
         var metric = new vmsq();
+        metric.match_set(match_set.id());   /* using global */
         var controls = dojo.query("[fmfield]", this.qnode_editor_container);
         for (var i = 0; i < controls.length; i++) {
             var field = dojo.attr(controls[i], "fmfield");
@@ -171,7 +180,6 @@ function QualityNodeEditor() {
     this.add = function(type) {
         this.clear();
 
-
         /* these are the editing widgets */
         var table = dojo.create("table", {"className": "node-editor"});
 
@@ -180,19 +188,51 @@ function QualityNodeEditor() {
 
         this._add_consistent_controls(table);
 
+        var ok_cxl_td = dojo.create(
+            "td", {"colspan": 2, "align": "center", "className": "space-me"},
+            dojo.create("tr", null, table)
+        );
+
         dojo.create(
             "input", {
                 "type": "submit", "value": localeStrings.OK,
-                "onclick": function() { alert("would update draggable here"/* XXX TODO */); }
-            }, dojo.create(
-                "td", {"colspan": 2, "align": "center"},
-                dojo.create("tr", null, table)
-            )
+                "onclick": function() {
+                    var metric = self.build_vmsq();
+                    if (metric) {
+                        self.clear();
+                        pcrud.create(
+                            metric, {
+                                /* borrowed from openils.widget.AutoGrid */
+                                "oncomplete": function(req, cudResults) {
+                                    var fmObject = cudResults[0];
+                                    if (vmsq_grid.onPostCreate)
+                                        vmsq_grid.onPostCreate(fmObject);
+                                    if (fmObject) {
+                                        vmsq_grid.store.newItem(
+                                            fmObject.toStoreItem()
+                                        );
+                                    }
+                                    setTimeout(function() {
+                                        try {
+                                            vmsq_grid.selection.select(vmsq_grid.rowCount-1);
+                                            vmsq_grid.views.views[0].getCellNode(vmsq_grid.rowCount-1, 1).focus();
+                                        } catch (E) {}
+                                    },200);
+                                }
+                            }
+                        );
+                    }
+                }
+            }, ok_cxl_td
+        );
+        dojo.create(
+            "input", {
+                "type": "reset", "value": localeStrings.CANCEL,
+                "onclick": function() { self.clear(); }
+            }, ok_cxl_td
         );
 
-        dojo.place(table, this.qnode_editor_container, "only");/* XXX put in dialog here */
-
-        //this.dnd_source.insertNodes(false, [draggable]);
+        dojo.place(table, this.qnode_editor_container, "only");
 
         /* nice */
         try { dojo.query("select, input", table)[0].focus(); }
@@ -200,17 +240,17 @@ function QualityNodeEditor() {
 
     };
 
-    openils.Util.objectProperties(NodeEditorAbstract).forEach(
-        function(m) { self[m] = NodeEditorAbstract[m]; }
-    );
+    apply_base_class(self, NodeEditorAbstract);
     this._init.apply(this, arguments);
 }
 
 function NodeEditor() {
     var self = this;
+    this.foi = ["tag", "svf", "bool_op"]; /* Fields of Interest - starting points for UI */
 
     this._init = function(dnd_source, node_editor_container) {
-        this._consistent_controls_query = "[consistent-controls]";
+        this._consistent_controls_query =
+            "[consistent-controls], [point-controls]";
         this.dnd_source = dnd_source;
         this.node_editor_container = dojo.byId(node_editor_container);
     };
@@ -285,29 +325,19 @@ function NodeEditor() {
 
     };
 
-    openils.Util.objectProperties(NodeEditorAbstract).forEach(
-        function(m) { self[m] = NodeEditorAbstract[m]; }
-    );
+    apply_base_class(self, NodeEditorAbstract);
 
-    this._factories_by_type.bool_op = function() {
-        var tr = dojo.create("tr");
-        dojo.create(
-            "label",
-            {"for": "operator-select", "innerHTML": "Operator:"},
-            dojo.create("td", null, tr)
-        );
-        var select = dojo.create(
-            "select", {"fmfield": "bool_op", "id": "operator-select"},
-            dojo.create("td", null, tr)
-        );
-        dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
-        dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
-
-        return [tr];
-    };
     this._init.apply(this, arguments);
 }
 
+function find_crad_by_name(name) {
+    for (var i = 0; i < _crads.length; i++) {
+        if (_crads[i].name() == name)
+            return _crads[i];
+    }
+    return null;
+}
+
 function render_vmsp_label(point, minimal) {
     /* "minimal" has this implication:
      * for svf, only show the code, not the longer label.
@@ -317,7 +347,7 @@ function render_vmsp_label(point, minimal) {
     } else if (point.svf()) {
         return (openils.Util.isTrue(point.negate()) ? "NOT " : "") + (
             minimal ?  point.svf() :
-                (point.svf() + " / " + _find_crad_by_name(point.svf()).label())
+                (point.svf() + " / " + find_crad_by_name(point.svf()).label())
         );
     } else {
         return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
@@ -459,7 +489,7 @@ function save_tree() {
                 root, function(r) {
                     fieldmapper.standardRequest(
                         ["open-ils.vandelay",
-                            "open-ils.vandelay.match_set.update"],/* XXX TODO */{
+                            "open-ils.vandelay.match_set.update"], {
                             "params": [
                                 openils.User.authtoken, match_set.id(), r
                             ],
@@ -482,7 +512,7 @@ function save_tree() {
 function init_vmsq_grid() {
     vmsq_grid.loadAll(
         {"order_by": {"vmsq": "quality"}},
-        {"match_set": CGI.param("match_set")}
+        {"match_set": match_set.id()}
     );
 }
 
@@ -538,6 +568,7 @@ function my_init() {
     );
 
     node_editor = new NodeEditor(src, "node-editor-container");
+    qnode_editor = new QualityNodeEditor("qnode-editor-container");
 
     replace_mode(0);
 
diff --git a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
index 7a11223..8ad1eea 100644
--- a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
@@ -3,6 +3,7 @@
 <style type="text/css">
     h1 { margin: 1ex 0; }
     .outer { clear: both; margin-bottom: 1ex; }
+    .space-me input { margin: 0 1em; }
     button { margin: 0 0.5em; }
     input[type=submit] { padding: 0 0.5em; }
     #tree-here { margin-bottom: 1.5em; }
@@ -48,6 +49,14 @@
     give some indication of which match set we're editing the tree for
     -->
 <table class="hidden">
+    <tr quality-controls="1">
+        <td>
+            <label for="value-input">Value:</label>
+        </td>
+        <td>
+            <input id="value-input" type="text" fmfield="value" />
+        </td>
+    </tr>
     <tr consistent-controls="1">
         <td>
             <label for="quality-input"
@@ -60,7 +69,7 @@
                 size="4" maxlength="3" fmfield="quality" />
         </td>
     </tr>
-    <tr consistent-controls="1">
+    <tr point-controls="1">
         <td>
             <label for="negate-input">Negate?</label>
         </td>
@@ -68,14 +77,6 @@
             <input id="negate-input" type="checkbox" fmfield="negate" />
         </td>
     </tr>
-    <tr quality-controls="1">
-        <td>
-            <label for="value-input">Value:</label>
-        </td>
-        <td>
-            <input id="value-input" type="text" fmfield="value" />
-        </td>
-    </tr>
 </table>
 <div class="outer">
     <div id="expr-preview-row">
@@ -131,6 +132,7 @@
         </div>
     </div>
     <br style="clear: both;" />
+    <table id="qnode-editor-container"></table>
     <table jsId="vmsq_grid"
         dojoType="openils.widget.AutoGrid"
         query="{id: '*'}"

commit da3a453442050482a419deff91d3dc92fdbf132a
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 17:15:03 2011 -0400

    Best Match w/ quality ratio merging
    
     * Mew Best Match option in merge/overlay
     * support for upload time minimum record quality ratio
     * We now /only/ create new records if no matches exist and the user
       selected a match-free import
     * Selecting a match-free import no longer trumps other merge imports.
       They both work together now.
    
    TODO: find out why queued record quality is not getting set at queued
    rec creation time.

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 510d6bf..759a624 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -708,11 +708,15 @@ sub import_record_list_impl {
 
     my $auto_overlay_exact = $$args{auto_overlay_exact};
     my $auto_overlay_1match = $$args{auto_overlay_1match};
+    my $auto_overlay_best = $$args{auto_overlay_best_match};
+    my $match_quality_ratio = $$args{match_quality_ratio};
     my $merge_profile = $$args{merge_profile};
     my $bib_source = $$args{bib_source};
+    my $import_no_match = $$args{import_no_match};
 
     my $overlay_func = 'vandelay.overlay_bib_record';
     my $auto_overlay_func = 'vandelay.auto_overlay_bib_record';
+    my $auto_overlay_best_func = 'vandelay.auto_overlay_bib_record_with_best'; # XXX bib-only
     my $retrieve_func = 'retrieve_vandelay_queued_bib_record';
     my $update_func = 'update_vandelay_queued_bib_record';
     my $search_func = 'search_vandelay_queued_bib_record';
@@ -863,7 +867,39 @@ sub import_record_list_impl {
                 }
             }
 
-            if(!$imported and !$error) {
+            if(!$imported and !$error and $auto_overlay_best and scalar(@{$rec->matches}) > 0 ) {
+
+                # caller says to overlay the best match
+
+                my $res = $e->json_query(
+                    {
+                        from => [
+                            $auto_overlay_best_func,
+                            $rec->id, 
+                            $merge_profile,
+                            $match_quality_ratio
+                        ]
+                    }
+                );
+
+                if($res and ($res = $res->[0])) {
+
+                    if($res->{$auto_overlay_best_func} eq 't') {
+                        $logger->info("vl: $type auto-overlay-best succeeded for queued rec " . $rec->id);
+                        $imported = 1;
+                    } else {
+                        $report_args{import_error} = 'overlay.record.quality' if $match_quality_ratio > 0;
+                        $logger->info("vl: $type auto-overlay-best failed for queued rec " . $rec->id);
+                    }
+
+                } else {
+                    $error = 1;
+                    $logger->error("vl: Error attempting overlay with func=$auto_overlay_best_func, ".
+                        "quality_ratio=$match_quality_ratio, profile=$merge_profile, record=$rec_id");
+                }
+            }
+
+            if(!$imported and !$error and $import_no_match and scalar(@{$rec->matches}) == 0) {
             
                 # No overlay / merge occurred.  Do a traditional record import by creating a new record
             
diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index e45999d..0ad9ad0 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -8667,6 +8667,7 @@ INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missin
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.record.quality', oils_i18n_gettext('overlay.record.quality', 'New record had insufficient quality', 'vie', 'description') );
 
 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
     'ui.cat.volume_copy_editor.horizontal',
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 5b000b7..4aeef8f 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -974,7 +974,7 @@ function vlHandleQueueItemsAction(action) {
             queueItemsImportDialog.hide();
 
             // hack to set the widgets the import funcs will be looking at.  Reset them below.
-            vlUploadQueueAutoImport.attr('value',  vlUploadQueueAutoImport2.attr('value'));
+            vlUploadQueueImportNoMatch.attr('value',  vlUploadQueueImportNoMatch2.attr('value'));
             vlUploadQueueAutoOverlayExact.attr('value',  vlUploadQueueAutoOverlayExact2.attr('value'));
             vlUploadQueueAutoOverlay1Match.attr('value',  vlUploadQueueAutoOverlay1Match2.attr('value'));
             vlUploadMergeProfile.attr('value',  vlUploadMergeProfile2.attr('value'));
@@ -986,8 +986,8 @@ function vlHandleQueueItemsAction(action) {
             }
             
             // reset the widgets to prevent accidental future actions
-            vlUploadQueueAutoImport.attr('value',  false);
-            vlUploadQueueAutoImport2.attr('value', false);
+            vlUploadQueueImportNoMatch.attr('value',  false);
+            vlUploadQueueImportNoMatch2.attr('value', false);
             vlUploadQueueAutoOverlayExact.attr('value', false);
             vlUploadQueueAutoOverlayExact2.attr('value', false);
             vlUploadQueueAutoOverlay1Match.attr('value', false);
@@ -1054,23 +1054,45 @@ function vlImportAllRecords() {
         function(){displayGlobalDiv('vl-queue-div');});
 }
 
-function vlImportRecordQueue(type, queueId, noMatchOnly, onload) {
+function vlImportRecordQueue(type, queueId, onload) {
     displayGlobalDiv('vl-generic-progress-with-total');
     var method = 'open-ils.vandelay.bib_queue.import';
-    if(noMatchOnly)
-        method = method.replace('import', 'nomatch.import');
     if(type == 'auth')
         method = method.replace('bib', 'auth');
 
+
+    var mergeOpt = false;
     var options = {};
+
+    if(vlUploadQueueImportNoMatch.checked) {
+        options.import_no_match = true;
+        vlUploadQueueImportNoMatch.checked = false;
+    }
+
     if(vlUploadQueueAutoOverlayExact.checked) {
         options.auto_overlay_exact = true;
         vlUploadQueueAutoOverlayExact.checked = false;
+        mergeOpt = true;
+    }
+
+    if(vlUploadQueueAutoOverlayBestMatch.checked) {
+        options.auto_overlay_best_match = true;
+        vlUploadQueueAutoOverlayBestMatch.checked = false;
+        options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
+        mergeOpt = true;
     }
 
     if(vlUploadQueueAutoOverlay1Match.checked) {
         options.auto_overlay_1match = true;
         vlUploadQueueAutoOverlay1Match.checked = false;
+        mergeOpt = true;
+    }
+
+    if(!mergeOpt) {
+        // in the interest of speed, if no merge options are 
+        // chosen, tell the back-end code to only process records
+        // that have no matches
+        method = method.replace('.import', 'nomatch.import');
     }
     
     var profile = vlUploadMergeProfile.attr('value');
@@ -1102,16 +1124,19 @@ function batchUpload() {
     currentType = dijit.byId('vl-record-type').getValue();
 
     var handleProcessSpool = function() {
-        if(vlUploadQueueAutoImport.checked || vlUploadQueueAutoOverlayExact.checked || vlUploadQueueAutoOverlay1Match.checked) {
-            var noMatchOnly = !vlUploadQueueAutoOverlayExact.checked && !vlUploadQueueAutoOverlay1Match.checked;
-            vlImportRecordQueue(
-                currentType, 
-                currentQueueId, 
-                noMatchOnly,
-                function() {
-                    retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-                }
-            );
+        if( 
+            vlUploadQueueImportNoMatch.checked || 
+            vlUploadQueueAutoOverlayExact.checked || 
+            vlUploadQueueAutoOverlay1Match.checked ||
+            vlUploadQueueAutoOverlayBestMatch.checked ) {
+
+                vlImportRecordQueue(
+                    currentType, 
+                    currentQueueId, 
+                    function() {
+                        retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+                    }
+                );
         } else {
             retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
         }
diff --git a/Open-ILS/web/opac/locale/en-US/vandelay.dtd b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
index 48db0a6..d2d1789 100644
--- a/Open-ILS/web/opac/locale/en-US/vandelay.dtd
+++ b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
@@ -6,6 +6,7 @@
 <!ENTITY vandelay.auto.import.auto_overlay_1match "Merge/Overlay Single Matches">
 <!ENTITY vandelay.auto.import.auto_overlay_best "Merge/Overlay Best Match">
 <!ENTITY vandelay.auto.import.auto_overlay_best_ratio "Best Match Minimum Quality Ratio">
+<!ENTITY vandelay.auto.import.auto_overlay_best_ratio.desc "New Record Quaility / Quality of Best Match">
 <!ENTITY vandelay.auto.import.merge_profile "Merge/Overlay Profile">
 <!ENTITY vandelay.auto.width "Auto Width">
 <!ENTITY vandelay.back.to.import.queue "Back To Import Queue">
diff --git a/Open-ILS/web/templates/default/vandelay/inc/matches.tt2 b/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
index 9cccbdb..40d6a80 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
@@ -15,7 +15,7 @@
                     formatter : vlFormatViewMatchMARC
                 },
                 {name: 'Match Score', field:'match_score'},
-                {name: 'Match Quality', field:'match_quality'},
+                {name: 'Matched Record Quality', field:'match_quality'},
                 {name: '&vandelay.creator;', get: vlGetCreator},
                 {name: '&vandelay.create.date;', field:'create_date', get: vlGetDateTimeField},
                 {name: '&vandelay.last.edit.date;', field:'edit_date', get: vlGetDateTimeField},
diff --git a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2 b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
index 8462ebc..f742104 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
@@ -54,7 +54,7 @@
         <tr>
             <td>&vandelay.auto.import.noncolliding;</td>
             <td colspan='4'>
-                <input jsId='vlUploadQueueAutoImport' dojoType='dijit.form.CheckBox'/>
+                <input jsId='vlUploadQueueImportNoMatch' dojoType='dijit.form.CheckBox'/>
             </td>
         </tr>
         <tr>
@@ -75,7 +75,10 @@
         </tr>
         <tr>
             <td>&vandelay.auto.import.auto_overlay_best_ratio;</td>
-            <td colspan='4'><input style='width:3em' value='0.0' jsId='vlUploadQueueAutoOverlayBestMatchRatio' dojoType='dijit.form.TextBox'/></td>
+            <td colspan='4'>
+                <input style='width:3em' value='0.0' jsId='vlUploadQueueAutoOverlayBestMatchRatio' dojoType='dijit.form.TextBox'/>
+                <span style='padding-left: 10px; font-size:90%'>(&vandelay.auto.import.auto_overlay_best_ratio.desc;)</span>
+            </td>
         </tr>
 
         <tr><td colspan='2' style='border-bottom:1px solid #888'></td></tr>

commit 9e9c1c3b6ab1c85e803f7611a38cfd4a9113e027
Author: senator <lebbeous at esilibrary.com>
Date:   Wed May 4 16:41:20 2011 -0400

    working in quality metric editor

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 8aa6685..f7ad9e3 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -588,11 +588,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.match_set_quality_id_seq">
 			<field reporter:label="Quality Metric ID" name="id" reporter:datatype="id"/>
 			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
-			<field reporter:label="Coded Field" name="svf" reporter:datatype="text"/>
+			<field reporter:label="Record Attribute" name="svf" reporter:datatype="text"/>
 			<field reporter:label="Tag" name="tag" reporter:datatype="text"/>
 			<field reporter:label="Subfield" name="subfield" reporter:datatype="text"/>
 			<field reporter:label="Value" name="value" reporter:datatype="text"/>
-			<field reporter:label="Importance" name="quality" reporter:datatype="int"/>
+			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 		</fields>
 		<links>
 			<link field="match_set" reltype="has_a" key="id" map="" class="vms"/>
diff --git a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
index 5128bd2..e07eeca 100644
--- a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
@@ -9,25 +9,64 @@ dojo.require("openils.User");
 dojo.require("openils.Util");
 dojo.require("openils.PermaCrud");
 dojo.require("openils.widget.ProgressDialog");
+dojo.require("openils.widget.AutoGrid");
 
 var localeStrings, node_editor, _crads, CGI, tree, match_set;
 
-function _find_crad_by_name(name) {
-    for (var i = 0; i < _crads.length; i++) {
-        if (_crads[i].name() == name)
-            return _crads[i];
-    }
-    return null;
-}
+var NodeEditorAbstract = {
+    "_svf_select_template": null,
+    "_simple_value_getter": function(control) {
+        if (typeof control.selectedIndex != "undefined")
+            return control.options[control.selectedIndex].value;
+        else if (dojo.attr(control, "type") == "checkbox")
+            return control.checked;
+        else
+            return control.value;
+    },
+    "is_sensible": function(thing) {
+        var need_one = 0;
+        var list = openils.Util.objectProperties(this._factories_by_type);
+        console.log(list.join(" "));
+        openils.Util.objectProperties(this._factories_by_type).forEach(
+            function(field) { if (thing[field]()) need_one++; }
+        );
 
-function NodeEditor() {
-    var self = this;
+        if (need_one != 1) {
+            alert(localeStrings.POINT_NEEDS_ONE);
+            return false;
+        }
+
+        if (thing.tag()) {
+            if (
+                !thing.tag().match(/^\d{3}$/) ||
+                thing.subfield().length != 1 ||
+                !thing.subfield().match(/\S/) ||
+                thing.subfield().charCodeAt(0) < 32
+            ) {
+                alert(localeStrings.FAULTY_MARC);
+                return false;
+            }
+        }
 
-    var _svf_select_template = null;
-    var _factories_by_type = {
+        return true;
+    },
+    "_add_consistent_controls": function(tgt) {
+        if (!this._consistent_controls) {
+            var trs = dojo.query(this._consistent_controls_query);
+            this._consistent_controls = [];
+            for (var i = 0; i < trs.length; i++)
+                this._consistent_controls[i] = dojo.clone(trs[i]);
+            dojo.empty(trs[0].parentNode);
+        }
+
+        this._consistent_controls.forEach(
+            function(node) { dojo.place(dojo.clone(node), tgt); }
+        );
+    },
+    "_factories_by_type": {
         "svf": function() {
-            if (!_svf_select_template) {
-                _svf_select_template = dojo.create(
+            if (!self._svf_select_template) {
+                self._svf_select_template = dojo.create(
                     "select", {"fmfield": "svf"}
                 );
                 for (var i=0; i<_crads.length; i++) {
@@ -35,12 +74,12 @@ function NodeEditor() {
                         "option", {
                             "value": _crads[i].name(),
                             "innerHTML": _crads[i].label()
-                        }, _svf_select_template
+                        }, self._svf_select_template
                     );
                 }
             }
 
-            var select = dojo.clone(_svf_select_template);
+            var select = dojo.clone(self._svf_select_template);
             dojo.attr(select, "id", "svf-select");
             var label = dojo.create(
                 "label", {
@@ -85,72 +124,104 @@ function NodeEditor() {
                 }, dojo.create("td", null, rows[1])
             );
             return rows;
-        },
-        "bool_op": function() {
-            var tr = dojo.create("tr");
-            dojo.create(
-                "label",
-                {"for": "operator-select", "innerHTML": "Operator:"},
-                dojo.create("td", null, tr)
-            );
-            var select = dojo.create(
-                "select", {"fmfield": "bool_op", "id": "operator-select"},
-                dojo.create("td", null, tr)
-            );
-            dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
-            dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
-
-            return [tr];
         }
-    };
+    }
+};
 
-    function _simple_value_getter(control) {
-        if (typeof control.selectedIndex != "undefined")
-            return control.options[control.selectedIndex].value;
-        else if (dojo.attr(control, "type") == "checkbox")
-            return control.checked;
-        else
-            return control.value;
-    };
+function _find_crad_by_name(name) {
+    for (var i = 0; i < _crads.length; i++) {
+        if (_crads[i].name() == name)
+            return _crads[i];
+    }
+    return null;
+}
 
-    this._init = function(dnd_source, node_editor_container) {
+function QualityNodeEditor() {
+    var self = this;
+
+    this._init = function(dnd_source, qnode_editor_container) {
+        this._consistent_controls_query =
+            "[consistent-controls], [quality-controls]";
         this.dnd_source = dnd_source;
-        this.node_editor_container = dojo.byId(node_editor_container);
+        this.qnode_editor_container = dojo.byId(qnode_editor_container);
     };
 
     this.clear = function() {
         this.dnd_source.selectAll().deleteSelectedNodes();
         dojo.create(
             "em", {"innerHTML": localeStrings.WORKING_MP_HERE},
-            this.node_editor_container, "only"
+            this.qnode_editor_container, "only"
         );
         this.dnd_source._ready = false;
     };
 
-    this.is_sensible = function(mp) {
-        var need_one = 0;
-        ["tag", "svf", "bool_op"].forEach(
-            function(field) { if (mp[field]()) need_one++; }
+    this.build_vmsq = function() {
+        var metric = new vmsq();
+        var controls = dojo.query("[fmfield]", this.qnode_editor_container);
+        for (var i = 0; i < controls.length; i++) {
+            var field = dojo.attr(controls[i], "fmfield");
+            var value = this._simple_value_getter(controls[i]);
+            metric[field](value);
+        }
+
+        if (!this.is_sensible(metric)) return null;    /* will alert() */
+        else return metric;
+    };
+
+    this.add = function(type) {
+        this.clear();
+
+
+        /* these are the editing widgets */
+        var table = dojo.create("table", {"className": "node-editor"});
+
+        var nodes = this._factories_by_type[type]();
+        for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
+
+        this._add_consistent_controls(table);
+
+        dojo.create(
+            "input", {
+                "type": "submit", "value": localeStrings.OK,
+                "onclick": function() { alert("would update draggable here"/* XXX TODO */); }
+            }, dojo.create(
+                "td", {"colspan": 2, "align": "center"},
+                dojo.create("tr", null, table)
+            )
         );
 
-        if (need_one != 1) {
-            alert(localeStrings.POINT_NEEDS_ONE);
-            return false;
-        }
+        dojo.place(table, this.qnode_editor_container, "only");/* XXX put in dialog here */
 
-        if (mp.tag()) {
-            if (
-                !mp.tag().match(/^\d{3}$/) ||
-                mp.subfield().length != 1 ||
-                !mp.subfield().match(/\S/) ||
-                mp.subfield().charCodeAt(0) < 32
-            ) {
-                alert(localeStrings.FAULTY_MARC);
-                return false;
-            }
-        }
+        //this.dnd_source.insertNodes(false, [draggable]);
 
-        return true;
+        /* nice */
+        try { dojo.query("select, input", table)[0].focus(); }
+        catch(E) { console.log(String(E)); }
+
+    };
+
+    openils.Util.objectProperties(NodeEditorAbstract).forEach(
+        function(m) { self[m] = NodeEditorAbstract[m]; }
+    );
+    this._init.apply(this, arguments);
+}
+
+function NodeEditor() {
+    var self = this;
+
+    this._init = function(dnd_source, node_editor_container) {
+        this._consistent_controls_query = "[consistent-controls]";
+        this.dnd_source = dnd_source;
+        this.node_editor_container = dojo.byId(node_editor_container);
+    };
+
+    this.clear = function() {
+        this.dnd_source.selectAll().deleteSelectedNodes();
+        dojo.create(
+            "em", {"innerHTML": localeStrings.WORKING_MP_HERE},
+            this.node_editor_container, "only"
+        );
+        this.dnd_source._ready = false;
     };
 
     this.build_vmsp = function() {
@@ -158,7 +229,7 @@ function NodeEditor() {
         var controls = dojo.query("[fmfield]", this.node_editor_container);
         for (var i = 0; i < controls.length; i++) {
             var field = dojo.attr(controls[i], "fmfield");
-            var value = _simple_value_getter(controls[i]);
+            var value = this._simple_value_getter(controls[i]);
             match_point[field](value);
         }
 
@@ -176,20 +247,6 @@ function NodeEditor() {
         this.dnd_source._ready = true;
     };
 
-    this._add_consistent_controls = function(tgt) {
-        if (!this._consistent_controls) {
-            var trs = dojo.query("[consistent-controls]");
-            this._consistent_controls = [];
-            for (var i = 0; i < trs.length; i++)
-                this._consistent_controls[i] = dojo.clone(trs[i]);
-            dojo.empty(trs[0].parentNode);
-        }
-
-        this._consistent_controls.forEach(
-            function(node) { dojo.place(dojo.clone(node), tgt); }
-        );
-    };
-
     this.add = function(type) {
         this.clear();
 
@@ -202,7 +259,7 @@ function NodeEditor() {
         /* these are the editing widgets */
         var table = dojo.create("table", {"className": "node-editor"});
 
-        var nodes = _factories_by_type[type]();
+        var nodes = this._factories_by_type[type]();
         for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
 
         if (type != "bool_op")
@@ -228,6 +285,26 @@ function NodeEditor() {
 
     };
 
+    openils.Util.objectProperties(NodeEditorAbstract).forEach(
+        function(m) { self[m] = NodeEditorAbstract[m]; }
+    );
+
+    this._factories_by_type.bool_op = function() {
+        var tr = dojo.create("tr");
+        dojo.create(
+            "label",
+            {"for": "operator-select", "innerHTML": "Operator:"},
+            dojo.create("td", null, tr)
+        );
+        var select = dojo.create(
+            "select", {"fmfield": "bool_op", "id": "operator-select"},
+            dojo.create("td", null, tr)
+        );
+        dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
+        dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
+
+        return [tr];
+    };
     this._init.apply(this, arguments);
 }
 
@@ -401,6 +478,14 @@ function save_tree() {
         }
     );
 }
+
+function init_vmsq_grid() {
+    vmsq_grid.loadAll(
+        {"order_by": {"vmsq": "quality"}},
+        {"match_set": CGI.param("match_set")}
+    );
+}
+
 function my_init() {
     progress_dialog.show(true);
 
@@ -473,6 +558,9 @@ function my_init() {
 
     redraw_expression_preview();
     node_editor.clear();
+
+    init_vmsq_grid();
+
     progress_dialog.hide();
 }
 
diff --git a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
index 4290e0b..7a11223 100644
--- a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
@@ -68,6 +68,14 @@
             <input id="negate-input" type="checkbox" fmfield="negate" />
         </td>
     </tr>
+    <tr quality-controls="1">
+        <td>
+            <label for="value-input">Value:</label>
+        </td>
+        <td>
+            <input id="value-input" type="text" fmfield="value" />
+        </td>
+    </tr>
 </table>
 <div class="outer">
     <div id="expr-preview-row">
@@ -102,8 +110,37 @@
     </div>
 </div>
 <div id="submit-row">
-    <hr />
-    <button onclick="save_tree()">Save Changes</button>
+    <button onclick="save_tree()">Save Changes To Expression</button>
+</div>
+<hr />
+<div id="quality-editor-wrapper">
+    <div>
+        <div style="float: left; width: 50%;">
+            <big>Quality Metrics for this Match Set</big>
+        </div>
+        <div style="float: right; width: 49%; text-align: right;">
+            <button onclick="qnode_editor.add('svf')">
+                Record Attribute
+            </button>
+            <button onclick="qnode_editor.add('tag')">
+                MARC Tag and Subfield
+            </button>
+            <button onclick="vmsq_grid.deleteSelected()">
+                Delete Selected Metrics
+            </button>
+        </div>
+    </div>
+    <br style="clear: both;" />
+    <table jsId="vmsq_grid"
+        dojoType="openils.widget.AutoGrid"
+        query="{id: '*'}"
+        defaultCellWidth="'17%'"
+        fmClass="vmsq"
+        fieldOrder="['quality','svf','tag','subfield','value']"
+        suppressFields="['match_set']"
+        showPaginator="true"
+        editOnEnter="false">
+    </table>
 </div>
 <div jsId="progress_dialog" dojoType="openils.widget.ProgressDialog"></div>
 <script type="text/javascript"

commit aa4fbb88b33a5b8f55295605caecf4841cfbb05a
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 15:58:37 2011 -0400

    grab attr value from vandelay.extract_rec_attrs hstore to compare to configured value

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 4082296..cc02d17 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -736,7 +736,7 @@ BEGIN
                 END IF;
             END LOOP;
         ELSE
-            IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) THEN
+            IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) -> test.svf THEN
                 out_q := out_q + test.quality;
             END IF;
         END IF;

commit 5a13cc0b51f5d5a96a125beac16bf587c6189701
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 15:45:55 2011 -0400

    repaired copy/paste error in match_bib_record

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 8c18550..4082296 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -689,7 +689,7 @@ BEGIN
 
     SELECT * INTO my_bib_queue FROM vandelay.bib_queue WHERE id = NEW.queue;
 
-    NEW.quality := vandelay.measure_record_quality( b.marc, my_bib_queue.match_set );
+    NEW.quality := vandelay.measure_record_quality( NEW.marc, my_bib_queue.match_set );
 
     -- Perfect matches on 901$c exit early with a match with high quality.
     incoming_existing_id :=

commit b212468779aa74dd719cb9ac614919c749927260
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed May 4 15:44:19 2011 -0400

    Allow different quality measures for different values on the same tag+sf/svf

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index b5d72df..8c18550 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -42,7 +42,7 @@ CREATE TABLE vandelay.match_set_quality (
     CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
     CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
 );
-CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''));
+CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''), value);
 
 
 CREATE TABLE vandelay.queue (

commit 45cf5cbebb15792d0439346d857e0a85aee7200d
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed May 4 14:43:59 2011 -0400

    Save incumbent record quality in bib_match.quality, incoming in queued_record.quality

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index c18fe36..b5d72df 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -143,7 +143,7 @@ CREATE TABLE vandelay.bib_match (
 	id				BIGSERIAL	PRIMARY KEY,
 	queued_record	BIGINT		REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    quality         INT         NOT NULL DEFAULT 0,
+    quality         INT         NOT NULL DEFAULT 1,
     match_score     INT         NOT NULL DEFAULT 0
 );
 
@@ -687,6 +687,10 @@ BEGIN
 
     DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
 
+    SELECT * INTO my_bib_queue FROM vandelay.bib_queue WHERE id = NEW.queue;
+
+    NEW.quality := vandelay.measure_record_quality( b.marc, my_bib_queue.match_set );
+
     -- Perfect matches on 901$c exit early with a match with high quality.
     incoming_existing_id :=
         oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
@@ -699,17 +703,17 @@ BEGIN
         END IF;
     END IF;
 
-    SELECT * INTO my_bib_queue FROM vandelay.bib_queue WHERE id = NEW.queue;
 
     FOR test_result IN SELECT * FROM
         vandelay.match_set_test_marcxml(my_bib_queue.match_set, NEW.marc) LOOP
 
-        INSERT INTO vandelay.bib_match (
-            queued_record, eg_record, match_score, quality
-        ) VALUES (
-            NEW.id, test_result.record,
-            test_result.quality, vandelay.incoming_record_quality(NEW.marc, my_bib_queue.match_set)
-        );
+        INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
+	SELECT  NEW.id,
+	        test_result.record,
+                test_result.quality,
+		vandelay.measure_record_quality( b.marc, my_bib_queue.match_set )
+	  FROM  biblio.record_entry b
+	  WHERE id = test_result.record;
 
     END LOOP;
 
@@ -717,7 +721,7 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
-CREATE OR REPLACE FUNCTION vandelay.incoming_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
+CREATE OR REPLACE FUNCTION vandelay.measure_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
 DECLARE
     out_q   INT := 0;
     rvalue  TEXT;
@@ -1283,14 +1287,13 @@ BEGIN
 END;
 $$ LANGUAGE PLPGSQL;
 
-CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS BOOL AS $$
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
 DECLARE
     eg_id           BIGINT;
+    lwm_ratio_value NUMERIC;
 BEGIN
 
-    IF lwm_ratio_value IS NULL THEN
-        lwm_ratio_value := 0.0;
-    END IF;
+    lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
 
     PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
 
@@ -1305,9 +1308,9 @@ BEGIN
             JOIN vandelay.bib_queue q ON (qr.queue = q.id)
             JOIN biblio.record_entry r ON (r.id = m.eg_record)
       WHERE m.queued_record = import_id
-            AND m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc, q.match_set),0),1)::NUMERIC >= lwm_ratio_value
+            AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
       ORDER BY  m.match_score DESC, -- required match score
-                m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc, q.match_set),0),1)::NUMERIC DESC, -- quality tie breaker
+                qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
                 m.id -- when in doubt, use the first match
       LIMIT 1;
 

commit 1395a488c2db041cffc9558d48c2beaef276845a
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 14:37:25 2011 -0400

    initial support for selecting merge-on-best-match and setting minimum quality ratio

diff --git a/Open-ILS/web/opac/locale/en-US/vandelay.dtd b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
index a97639f..48db0a6 100644
--- a/Open-ILS/web/opac/locale/en-US/vandelay.dtd
+++ b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
@@ -1,9 +1,11 @@
 <!ENTITY vandelay.add.existing.queue "or Add to an Existing Queue">
 <!ENTITY vandelay.auth.attrs "Authority attributes">
 <!ENTITY vandelay.auth.records "Authority Records">
-<!ENTITY vandelay.auto.import.noncolliding "Auto-Import Non-Colliding Records">
-<!ENTITY vandelay.auto.import.auto_overlay_exact "Auto Merge/Overlay Exact Matches">
-<!ENTITY vandelay.auto.import.auto_overlay_1match "Auto Merge/Overlay When Exactly 1 Match is Found">
+<!ENTITY vandelay.auto.import.noncolliding "Import Non-Colliding Records">
+<!ENTITY vandelay.auto.import.auto_overlay_exact "Merge/Overlay Exact Matches (901c)">
+<!ENTITY vandelay.auto.import.auto_overlay_1match "Merge/Overlay Single Matches">
+<!ENTITY vandelay.auto.import.auto_overlay_best "Merge/Overlay Best Match">
+<!ENTITY vandelay.auto.import.auto_overlay_best_ratio "Best Match Minimum Quality Ratio">
 <!ENTITY vandelay.auto.import.merge_profile "Merge/Overlay Profile">
 <!ENTITY vandelay.auto.width "Auto Width">
 <!ENTITY vandelay.back.to.import.queue "Back To Import Queue">
diff --git a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2 b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
index 909670e..8462ebc 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
@@ -69,6 +69,15 @@
                 <input jsId='vlUploadQueueAutoOverlay1Match' dojoType='dijit.form.CheckBox'/>
             </td>
         </tr>
+        <tr>
+            <td>&vandelay.auto.import.auto_overlay_best;</td>
+            <td colspan='4'><input jsId='vlUploadQueueAutoOverlayBestMatch' dojoType='dijit.form.CheckBox'/></td>
+        </tr>
+        <tr>
+            <td>&vandelay.auto.import.auto_overlay_best_ratio;</td>
+            <td colspan='4'><input style='width:3em' value='0.0' jsId='vlUploadQueueAutoOverlayBestMatchRatio' dojoType='dijit.form.TextBox'/></td>
+        </tr>
+
         <tr><td colspan='2' style='border-bottom:1px solid #888'></td></tr>
         <tr>
             <td colspan='5'>

commit 054ac72165d347166553d3b0987d1442c194ee2d
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 13:14:38 2011 -0400

    show match score/quality in VL record match grid

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index bb6ce0c..5b000b7 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -464,26 +464,24 @@ function vlLoadMatchUI(recId) {
 
                 // build the data store of records with match information
                 var dataStore = bre.toStoreData(recs, null, 
-                    {virtualFields:['_id']});
+                    {virtualFields:['_id', 'match_score', 'match_quality']});
                 dataStore.identifier = '_id';
 
                 var matchSeenMap = {};
 
-                // XXX much of this is no longer needed with changes to match_set
                 for(var i = 0; i < dataStore.items.length; i++) {
                     var item = dataStore.items[i];
                     item._id = i; // just need something unique
-                    /*
                     for(var j = 0; j < matches.length; j++) {
                         var match = matches[j];
                         if(match.eg_record() == item.id && !matchSeenMap[match.id()]) {
-                            var attr = getRecAttrFromMatch(queuedRecordsMap[recId], match);
-                            item.src_matchpoint = getRecAttrDefFromAttr(attr, currentType).code();
+                            if(match.match_score)
+                                item.match_score = match.match_score();
+                            item.match_quality = match.quality();
                             matchSeenMap[match.id()] = 1;
                             break;
                         }
                     }
-                    */
                 }
 
                 // now populate the grid
diff --git a/Open-ILS/web/templates/default/vandelay/inc/matches.tt2 b/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
index 39f8261..9cccbdb 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
@@ -14,6 +14,8 @@
                     get: vlGetViewMARC, 
                     formatter : vlFormatViewMatchMARC
                 },
+                {name: 'Match Score', field:'match_score'},
+                {name: 'Match Quality', field:'match_quality'},
                 {name: '&vandelay.creator;', get: vlGetCreator},
                 {name: '&vandelay.create.date;', field:'create_date', get: vlGetDateTimeField},
                 {name: '&vandelay.last.edit.date;', field:'edit_date', get: vlGetDateTimeField},

commit d856654f61afd4c674440c2d2b068336c3d5dfbd
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 13:14:01 2011 -0400

    added missing bib_match.match_score field to IDL

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 068403d..8aa6685 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -394,6 +394,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Queued Record" name="queued_record" reporter:datatype="link"/>
 			<field reporter:label="Evergreen Record" name="eg_record" reporter:datatype="link"/>
 			<field reporter:label="Quality" name="quality" reporter:datatype="text"/>
+			<field reporter:label="Match Score" name="match_score" reporter:datatype="text"/>
 		</fields>
 		<links>
 			<link field="queued_record" reltype="has_a" key="id" map="" class="vqbr"/>

commit 76699369880f251fa0aae5f936642e53cb658a92
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 12:03:22 2011 -0400

    slight mods to vandelay match set page to sync w/ updated bib/auth match table layout

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index ad93a3c..bb6ce0c 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -444,7 +444,7 @@ function vlLoadMatchUI(recId) {
     var params = [records];
     if(currentType == 'auth') {
         retrieve = ['open-ils.cat', 'open-ils.cat.authority.record.retrieve'];
-        parmas = [authtoken, records, {clear_marc:1}];
+        params = [authtoken, records, {clear_marc:1}];
     }
 
     fieldmapper.standardRequest(
@@ -464,24 +464,26 @@ function vlLoadMatchUI(recId) {
 
                 // build the data store of records with match information
                 var dataStore = bre.toStoreData(recs, null, 
-                    {virtualFields:['dest_matchpoint', 'src_matchpoint', '_id']});
+                    {virtualFields:['_id']});
                 dataStore.identifier = '_id';
 
                 var matchSeenMap = {};
 
+                // XXX much of this is no longer needed with changes to match_set
                 for(var i = 0; i < dataStore.items.length; i++) {
                     var item = dataStore.items[i];
                     item._id = i; // just need something unique
+                    /*
                     for(var j = 0; j < matches.length; j++) {
                         var match = matches[j];
                         if(match.eg_record() == item.id && !matchSeenMap[match.id()]) {
-                            item.dest_matchpoint = match.field_type();
                             var attr = getRecAttrFromMatch(queuedRecordsMap[recId], match);
                             item.src_matchpoint = getRecAttrDefFromAttr(attr, currentType).code();
                             matchSeenMap[match.id()] = 1;
                             break;
                         }
                     }
+                    */
                 }
 
                 // now populate the grid
@@ -554,6 +556,7 @@ function getRecMatchesFromAttrCode(rec, attrCode) {
 }
 */
 
+/*
 function getRecAttrFromMatch(rec, match) {
     for(var i = 0; i < rec.attributes().length; i++) {
         var attr = rec.attributes()[i];
@@ -561,6 +564,7 @@ function getRecAttrFromMatch(rec, match) {
             return attr;
     }
 }
+*/
 
 function getRecAttrDefFromAttr(attr, type) {
     var defs = (type == 'bib') ? bibAttrDefs : authAttrDefs;
diff --git a/Open-ILS/web/templates/default/vandelay/inc/matches.tt2 b/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
index 90f3546..39f8261 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/matches.tt2
@@ -8,13 +8,7 @@
                     name: '&vandelay.overlay.target;', 
                     get: vlGetOverlayTargetSelector,
                     formatter : vlFormatOverlayTargetSelector,
-                    /*
-                    value: '&lt;input type="checkbox" name="vl-overlay-target-RECID" '+
-                        'onclick="vlHandleOverlayTargetSelected(ID, GRIDID);" gridid="GRIDID" match="ID"/>'
-                        */
                 },
-                {name:'&vandelay.source.match.point;', field:'src_matchpoint'},
-                {name:'&vandelay.dest.match.point;', field:'dest_matchpoint'},
                 {name: '&vandelay.id;', field:'id'},
                 {   name: '&vandelay.view.marc;', 
                     get: vlGetViewMARC, 

commit 9af9f804e7e272845b0dce09d98c1f88f0097670
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed May 4 11:52:31 2011 -0400

    Give authority flattening the same treatment as biblio flattening

diff --git a/Open-ILS/src/sql/Pg/030.schema.metabib.sql b/Open-ILS/src/sql/Pg/030.schema.metabib.sql
index 22bc674..9f00d0c 100644
--- a/Open-ILS/src/sql/Pg/030.schema.metabib.sql
+++ b/Open-ILS/src/sql/Pg/030.schema.metabib.sql
@@ -409,6 +409,27 @@ CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( BIGINT ) RETURNS
 	SELECT * FROM biblio.extract_metabib_field_entry($1, ' ');
 $func$ LANGUAGE SQL;
 
+CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
+DECLARE
+	auth	authority.record_entry%ROWTYPE;
+	output	authority.full_rec%ROWTYPE;
+	field	RECORD;
+BEGIN
+	SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
+
+	FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
+		output.record := rid;
+		output.ind1 := field.ind1;
+		output.ind2 := field.ind2;
+		output.tag := field.tag;
+		output.subfield := field.subfield;
+		output.value := field.value;
+
+		RETURN NEXT output;
+	END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
 CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
 DECLARE
 	bib	biblio.record_entry%ROWTYPE;
diff --git a/Open-ILS/src/sql/Pg/999.functions.global.sql b/Open-ILS/src/sql/Pg/999.functions.global.sql
index 66532cb..0152417 100644
--- a/Open-ILS/src/sql/Pg/999.functions.global.sql
+++ b/Open-ILS/src/sql/Pg/999.functions.global.sql
@@ -1378,67 +1378,6 @@ CREATE OR REPLACE FUNCTION authority.propagate_changes (aid BIGINT) RETURNS SETO
     SELECT authority.propagate_changes( authority, bib ) FROM authority.bib_linking WHERE authority = $1;
 $func$ LANGUAGE SQL;
 
-CREATE OR REPLACE FUNCTION authority.flatten_marc ( TEXT ) RETURNS SETOF authority.full_rec AS $func$
-
-use MARC::Record;
-use MARC::File::XML (BinaryEncoding => 'UTF-8');
-use MARC::Charset;
-
-MARC::Charset->assume_unicode(1);
-
-my $xml = shift;
-my $r = MARC::Record->new_from_xml( $xml );
-
-return_next( { tag => 'LDR', value => $r->leader } );
-
-for my $f ( $r->fields ) {
-    if ($f->is_control_field) {
-        return_next({ tag => $f->tag, value => $f->data });
-    } else {
-        for my $s ($f->subfields) {
-            return_next({
-                tag      => $f->tag,
-                ind1     => $f->indicator(1),
-                ind2     => $f->indicator(2),
-                subfield => $s->[0],
-                value    => $s->[1]
-            });
-
-        }
-    }
-}
-
-return undef;
-
-$func$ LANGUAGE PLPERLU;
-
-CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
-DECLARE
-    auth    authority.record_entry%ROWTYPE;
-    output    authority.full_rec%ROWTYPE;
-    field    RECORD;
-BEGIN
-    SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
-
-    FOR field IN SELECT * FROM authority.flatten_marc( auth.marc ) LOOP
-        output.record := rid;
-        output.ind1 := field.ind1;
-        output.ind2 := field.ind2;
-        output.tag := field.tag;
-        output.subfield := field.subfield;
-        IF field.subfield IS NOT NULL THEN
-            output.value := naco_normalize(field.value, field.subfield);
-        ELSE
-            output.value := field.value;
-        END IF;
-
-        CONTINUE WHEN output.value IS NULL;
-
-        RETURN NEXT output;
-    END LOOP;
-END;
-$func$ LANGUAGE PLPGSQL;
-
 -- authority.rec_descriptor appears to be unused currently
 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
 BEGIN

commit 5c858d48ef21bb7ed74697b209120faf88c5fcef
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed May 4 11:08:13 2011 -0400

    Only ingest bibs and items and run matches /before/ we import a bib

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 88de9d7..c18fe36 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -681,6 +681,10 @@ DECLARE
     test_result             vandelay.match_set_test_result%ROWTYPE;
     tmp_rec                 BIGINT;
 BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
     DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
 
     -- Perfect matches on 901$c exit early with a match with high quality.
@@ -1669,6 +1673,10 @@ DECLARE
     atype   TEXT;
     adef    RECORD;
 BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
     FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
 
         SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
@@ -1688,6 +1696,10 @@ DECLARE
     item_data   vandelay.import_item%ROWTYPE;
 BEGIN
 
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
     SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
 
     FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
@@ -1824,6 +1836,10 @@ $func$ LANGUAGE PLPGSQL;
 
 CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
 BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
     DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
     DELETE FROM vandelay.import_item WHERE record = OLD.id;
 
@@ -1897,6 +1913,10 @@ DECLARE
     atype   TEXT;
     adef    RECORD;
 BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
     FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
 
         SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
@@ -1912,6 +1932,10 @@ $$ LANGUAGE PLPGSQL;
 
 CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
 BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
     DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
     IF TG_OP = 'UPDATE' THEN
         RETURN NEW;

commit c3201eb4258ce9f1317910417ea6d3b810354727
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 10:45:57 2011 -0400

    more vandelay menubar slimming

diff --git a/Open-ILS/web/opac/locale/en-US/vandelay.dtd b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
index f8fa799..a97639f 100644
--- a/Open-ILS/web/opac/locale/en-US/vandelay.dtd
+++ b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
@@ -26,7 +26,7 @@
 <!ENTITY vandelay.done "Done">
 <!ENTITY vandelay.edit.attributes "Record Display Attributes">
 <!ENTITY vandelay.edit.attrs "Record Display Attributes">
-<!ENTITY vandelay.edit.profiles "Edit Merge / Overlay Profiles">
+<!ENTITY vandelay.edit.profiles "Merge / Overlay Profiles">
 <!ENTITY vandelay.edit.match_set "Record Match Sets">
 <!ENTITY vandelay.edit.import_item_attrs "Import Item Attributes">
 <!ENTITY vandelay.false "False">

commit 8c40a2e78e76a495bd3d77abdf565ac1175c89c0
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 10:45:40 2011 -0400

    don't match on deleted bib records

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index eb089a4..88de9d7 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -522,7 +522,7 @@ BEGIN
         FROM _vandelay_tmp_jrows;
 
     -- add those joins and the where clause to our query.
-    query_ := query_ || joins || E'\n' || 'WHERE ' || wq;
+    query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
 
     -- this will return rows of record,quality
     FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP

commit b27b7881d3c3c776bca73b78e02149b3a9459f4c
Author: berick <berick at esilibrary.com>
Date:   Wed May 4 10:13:42 2011 -0400

    also remove matched_set from IDL

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index dd522fa..068403d 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -392,14 +392,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.bib_match_id_seq">
 			<field reporter:label="Match ID" name="id" reporter:datatype="id"/>
 			<field reporter:label="Queued Record" name="queued_record" reporter:datatype="link"/>
-			<field reporter:label="Matched Set" name="matched_set" reporter:datatype="link"/>
 			<field reporter:label="Evergreen Record" name="eg_record" reporter:datatype="link"/>
 			<field reporter:label="Quality" name="quality" reporter:datatype="text"/>
 		</fields>
 		<links>
 			<link field="queued_record" reltype="has_a" key="id" map="" class="vqbr"/>
 			<link field="eg_record" reltype="has_a" key="id" map="" class="bre"/>
-			<link field="matched_set" reltype="has_a" key="id" map="" class="vms"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>

commit 8c223ecec89a5e097f13d788e9db016544f761e9
Author: Mike Rylander <mrylander at gmail.com>
Date:   Tue May 3 20:23:57 2011 -0400

    Clean up uses of vandelay.incoming_record_quality to provide all required params; Remove matched_set on matches, just look at the match_set for the queue of the vandelay record in question

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index e639998..eb089a4 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -141,7 +141,6 @@ CREATE INDEX queued_bib_record_attr_record_idx ON vandelay.queued_bib_record_att
 
 CREATE TABLE vandelay.bib_match (
 	id				BIGSERIAL	PRIMARY KEY,
-	matched_set 	INT			REFERENCES vandelay.match_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	queued_record	BIGINT		REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     quality         INT         NOT NULL DEFAULT 0,
@@ -702,10 +701,10 @@ BEGIN
         vandelay.match_set_test_marcxml(my_bib_queue.match_set, NEW.marc) LOOP
 
         INSERT INTO vandelay.bib_match (
-            matched_set, queued_record, eg_record, match_score, quality
+            queued_record, eg_record, match_score, quality
         ) VALUES (
-            my_bib_queue.match_set, NEW.id, test_result.record,
-            test_result.quality, vandelay.incoming_record_quality(NEW.marc)
+            NEW.id, test_result.record,
+            test_result.quality, vandelay.incoming_record_quality(NEW.marc, my_bib_queue.match_set)
         );
 
     END LOOP;
@@ -1298,17 +1297,18 @@ BEGIN
 
     SELECT  m.eg_record INTO eg_id
       FROM  vandelay.bib_match m
-            JOIN biblio.record_entry r
+            JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
+            JOIN vandelay.bib_queue q ON (qr.queue = q.id)
+            JOIN biblio.record_entry r ON (r.id = m.eg_record)
       WHERE m.queued_record = import_id
-            AND r.id = m.eg_record
-            AND m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc),0),1)::NUMERIC >= lwm_ratio_value
-      ORDER BY  m.match_score DESC,
-                m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc),0),1)::NUMERIC DESC,
-                id
+            AND m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc, q.match_set),0),1)::NUMERIC >= lwm_ratio_value
+      ORDER BY  m.match_score DESC, -- required match score
+                m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc, q.match_set),0),1)::NUMERIC DESC, -- quality tie breaker
+                m.id -- when in doubt, use the first match
       LIMIT 1;
 
     IF eg_id IS NULL THEN
-        -- RAISE NOTICE 'incoming record is not of hight enough quality';
+        -- RAISE NOTICE 'incoming record is not of high enough quality';
         RETURN FALSE;
     END IF;
 
@@ -1886,7 +1886,6 @@ CREATE INDEX queued_authority_record_attr_record_idx ON vandelay.queued_authorit
 
 CREATE TABLE vandelay.authority_match (
 	id				BIGSERIAL	PRIMARY KEY,
-	matched_set 	INT			REFERENCES vandelay.match_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	queued_record	BIGINT		REFERENCES vandelay.queued_authority_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	eg_record		BIGINT		REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
     quality         INT         NOT NULL DEFAULT 0

commit 065b545d3e1ad5ab06205ce8205d49b7dc632610
Author: Mike Rylander <mrylander at gmail.com>
Date:   Tue May 3 20:06:46 2011 -0400

    Add MARC::Charset dance to the remaining plperlu function

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index c5740d7..e639998 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -321,6 +321,10 @@ CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.fl
 
 use MARC::Record;
 use MARC::File::XML (BinaryEncoding => 'UTF-8');
+use MARC::Charset;
+use strict;
+
+MARC::Charset->assume_unicode(1);
 
 my $xml = shift;
 my $r = MARC::Record->new_from_xml( $xml );

commit b81fd284cb7e600c6f4011741f9d7f91618cab12
Author: berick <berick at esilibrary.com>
Date:   Tue May 3 15:18:55 2011 -0400

    match set selection support in vl uploage UI, part 1

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 0820cb0..510d6bf 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -60,6 +60,7 @@ sub create_bib_queue {
     my $name = shift;
     my $owner = shift;
     my $type = shift;
+    my $match_set = shift;
     my $import_def = shift;
 
     my $e = new_editor(authtoken => $auth, xact => 1);
@@ -78,6 +79,7 @@ sub create_bib_queue {
     $queue->owner( $owner );
     $queue->queue_type( $type ) if ($type);
     $queue->item_attr_def( $import_def ) if ($import_def);
+    $queue->match_set($match_set) if $match_set;
 
     my $new_q = $e->create_vandelay_bib_queue( $queue );
     return $e->die_event unless ($new_q);
@@ -100,6 +102,7 @@ sub create_auth_queue {
     my $name = shift;
     my $owner = shift;
     my $type = shift;
+    my $match_set = shift;
 
     my $e = new_editor(authtoken => $auth, xact => 1);
 
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 6ce390f..ad93a3c 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -86,13 +86,14 @@ var cgi = new openils.CGI();
 var vlQueueGridColumePicker = {};
 var vlBibSources = [];
 var importItemDefs = [];
+var matchSets = {};
 
 /**
   * Grab initial data
   */
 function vlInit() {
     authtoken = openils.User.authtoken;
-    var initNeeded = 6; // how many async responses do we need before we're init'd 
+    var initNeeded = 7; // how many async responses do we need before we're init'd 
     var initCount = 0; // how many async reponses we've received
 
     openils.Util.registerEnterHandler(
@@ -165,6 +166,23 @@ function vlInit() {
         }
     );
 
+    new openils.PermaCrud().search('vms',
+        {owner: owner.map(function(org) { return org.id(); })},
+        {   async: true,
+            oncomplete: function(r) {
+                var sets = openils.Util.readResponse(r);
+                dojo.forEach(sets, 
+                    function(set) {
+                        if(!matchSets[set.mtype()])
+                            matchSets[set.mtype()] = [];
+                        matchSets[set.mtype()].push(set);
+                    }
+                );
+                checkInitDone();
+            }
+        }
+    );
+
     vlAttrEditorInit();
 }
 
@@ -319,13 +337,13 @@ function uploadMARC(onload){
 /**
   * Creates a new vandelay queue
   */
-function createQueue(queueName, type, onload, importDefId) {
+function createQueue(queueName, type, onload, importDefId, matchSet) {
     var name = (type=='bib') ? 'bib' : 'authority';
     var method = 'open-ils.vandelay.'+ name +'_queue.create'
     fieldmapper.standardRequest(
         ['open-ils.vandelay', method],
         {   async: true,
-            params: [authtoken, queueName, null, name, importDefId],
+            params: [authtoken, queueName, null, name, matchSet, importDefId],
             oncomplete : function(r) {
                 var queue = r.recv().content();
                 if(e = openils.Event.parse(queue)) 
@@ -1111,7 +1129,10 @@ function batchUpload() {
         currentQueueId = vlUploadQueueSelector.getValue();
         uploadMARC(handleUploadMARC);
     } else {
-        createQueue(queueName, currentType, handleCreateQueue, vlUploadQueueHoldingsImportProfile.attr('value'));
+        createQueue(queueName, currentType, handleCreateQueue, 
+            vlUploadQueueHoldingsImportProfile.attr('value'),
+            vlUploadQueueMatchSet.attr('value')
+        );
     }
 }
 
@@ -1134,9 +1155,13 @@ function vlFleshQueueSelect(selector, type) {
         if(val) {
             vlUploadQueueHoldingsImportProfile.attr('value', queue.item_attr_def() || '');
             vlUploadQueueHoldingsImportProfile.attr('disabled', true);
+            vlUploadQueueMatchSet.attr('value', queue.match_set() || '');
+            vlUploadQueueMatchSet.attr('disabled', true);
         } else {
             vlUploadQueueHoldingsImportProfile.attr('value', '');
             vlUploadQueueHoldingsImportProfile.attr('disabled', false);
+            vlUploadQueueMatchSet.attr('value', '');
+            vlUploadQueueMatchSet.attr('disabled', false);
         }
         dojo.disconnect(qInput._onchange);
         qInput.attr('value', '');
@@ -1148,6 +1173,8 @@ function vlFleshQueueSelect(selector, type) {
         // user entered a new queue name. clear the selector 
         vlUploadQueueHoldingsImportProfile.attr('value', '');
         vlUploadQueueHoldingsImportProfile.attr('disabled', false);
+        vlUploadQueueMatchSet.attr('value', '');
+        vlUploadQueueMatchSet.attr('disabled', false);
         dojo.disconnect(selector._onchange);
         selector.attr('value', '');
         selector._onchange = dojo.connect(selector, 'onChange', selChange);
@@ -1157,6 +1184,12 @@ function vlFleshQueueSelect(selector, type) {
     qInput._onchange = dojo.connect(qInput, 'onChange', inputChange);
 }
 
+function vlUpdateMatchSetSelector(type) {
+    type = (type.match(/bib/)) ? 'biblio' : 'authority';
+    vlUploadQueueMatchSet.store = 
+        new dojo.data.ItemFileReadStore({data:vms.toStoreData(matchSets[type])});
+}
+
 function vlShowUploadForm() {
     displayGlobalDiv('vl-marc-upload-div');
     vlFleshQueueSelect(vlUploadQueueSelector, vlUploadRecordType.getValue());
@@ -1165,6 +1198,7 @@ function vlShowUploadForm() {
     vlUploadSourceSelector.setValue(vlBibSources[0].id());
     vlUploadQueueHoldingsImportProfile.store = 
         new dojo.data.ItemFileReadStore({data:viiad.toStoreData(importItemDefs)});
+    vlUpdateMatchSetSelector(vlUploadRecordType.getValue());
 }
 
 function vlShowQueueSelect() {
diff --git a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2 b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
index 8356075..909670e 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
@@ -24,6 +24,13 @@
             </td>
         </tr>
         <tr>
+            <td>Record Match Set</td>
+            <td>
+                <input jsId='vlUploadQueueMatchSet'
+                    dojoType='dijit.form.FilteringSelect' labelAttr='name' searchAttr='name'/>
+            </td>
+        </tr>
+        <tr>
             <td>Holdings Import Profile</td>
             <td>
                 <input jsId='vlUploadQueueHoldingsImportProfile'

commit f1170755a54cfeb7f55d394989ebbb5c14d9cc85
Author: berick <berick at esilibrary.com>
Date:   Tue May 3 14:42:09 2011 -0400

    delete match_set_point's starting with leaf nodes to avoid foreign key constraints on nodes w/ children

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 1d69eb4..0820cb0 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1420,8 +1420,15 @@ sub match_set_update_tree {
         {"order_by" => {"vmsp" => "id DESC"}}
     ]) or return $e->die_event;
 
-    foreach (@$existing) {
-        $e->delete_vandelay_match_set_point($_) or return $e->die_event;
+    # delete points, working up from leaf points to the root
+    while(@$existing) {
+        for my $point (shift @$existing) {
+            if( grep {$_->parent eq $point->id} @$existing) {
+                push(@$existing, $point);
+            } else {
+                $e->delete_vandelay_match_set_point($point) or return $e->die_event;
+            }
+        }
     }
 
     _walk_new_vmsp($e, $match_set_id, $tree);

commit ce07a23e829a7271fc4b11af72117b8c14837ad3
Author: berick <berick at esilibrary.com>
Date:   Tue May 3 13:55:22 2011 -0400

    create an embedded view of the vandelay match-set admin ui inside of vandelay (as one of the tabs)

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 26d73c7..6ce390f 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -258,6 +258,10 @@ function displayGlobalDiv(id) {
     openils.Util.removeCSSClass(dojo.byId('vl-menu-queue-select'), 'toolbar_selected');
     openils.Util.removeCSSClass(dojo.byId('vl-menu-attr-editor'), 'toolbar_selected');
     openils.Util.removeCSSClass(dojo.byId('vl-menu-profile-editor'), 'toolbar_selected');
+    openils.Util.removeCSSClass(dojo.byId('vl-menu-match-set-editor'), 'toolbar_selected');
+
+    if(dojo.byId('vl-match-set-iframe'))
+        dojo.byId('vl-match-set-editor-div').removeChild(dojo.byId('vl-match-set-iframe'));
 
     switch(id) {
         case 'vl-marc-export-div':
@@ -278,6 +282,9 @@ function displayGlobalDiv(id) {
         case 'vl-item-attr-editor-div':
             openils.Util.addCSSClass(dojo.byId('vl-menu-import-item-attr-editor'), 'toolbar_selected');
             break;
+        case 'vl-match-set-editor-div':
+            openils.Util.addCSSClass(dojo.byId('vl-menu-match-set-editor'), 'toolbar_selected');
+            break;
     }
 }
 
@@ -1165,6 +1172,17 @@ function vlShowQueueSelect() {
     vlFleshQueueSelect(vlQueueSelectQueueList, vlQueueSelectType.getValue());
 }
 
+function vlShowMatchSetEditor() {
+    displayGlobalDiv('vl-match-set-editor-div');
+    dojo.byId('vl-match-set-editor-div').appendChild(
+        dojo.create('iframe', {
+            id : 'vl-match-set-iframe',
+            src : oilsBasePath + '/eg/conify/global/vandelay/match_set',
+            style : 'width:100%; height:500px; border:none; margin:0px;'
+        })
+    );
+}
+
 function vlFetchQueueFromForm() {
     currentType = vlQueueSelectType.getValue();
     currentQueueId = vlQueueSelectQueueList.getValue();
@@ -1321,7 +1339,6 @@ function onAttrEditorClick() {
     var parsed_xpath = xpathParser.parse(this.store.getValue(row, 'xpath'));
     dijit.byId('attr-editor-tags').attr('value', parsed_xpath.tags);
     dijit.byId('attr-editor-subfields').attr('value', parsed_xpath.subfields);
-    dijit.byId('attr-editor-identifier').attr('value', this.store.getValue(row, 'ident'));
     dijit.byId('attr-editor-xpath').attr('value', this.store.getValue(row, 'xpath'));
     dijit.byId('attr-editor-remove').attr('value', this.store.getValue(row, 'remove'));
 
diff --git a/Open-ILS/web/opac/locale/en-US/vandelay.dtd b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
index e94f8c7..f8fa799 100644
--- a/Open-ILS/web/opac/locale/en-US/vandelay.dtd
+++ b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
@@ -24,10 +24,11 @@
 <!ENTITY vandelay.dest.match.point "Destination Match Point">
 <!ENTITY vandelay.display "Display">
 <!ENTITY vandelay.done "Done">
-<!ENTITY vandelay.edit.attributes "Edit Attributes">
-<!ENTITY vandelay.edit.attrs "Edit Attributes">
+<!ENTITY vandelay.edit.attributes "Record Display Attributes">
+<!ENTITY vandelay.edit.attrs "Record Display Attributes">
 <!ENTITY vandelay.edit.profiles "Edit Merge / Overlay Profiles">
-<!ENTITY vandelay.edit.import_item_attrs "Edit Import Item Attributes">
+<!ENTITY vandelay.edit.match_set "Record Match Sets">
+<!ENTITY vandelay.edit.import_item_attrs "Import Item Attributes">
 <!ENTITY vandelay.false "False">
 <!ENTITY vandelay.file.to.upload "File to Upload:">
 <!ENTITY vandelay.for.example "Example">
diff --git a/Open-ILS/web/templates/default/vandelay/inc/attrs.tt2 b/Open-ILS/web/templates/default/vandelay/inc/attrs.tt2
index d06b176..1a09b0b 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/attrs.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/attrs.tt2
@@ -38,15 +38,6 @@
                 </td>
             </tr>
             <tr>
-                <td><label for="ident">&vandelay.id.field;: </label></td>
-                <td>
-                    <select dojoType="dijit.form.FilteringSelect" name="ident" id="attr-editor-identifier">
-                        <option value='f' selected='selected'>&vandelay.false;</option>
-                        <option value='t'>&vandelay.true;</option>
-                    </select>
-                </td>
-            </tr>
-            <tr>
                 <td><label for="attr-editor-xpath">&vandelay.xpath.advanced;: </label></td>
 
                 <td><input dojoType="dijit.form.TextBox" id="attr-editor-xpath" name="xpath"></input></td>
@@ -90,7 +81,6 @@
                 <th field='description' width='auto'>&vandelay.descrip;</th>
                 <th field='tag' get='attrGridGetTag'>&vandelay.tag;</th>
                 <th field='subfield' get='attrGridGetSubfield'>&vandelay.subfield;</th>
-                <th field='ident'>&vandelay.identifier;</th>
                 <th field='xpath' width='auto'>&vandelay.xpath;</th>
                 <th field='remove' width='auto'>&vandelay.remove;</th>
             </tr>
diff --git a/Open-ILS/web/templates/default/vandelay/inc/toolbar.tt2 b/Open-ILS/web/templates/default/vandelay/inc/toolbar.tt2
index 69c2190..144cd45 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/toolbar.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/toolbar.tt2
@@ -9,6 +9,8 @@
         onclick="vlShowAttrEditor();" showLabel="true">&vandelay.edit.attributes;</div>
     <div dojoType="dijit.form.Button" iconClass="dijitEditorIcon dijitEditorIconCopy"  id='vl-menu-profile-editor'
         onclick="vlShowProfileEditor();" showLabel="true">&vandelay.edit.profiles;</div>
+    <div dojoType="dijit.form.Button" iconClass="dijitEditorIcon dijitEditorIconCopy"  id='vl-menu-match-set-editor'
+        onclick="vlShowMatchSetEditor();" showLabel="true">&vandelay.edit.match_set;</div>
     <div dojoType="dijit.form.Button" iconClass="dijitEditorIcon dijitEditorIconCopy"  id='vl-menu-import-item-attr-editor'
         onclick="vlShowImportItemAttrEditor();" showLabel="true">&vandelay.edit.import_item_attrs;</div>
 </div>
diff --git a/Open-ILS/web/templates/default/vandelay/vandelay.tt2 b/Open-ILS/web/templates/default/vandelay/vandelay.tt2
index 4ca2f37..78f23a8 100644
--- a/Open-ILS/web/templates/default/vandelay/vandelay.tt2
+++ b/Open-ILS/web/templates/default/vandelay/vandelay.tt2
@@ -37,6 +37,8 @@
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client' id='vl-profile-editor-div' class='hidden content'>
     [% INCLUDE 'default/vandelay/inc/profiles.tt2' %]
 </div>
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client' id='vl-match-set-editor-div' class='hidden content'>
+</div>
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client' id='vl-item-attr-editor-div' class='hidden content'>
     [% INCLUDE 'default/vandelay/inc/item_attrs.tt2' %]
 </div>

commit d2127cf97568f02c8b7cf40e8d981b677455f619
Author: berick <berick at esilibrary.com>
Date:   Tue May 3 11:11:11 2011 -0400

    added support for viewing all import-items related to a queue, with a filter to limit to those that failed import

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 182cf5c..26d73c7 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -648,9 +648,12 @@ function vlLoadErrorUIAll() {
 
     displayGlobalDiv('vl-import-error-div');
     openils.Util.hide('vl-import-error-grid-some');
+    openils.Util.hide('vl-import-error-record');
     openils.Util.show('vl-import-error-grid-all');
     vlAllImportErrorGrid.resetStore();
 
+    vlImportErrorGrid.displayOffset = 0;
+
     vlAllImportErrorGrid.dataLoader = function() {
 
         vlAllImportErrorGrid.showLoadProgressIndicator();
@@ -661,7 +664,7 @@ function vlLoadErrorUIAll() {
                 async : true,
                 params : [
                     authtoken, currentQueueId, {   
-                        with_import_error:1, 
+                        with_import_error: (vlImportItemsShowErrors.checked) ? 1 : null,
                         offset : vlAllImportErrorGrid.displayOffset,
                         limit : vlAllImportErrorGrid.displayLimit
                     }
diff --git a/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2 b/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
index 6b08db4..f72630b 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
@@ -1,4 +1,3 @@
-<h1>Import Errors</h1><br/>
 
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
     <button dojoType='dijit.form.Button' 
@@ -6,23 +5,25 @@
 </div>
 
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
-    <table id='vl-import-error-record' class='hidden'>
-        <tbody>
-            <tr><td>ID</td><td id='vl-error-id'/></tr>
-            <tr><td>Import Error</td><td id='vl-error-import-error'/></tr>
-            <tr><td>Error Detail</td><td id='vl-error-error-detail'/></tr>
-            <tr><td>Title</td><td id='vl-error-title'/></tr>
-            <tr><td>Author</td><td id='vl-error-author'/></tr>
-            <tr><td>ISBN</td><td id='vl-error-isbn'/></tr>
-            <tr><td>ISSN</td><td id='vl-error-issn'/></tr>
-            <tr><td>UPC</td><td id='vl-error-upc'/></tr>
-        </tbody>
-    </table>
+    <div id='vl-import-error-record' class='hidden'>
+        <h1>Import Errors</h1><br/>
+        <table>
+            <tbody>
+                <tr><td>ID</td><td id='vl-error-id'/></tr>
+                <tr><td>Import Error</td><td id='vl-error-import-error'/></tr>
+                <tr><td>Error Detail</td><td id='vl-error-error-detail'/></tr>
+                <tr><td>Title</td><td id='vl-error-title'/></tr>
+                <tr><td>Author</td><td id='vl-error-author'/></tr>
+                <tr><td>ISBN</td><td id='vl-error-isbn'/></tr>
+                <tr><td>ISSN</td><td id='vl-error-issn'/></tr>
+                <tr><td>UPC</td><td id='vl-error-upc'/></tr>
+            </tbody>
+        </table>
+    </div>
 </div>
 
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
     <div class='hidden' id='vl-import-error-grid-some'>
-        <h3>Item Import Errors</h3>
         <table  jsId="vlImportErrorGrid"
                 dojoType="openils.widget.AutoGrid"
                 autoHeight='true'
@@ -33,15 +34,19 @@
                 columnPickerPrefix='"vandelay.item.import_error"'
                 fmClass='vii'>
                 <thead>
-                    <th field='owning_lib' get='vlGetOrg'/>
-                    <th field='circ_lib' get='vlGetOrg'/>
+                    <tr>
+                        <th field='owning_lib' get='vlGetOrg'/>
+                        <th field='circ_lib' get='vlGetOrg'/>
+                    </tr>
                 </thead>
         </table>
     </div>
 </div>
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
     <div class='hidden' id='vl-import-error-grid-all'>
-        <h3>Item Import Errors</h3>
+        <h1>Import Items</h1><br/>
+        <input dojoType='dijit.form.CheckBox' jsId='vlImportItemsShowErrors' onchange='vlLoadErrorUIAll();'/>
+        <span>Limit to Import Failures</span>
         <table  jsId="vlAllImportErrorGrid"
                 dojoType="openils.widget.AutoGrid"
                 autoHeight='true'
@@ -52,8 +57,10 @@
                 columnPickerPrefix='"vandelay.item.import_error"'
                 fmClass='vii'>
                 <thead>
-                    <th field='owning_lib' get='vlGetOrg'/>
-                    <th field='circ_lib' get='vlGetOrg'/>
+                    <tr>
+                        <th field='owning_lib' get='vlGetOrg'/>
+                        <th field='circ_lib' get='vlGetOrg'/>
+                    </tr>
                 </thead>
         </table>
     </div>
diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index 50f74b4..00c812d 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -11,7 +11,7 @@
                             <tbody>
                                 <tr><td><a href='#' onclick='vlHandleQueueItemsAction("import")'>&vandelay.import.selected;</a></td></tr>
                                 <tr><td><a href='#' onclick='vlHandleQueueItemsAction("import_all")'>&vandelay.import.all;</a></td></tr>
-                                <tr><td><a href='#' onclick='vlLoadErrorUIAll();'>View Item Import Failures</a></td></tr>
+                                <tr><td><a href='#' onclick='vlLoadErrorUIAll();'>View Import Items</a></td></tr>
                                 <tr><td><a href='#' onclick='
                                     if(confirm("&vandelay.sure.to.delete.queue;")) {
                                         vlDeleteQueue(currentType, currentQueueId, 

commit 0d784d89f00fd350181e6559a9c94bca039210c8
Author: berick <berick at esilibrary.com>
Date:   Mon May 2 17:48:29 2011 -0400

    added view for all failed item imports for a queue; next up is export options

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 4df6c1d..1d69eb4 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -518,7 +518,9 @@ sub retrieve_queue_import_items {
                 }
             }
         },
-        order_by => {'vii' => ['record','id']}
+        order_by => {'vii' => ['record','id']},
+        limit => $limit,
+        offset => $offset
     };
 
     $query->{where} = {'+vii' => {import_error => {'!=' => undef}}}
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index cd6cd82..182cf5c 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -644,6 +644,43 @@ function vlLoadErrorUI(id) {
     }
 }
 
+function vlLoadErrorUIAll() {
+
+    displayGlobalDiv('vl-import-error-div');
+    openils.Util.hide('vl-import-error-grid-some');
+    openils.Util.show('vl-import-error-grid-all');
+    vlAllImportErrorGrid.resetStore();
+
+    vlAllImportErrorGrid.dataLoader = function() {
+
+        vlAllImportErrorGrid.showLoadProgressIndicator();
+
+        fieldmapper.standardRequest(
+            ['open-ils.vandelay', 'open-ils.vandelay.import_item.queue.retrieve'],
+            {
+                async : true,
+                params : [
+                    authtoken, currentQueueId, {   
+                        with_import_error:1, 
+                        offset : vlAllImportErrorGrid.displayOffset,
+                        limit : vlAllImportErrorGrid.displayLimit
+                    }
+                ],
+                onresponse : function(r) {
+                    var item = openils.Util.readResponse(r);
+                    if(!item) return;
+                    vlAllImportErrorGrid.store.newItem(vii.toStoreItem(item));
+                },
+                oncomplete : function() {
+                    vlAllImportErrorGrid.hideLoadProgressIndicator();
+                }
+            }
+        );
+    };
+
+    vlAllImportErrorGrid.dataLoader();
+}
+
 function vlGetOrg(rowIdx, item) {
     if(!item) return '';
     var value = this.grid.store.getValue(item, this.field);
diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index 25e22a2..50f74b4 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -11,7 +11,7 @@
                             <tbody>
                                 <tr><td><a href='#' onclick='vlHandleQueueItemsAction("import")'>&vandelay.import.selected;</a></td></tr>
                                 <tr><td><a href='#' onclick='vlHandleQueueItemsAction("import_all")'>&vandelay.import.all;</a></td></tr>
-                                <tr><td><a href='#' onclick='alert("coming soon"); return;vlHandleQueueItemsAction("item_errors")'>View Item Import Failures</a></td></tr>
+                                <tr><td><a href='#' onclick='vlLoadErrorUIAll();'>View Item Import Failures</a></td></tr>
                                 <tr><td><a href='#' onclick='
                                     if(confirm("&vandelay.sure.to.delete.queue;")) {
                                         vlDeleteQueue(currentType, currentQueueId, 
@@ -99,62 +99,6 @@
     </fieldset>
 </div>
 
-<!-- queue grid navigation row -->
-<div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
-    <table width='100%' style='margin-bottom:0px;'>
-        <tr>
-        <!--
-            <td align='left' valign='bottom'>
-                <select id='vl-queue-actions-selector'>
-                    <option selected='selected' disabled='disabled' value='select-actions'>&vandelay.select_actions;</option>
-                    <option value='import'>&vandelay.import.selected;</option>
-                    <option value='import_all'>&vandelay.import.all;</option>
-                    <option value='delete_queue'>&vandelay.delete.queue;</option>
-                </select>
-                <script type="text/javascript">
-                    var sel = dojo.byId('vl-queue-actions-selector');
-                    sel.onchange = function(evt) {
-                        switch(openils.Util.selectorValue(evt.target)) {
-                            case 'import': vlHandleQueueItemsAction('import'); break;;
-                            case 'import_all': vlHandleQueueItemsAction('import_all'); break;;
-                            case 'delete_queue': 
-                                if(confirm('&vandelay.sure.to.delete.queue;')) {
-                                    vlDeleteQueue(currentType, currentQueueId, 
-                                        function() { displayGlobalDiv('vl-marc-upload-div'); });
-                                }
-                        }
-                        evt.target.selectedIndex = 0;
-                    }
-                </script>
-            </td>
-            <td align='right' valign='bottom'>
-                <style type="text/css">.filter_span { padding-right: 5px; border-right: 2px solid #e8e1cf; } </style>
-                <span>&vandelay.results.per.page;</span>
-                <span class='filter_span'>
-                    <select jsId='vlQueueDisplayLimit' id='vl-queue-display-limit-selector'
-                        value='10' onchange='retrieveQueuedRecords();'>
-                        <option value='10'>10</option>
-                        <option value='20'>20</option>
-                        <option value='50'>50</option>
-                        <option value='100'>100</option>
-                    </select>
-                </span>
-                <span style='padding-left:5px;'>&vandelay.page;</span>
-                <input style='width:36px;' dojoType='dijit.form.TextBox' jsId='vlQueueDisplayPage' value='1'/>
-
-                <span style='padding-right:4px;'>
-                    <a href='javascript:void(0);' onclick='vlQueueGridPrevPage();'>&vandelay.prev.page;</a>
-                </span>
-                <span style='padding-right:10px;'>
-                    <a href='javascript:void(0);' onclick='vlQueueGridNextPage();'>&vandelay.next.page;</a>
-                </span>
-            </td>
-            -->
-        </tr>
-    </table>
-</div>
-
-
 <!-- Bib Queue Grid -->
 <div class='' id='vl-bib-queue-grid-wrapper' dojoType='dijit.layout.ContentPane'>
     <table dojoType='dojox.grid.DataGrid' jsId='vlBibQueueGrid' query="{id:'*'}" autoHeight='true'>

commit a978e74d8303bf39bdcd081c2f77bb5289d1159d
Author: berick <berick at esilibrary.com>
Date:   Mon May 2 15:51:15 2011 -0400

    added support for 'limit records to those with any import errors' in vandelay queue display

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index fa97b8c..cd6cd82 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -374,6 +374,9 @@ function retrieveQueuedRecords(type, queueId, onload) {
     if(vlQueueGridShowNonImport.checked)
         params[2].non_imported = 1;
 
+    if(vlQueueGridShowImportErrors.checked)
+        params[2].with_import_error = 1;
+
     fieldmapper.standardRequest(
         ['open-ils.vandelay', method],
         {   async: true,

commit 0aec475a117564063a98a4e0219b35946eb393db
Author: berick <berick at esilibrary.com>
Date:   Mon May 2 15:49:33 2011 -0400

    added 'with_import_error' option to queued record retrieval;  returns recs with either a record or item import failure (or both)

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 7f34422..4df6c1d 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -430,19 +430,25 @@ sub retrieve_queued_records {
     };
 
     $query->{where}->{import_time} = undef if $$options{non_imported};
-    $query->{where}->{import_error} = {'!=' => undef} if $$options{with_rec_import_error};
-
-    if($$options{with_item_import_error} and $type eq 'bib') {
-        # limit to recs that have at least 1 item import error
-        $query->{from} = {
-            $class => {
-                vii => {
-                    field => 'record',
-                    fkey => 'id',
-                    filter => {import_error => {'!=' => undef}}
-                }
-            }
-        };
+
+    if($$options{with_import_error}) {
+
+        $query->{from} = {$class => {vii => {type => 'right'}}};
+        $query->{where}->{'-or'} = [
+            {'+vqbr' => {import_error => {'!=' => undef}}},
+            {'+vii' => {import_error => {'!=' => undef}}}
+        ];
+
+    } else {
+        
+        if($$options{with_rec_import_error}) {
+            $query->{where}->{import_error} = {'!=' => undef};
+
+        } elsif( $$options{with_item_import_error} and $type eq 'bib') {
+
+            $query->{from} = {$class => 'vii'};
+            $query->{where}->{'+vii'} = {import_error => {'!=' => undef}};
+        }
     }
 
     if($self->api_name =~ /matches/) {

commit 90047c17588fa07bb6cabca1ec14f290198d8883
Author: berick <berick at esilibrary.com>
Date:   Mon May 2 12:52:30 2011 -0400

    Vandelay file upload page cleanup
    
     * Separate data configuration parameters from import actions
     * Since import item profile is linked to queue, show the profile for
        the selected queue and dissallow changing the profile for existing
        queues in the UI (which has no effect)

diff --git a/Open-ILS/web/css/skin/default/vandelay.css b/Open-ILS/web/css/skin/default/vandelay.css
index 1f0303a..b1987f8 100644
--- a/Open-ILS/web/css/skin/default/vandelay.css
+++ b/Open-ILS/web/css/skin/default/vandelay.css
@@ -47,3 +47,4 @@ table.dijitTooltipTable { border-collapse: separate; }
 .queue-nav-table-label { font-weight: bold; text-decoration:underline;}
 .queue-pager-span { padding-right: 5px; margin-right: 5px; border-right: 2px solid #e8e1cf; }
 #vl-queue-paging-table td { padding-bottom: 0px; }
+#vl-file-label { margin-right: 10px; }
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index b6022c2..fa97b8c 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -1073,6 +1073,38 @@ function vlFleshQueueSelect(selector, type) {
     selector.setDisplayedValue('');
     if(data[0])
         selector.setValue(data[0].id());
+
+    var qInput = dijit.byId('vl-queue-name');
+
+    var selChange = function(val) {
+        console.log('selector onchange');
+        // user selected a queue from the selector;  clear the input and 
+        // set the item import profile already defined for the queue
+        var queue = allUserBibQueues.filter(function(q) { return (q.id() == val) })[0];
+        if(val) {
+            vlUploadQueueHoldingsImportProfile.attr('value', queue.item_attr_def() || '');
+            vlUploadQueueHoldingsImportProfile.attr('disabled', true);
+        } else {
+            vlUploadQueueHoldingsImportProfile.attr('value', '');
+            vlUploadQueueHoldingsImportProfile.attr('disabled', false);
+        }
+        dojo.disconnect(qInput._onchange);
+        qInput.attr('value', '');
+        qInput._onchange = dojo.connect(qInput, 'onChange', inputChange);
+    }
+    
+    var inputChange = function(val) {
+        console.log('qinput onchange');
+        // user entered a new queue name. clear the selector 
+        vlUploadQueueHoldingsImportProfile.attr('value', '');
+        vlUploadQueueHoldingsImportProfile.attr('disabled', false);
+        dojo.disconnect(selector._onchange);
+        selector.attr('value', '');
+        selector._onchange = dojo.connect(selector, 'onChange', selChange);
+    }
+
+    selector._onchange = dojo.connect(selector, 'onChange', selChange);
+    qInput._onchange = dojo.connect(qInput, 'onChange', inputChange);
 }
 
 function vlShowUploadForm() {
diff --git a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2 b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
index aca6320..8356075 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/upload.tt2
@@ -24,54 +24,50 @@
             </td>
         </tr>
         <tr>
-            <td>&vandelay.auto.import.noncolliding;</td>
-            <td colspan='4'>
-                <input jsId='vlUploadQueueAutoImport' dojoType='dijit.form.CheckBox'/>
+            <td>Holdings Import Profile</td>
+            <td>
+                <input jsId='vlUploadQueueHoldingsImportProfile'
+                    dojoType='dijit.form.FilteringSelect' labelAttr='name' searchAttr='name'/>
             </td>
         </tr>
         <tr>
-            <td>&vandelay.auto.import.auto_overlay_exact;</td>
-            <td colspan='4'>
-                <input jsId='vlUploadQueueAutoOverlayExact' dojoType='dijit.form.CheckBox'/>
+            <td>&vandelay.import.bib_sources;</td>
+            <td>
+                <select name='bib_source' jsId='vlUploadSourceSelector' 
+                    dojoType='dijit.form.FilteringSelect' labelAttr='source' searchAttr='source'/>
             </td>
         </tr>
+        <tr><td colspan='2' style='margin-top:10px;border-bottom:1px solid #888;border-top:1px solid #888'><b>Import Actions</b></td></tr>
         <tr>
-            <td>&vandelay.auto.import.auto_overlay_1match;</td>
+            <td>&vandelay.auto.import.merge_profile;</td>
             <td colspan='4'>
-                <input jsId='vlUploadQueueAutoOverlay1Match' dojoType='dijit.form.CheckBox'/>
+                <div jsId='vlUploadMergeProfile' dojoType='dijit.form.FilteringSelect' required='false' labelAttr='name' searchAttr='name'/>
             </td>
         </tr>
         <tr>
-            <td>&vandelay.auto.import.merge_profile;</td>
+            <td>&vandelay.auto.import.noncolliding;</td>
             <td colspan='4'>
-                <div jsId='vlUploadMergeProfile' dojoType='dijit.form.FilteringSelect' required='false' labelAttr='name' searchAttr='name'/>
+                <input jsId='vlUploadQueueAutoImport' dojoType='dijit.form.CheckBox'/>
             </td>
         </tr>
         <tr>
-            <td>Holdings Import Profile</td>
-            <td>
-                <input jsId='vlUploadQueueHoldingsImportProfile'
-                    dojoType='dijit.form.FilteringSelect' labelAttr='name' searchAttr='name'/>
+            <td>&vandelay.auto.import.auto_overlay_exact;</td>
+            <td colspan='4'>
+                <input jsId='vlUploadQueueAutoOverlayExact' dojoType='dijit.form.CheckBox'/>
             </td>
         </tr>
         <tr>
-            <td>&vandelay.import.bib_sources;</td>
-            <td>
-                <select name='bib_source' jsId='vlUploadSourceSelector' 
-                    dojoType='dijit.form.FilteringSelect' labelAttr='source' searchAttr='source'/>
+            <td>&vandelay.auto.import.auto_overlay_1match;</td>
+            <td colspan='4'>
+                <input jsId='vlUploadQueueAutoOverlay1Match' dojoType='dijit.form.CheckBox'/>
             </td>
         </tr>
+        <tr><td colspan='2' style='border-bottom:1px solid #888'></td></tr>
         <tr>
-            <td>
+            <td colspan='5'>
                 <span id="vl-file-label">&vandelay.file.to.upload;</span>
-            </td>
-            <td id='vl-input-td' colspan='4'>
                 <input size='48' type="file" name="marc_upload"/>
-            </td>
-        </tr>
-        <tr>
-            <td align='center' colspan='4'>
-                <button dojoType="dijit.form.Button" onclick="batchUpload()">&vandelay.upload;</button>
+                <span style='margin-left:10px;'><button dojoType="dijit.form.Button" onclick="batchUpload()">&vandelay.upload;</button></span>
             </td>
         </tr>
     </table>

commit a8f7f42453896dc97af0d51729a1a04ea7b3e900
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 16:34:54 2011 -0400

    added new stub actions for limit-to-import-errors and show all item import errors; displaying new summary info; much rearrangement to bettet fit the UI pieces

diff --git a/Open-ILS/web/css/skin/default/vandelay.css b/Open-ILS/web/css/skin/default/vandelay.css
index 7476377..1f0303a 100644
--- a/Open-ILS/web/css/skin/default/vandelay.css
+++ b/Open-ILS/web/css/skin/default/vandelay.css
@@ -34,3 +34,16 @@ table.dijitTooltipTable { border-collapse: separate; }
 #vl-import-error-record td:first-child { text-decoration: underline; }
 #vl-import-error-record td { padding: 5px; }
 #vl-error-import-error { font-weight: bold; }
+
+#vl-queue-filter-fieldset { padding-top: 3px; border:2px dashed #d9e8f9; }
+#vl-queue-filter-fieldset { margin: 5px; }
+#vl-queue-filter-fieldset td { padding: 5px; }
+#vl-queue-filter-fieldset th { padding: 5px; }
+#vl-queue-filter-fieldset legend { font-weight: bold; font-size: 1.3em }
+
+#vl-queue-filter-table { margin-left: 15px; padding-left: 15px; border-left: 2px dashed #d9e8f9;}
+#vl-queue-summary-table { margin-left: 15px; padding-left: 15px; border-left: 2px dashed #d9e8f9;}
+.queue-nav-table tr:nth-child(even) {background: #EEE}
+.queue-nav-table-label { font-weight: bold; text-decoration:underline;}
+.queue-pager-span { padding-right: 5px; margin-right: 5px; border-right: 2px solid #e8e1cf; }
+#vl-queue-paging-table td { padding-bottom: 0px; }
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 490d84c..b6022c2 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -865,6 +865,9 @@ var handleRetrieveRecords = function() {
             dojo.byId('vl-queue-summary-name').innerHTML = summary.queue.name();
             dojo.byId('vl-queue-summary-total-count').innerHTML = summary.total +'';
             dojo.byId('vl-queue-summary-import-count').innerHTML = summary.imported + '';
+            dojo.byId('vl-queue-summary-import-item-count').innerHTML = summary.total_items + '';
+            dojo.byId('vl-queue-summary-rec-error-count').innerHTML = summary.rec_import_errors + '';
+            dojo.byId('vl-queue-summary-item-error-count').innerHTML = summary.item_import_errors + '';
         }
     );
 }
diff --git a/Open-ILS/web/opac/locale/en-US/vandelay.dtd b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
index 31fdc67..e94f8c7 100644
--- a/Open-ILS/web/opac/locale/en-US/vandelay.dtd
+++ b/Open-ILS/web/opac/locale/en-US/vandelay.dtd
@@ -36,22 +36,23 @@
 <!ENTITY vandelay.id "ID">
 <!ENTITY vandelay.import.matches "Import Matches">
 <!ENTITY vandelay.import.records "Import Records">
-<!ENTITY vandelay.import.selected "Import Selected">
-<!ENTITY vandelay.import.all "Import All">
+<!ENTITY vandelay.import.selected "Import Selected Records">
+<!ENTITY vandelay.import.all "Import All  Records">
 <!ENTITY vandelay.import.time "Import Time">
 <!ENTITY vandelay.inspect.queue "Inspect Queue">
 <!ENTITY vandelay.last.edit.date "Last Edit Date">
-<!ENTITY vandelay.limit.to.collision.matches "Limit to Collision Matches">
+<!ENTITY vandelay.limit.to.collision.matches "Limit to Records with Matches">
 <!ENTITY vandelay.limit.to.non.imported "Limit to Non-Imported Records">
+<!ENTITY vandelay.limit.to.import_error "Limit to Records with Import Errors">
 <!ENTITY vandelay.marc.file.upload "Evergreen MARC File Upload">
 <!ENTITY vandelay.marc.record "MARC Record">
 <!ENTITY vandelay.matches "Matches">
-<!ENTITY vandelay.next.page "Next Page &#187;">
+<!ENTITY vandelay.next.page "Next &#187;">
 <!ENTITY vandelay.overlay.selected.record "Overlay selected record with imported record">
 <!ENTITY vandelay.overlay.target "Overlay Target">
 <!ENTITY vandelay.page "Page">
 <!ENTITY vandelay.powered.by.evergreen "Powered by Evergreen!">
-<!ENTITY vandelay.prev.page "&#171; Previous Page">
+<!ENTITY vandelay.prev.page "&#171; Previous">
 <!ENTITY vandelay.processing "Processing... ">
 <!ENTITY vandelay.queue "Queue">
 <!ENTITY vandelay.queue.type "Queue Type">
@@ -60,7 +61,7 @@
 <!ENTITY vandelay.refresh "Refresh">
 <!ENTITY vandelay.remove.advanced "Remove (advanced)">
 <!ENTITY vandelay.remove "Remove">
-<!ENTITY vandelay.results.per.page "Results Per Page">
+<!ENTITY vandelay.results.per.page "Records Per Page">
 <!ENTITY vandelay.retrieve.queue "Retrieve Queue">
 <!ENTITY vandelay.return "Return">
 <!ENTITY vandelay.select.cols "Select Columns">
@@ -106,7 +107,7 @@
 <!ENTITY vandelay.export.field_no_hint "(starting from 0)">
 <!ENTITY vandelay.export.bucket "Record Bucket ID">
 <!ENTITY vandelay.select_actions "-- Actions --">
-<!ENTITY vandelay.queue.total "Total:">
-<!ENTITY vandelay.queue.imported "Imported:">
+<!ENTITY vandelay.queue.total "Records in Queue:">
+<!ENTITY vandelay.queue.imported "Records Imported:">
 <!ENTITY vandelay.queue.column_picker.title "Column Picker">
 <!ENTITY vandelay.import.bib_sources "Select a Record Source">
diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index 4308898..25e22a2 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -1,66 +1,109 @@
-<div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
-    <table class='wide'>
-        <tr>
-            <td align='left'>
-                <h1>&vandelay.record.queue; <span style='font-style:italic;' id='vl-queue-summary-name'/></h1><br/>
-            </td>
-            <td align='right'>
-                &vandelay.queue.total; <span style='font-weight:bold;' id='vl-queue-summary-total-count'/>
-                &vandelay.queue.imported; <span style='font-weight:bold;' id='vl-queue-summary-import-count'/>
-            </td>
-        </tr>
-    </table>
-</div>
-
-<br/>
-
-<div jsId='queueItemsImportDialog' dojoType="dijit.Dialog" title="Import Items">
-    <div dojoType="dijit.layout.ContentPane">
-        <table class='form_table'>
-            <tbody>
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client' style='margin-top:10px;'>
+    <fieldset id='vl-queue-filter-fieldset'>
+        <legend>Queue <span style='font-style:italic;' id='vl-queue-summary-name'/></legend>
+        <table width='100%'><tr>
+            <td> <!-- big left td -->
+            <table>
                 <tr>
-                    <td>&vandelay.auto.import.noncolliding;</td>
-                    <td colspan='4'>
-                        <input jsId='vlUploadQueueAutoImport2' dojoType='dijit.form.CheckBox'/>
+                    <td valign='top'>
+                        <table class='queue-nav-table'>
+                            <thead><tr><th colspan='2' class='queue-nav-table-label'>Queue Actions</th></tr></thead>
+                            <tbody>
+                                <tr><td><a href='#' onclick='vlHandleQueueItemsAction("import")'>&vandelay.import.selected;</a></td></tr>
+                                <tr><td><a href='#' onclick='vlHandleQueueItemsAction("import_all")'>&vandelay.import.all;</a></td></tr>
+                                <tr><td><a href='#' onclick='alert("coming soon"); return;vlHandleQueueItemsAction("item_errors")'>View Item Import Failures</a></td></tr>
+                                <tr><td><a href='#' onclick='
+                                    if(confirm("&vandelay.sure.to.delete.queue;")) {
+                                        vlDeleteQueue(currentType, currentQueueId, 
+                                            function() { displayGlobalDiv("vl-marc-upload-div"); });}'>&vandelay.delete.queue;</a></td></tr>
+                            </tbody>
+                        </table>
                     </td>
-                </tr>
-                <tr>
-                    <td>&vandelay.auto.import.auto_overlay_exact;</td>
-                    <td colspan='4'>
-                        <input jsId='vlUploadQueueAutoOverlayExact2' dojoType='dijit.form.CheckBox'/>
-                    </td>
-                </tr>
-                <tr>
-                    <td>&vandelay.auto.import.auto_overlay_1match;</td>
-                    <td colspan='4'>
-                        <input jsId='vlUploadQueueAutoOverlay1Match2' dojoType='dijit.form.CheckBox'/>
-                    </td>
-                </tr>
-                <tr>
-                    <td>&vandelay.auto.import.merge_profile;</td>
-                    <td colspan='4'>
-                        <div jsId='vlUploadMergeProfile2' 
-                            dojoType='dijit.form.FilteringSelect' required='false' labelAttr='name' searchAttr='name'/>
-                    </td>
-                </tr>
-                <tr>
-                    <td>
-                        <button dojoType='dijit.form.Button' jsId='queueItemsImportCancelButton'>Cancel</button>
+
+                    <td valign='top'>
+                        <table  id='vl-queue-summary-table' class='queue-nav-table'>
+                            <thead><tr><th colspan='2' class='queue-nav-table-label'>Queue Summary</th></tr></thead>
+                            <tbody>
+                                <tr><td>&vandelay.queue.total;</td><td> <span style='font-weight:bold;' id='vl-queue-summary-total-count'/></td></tr>
+                                <tr><td>&vandelay.queue.imported;</td><td> <span style='font-weight:bold;' id='vl-queue-summary-import-count'/></td></tr>
+                                <tr><td>Record Import Failures</td><td> <span style='font-weight:bold;' id='vl-queue-summary-rec-error-count'/></td></tr>
+                                <tr><td>Items in Queue</td><td> <span style='font-weight:bold;' id='vl-queue-summary-import-item-count'/></td></tr>
+                                <tr><td>Item Import Failures</td><td> <span style='font-weight:bold;' id='vl-queue-summary-item-error-count'/></td></tr>
+                            </tbody>
+                        </table>
                     </td>
-                    <td>
-                        <button dojoType='dijit.form.Button' jsId='queueItemsImportGoButton'>Import</button>
+
+                    <td valign='top'> <!-- filters -->
+                        <table id='vl-queue-filter-table' class='queue-nav-table'>
+                            <thead><tr><th colspan='2' class='queue-nav-table-label'>Queue Filters</th></tr></thead>
+                            <tbody>
+                                <tr>
+                                    <td>&vandelay.limit.to.collision.matches;</td>
+                                    <td>
+                                        <input dojoType='dijit.form.CheckBox' 
+                                            jsId='vlQueueGridShowMatches' onchange='retrieveQueuedRecords();'/>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td>&vandelay.limit.to.non.imported;</td>
+                                    <td>
+                                        <input dojoType='dijit.form.CheckBox' 
+                                            jsId='vlQueueGridShowNonImport' onchange='retrieveQueuedRecords();'/>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td>&vandelay.limit.to.import_error;</td>
+                                    <td>
+                                        <input dojoType='dijit.form.CheckBox' 
+                                            jsId='vlQueueGridShowImportErrors' onchange='retrieveQueuedRecords();'/>
+                                    </td>
+                                </tr>
+                            </tbody>
+                        </table>
                     </td>
                 </tr>
-            </tbody>
-        </table>
-    </div>
+            </table>
+        </td>
+
+        <td align='right' valign='bottom'> <!-- big right td -->
+            <table id='vl-queue-paging-table' class='queue-nav-table'>
+                <tbody>
+                    <tr><td valign='bottom' align='right'>
+                        <span style='padding-right:5px;'>&vandelay.results.per.page;</span>
+                        <span class='queue-pager-span'>
+                            <select jsId='vlQueueDisplayLimit' id='vl-queue-display-limit-selector'
+                                value='10' onchange='retrieveQueuedRecords();'>
+                                <option value='10'>10</option>
+                                <option value='20'>20</option>
+                                <option value='50'>50</option>
+                                <option value='100'>100</option>
+                            </select>
+                        </span>
+
+                        <span class='queue-pager-span'>
+                            <span style='padding-left:5px;'>&vandelay.page;</span>
+                            <input style='width:36px;' dojoType='dijit.form.TextBox' jsId='vlQueueDisplayPage' value='1'/>
+                        </span>
+
+                        <span style='padding-right:8px;'>
+                            <a href='javascript:void(0);' onclick='vlQueueGridPrevPage();'>&vandelay.prev.page;</a>
+                        </span>
+                        <span style='padding-right:10px;'>
+                            <a href='javascript:void(0);' onclick='vlQueueGridNextPage();'>&vandelay.next.page;</a>
+                        </span>
+                    </td></tr>
+                </tbody>
+            </table>
+        </td>
+    </tr></table>
+    </fieldset>
 </div>
 
 <!-- queue grid navigation row -->
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
-
     <table width='100%' style='margin-bottom:0px;'>
         <tr>
+        <!--
             <td align='left' valign='bottom'>
                 <select id='vl-queue-actions-selector'>
                     <option selected='selected' disabled='disabled' value='select-actions'>&vandelay.select_actions;</option>
@@ -84,38 +127,21 @@
                     }
                 </script>
             </td>
-            <td align='middle' valign='bottom'>
+            <td align='right' valign='bottom'>
                 <style type="text/css">.filter_span { padding-right: 5px; border-right: 2px solid #e8e1cf; } </style>
-                <table><tr>
-                    <td>
-                        <span>&vandelay.limit.to.collision.matches;</span>
-                        <span class='filter_span'>
-                            <input dojoType='dijit.form.CheckBox' jsId='vlQueueGridShowMatches' onchange='retrieveQueuedRecords();'/>
-                        </span>
-
-                        <span>&vandelay.limit.to.non.imported;</span>
-                        <span class='filter_span'>
-                            <input dojoType='dijit.form.CheckBox' 
-                                jsId='vlQueueGridShowNonImport' onchange='retrieveQueuedRecords();'/>
-                        </span>
-
-                        <span>&vandelay.results.per.page;</span>
-                        <span class='filter_span'>
-                            <select jsId='vlQueueDisplayLimit' id='vl-queue-display-limit-selector'
-                                value='10' onchange='retrieveQueuedRecords();'>
-                                <option value='10'>10</option>
-                                <option value='20'>20</option>
-                                <option value='50'>50</option>
-                                <option value='100'>100</option>
-                            </select>
-                        </span>
+                <span>&vandelay.results.per.page;</span>
+                <span class='filter_span'>
+                    <select jsId='vlQueueDisplayLimit' id='vl-queue-display-limit-selector'
+                        value='10' onchange='retrieveQueuedRecords();'>
+                        <option value='10'>10</option>
+                        <option value='20'>20</option>
+                        <option value='50'>50</option>
+                        <option value='100'>100</option>
+                    </select>
+                </span>
+                <span style='padding-left:5px;'>&vandelay.page;</span>
+                <input style='width:36px;' dojoType='dijit.form.TextBox' jsId='vlQueueDisplayPage' value='1'/>
 
-                        <span style='padding-left:5px;'>&vandelay.page;</span>
-                        <input style='width:36px;' dojoType='dijit.form.TextBox' jsId='vlQueueDisplayPage' value='1'/>
-                    </td>
-                </tr></table>
-            </td>
-            <td align='right' valign='bottom'>
                 <span style='padding-right:4px;'>
                     <a href='javascript:void(0);' onclick='vlQueueGridPrevPage();'>&vandelay.prev.page;</a>
                 </span>
@@ -123,6 +149,7 @@
                     <a href='javascript:void(0);' onclick='vlQueueGridNextPage();'>&vandelay.next.page;</a>
                 </span>
             </td>
+            -->
         </tr>
     </table>
 </div>
@@ -158,7 +185,7 @@
                     get='vlGetViewErrors'
                     formatter='vlFormatViewErrors'
                     styles='text-align: center;'
-                    nonSelectable='true'>Import Errors</th>
+                    nonSelectable='true'>Import Failures</th>
                 <th
                     field='import_time'
                     styles='text-align: center;'
@@ -199,7 +226,7 @@
                     get='vlGetViewErrors'
                     formatter='vlFormatViewErrors'
                     styles='text-align: center;'
-                    nonSelectable='true'>Import Errors</th>
+                    nonSelectable='true'>Import Failures</th>
                 <th
                     field='import_time'
                     styles='text-align: center;'
@@ -210,4 +237,47 @@
     <div/>
 </div>
 
+<div jsId='queueItemsImportDialog' dojoType="dijit.Dialog" title="Import Items">
+    <div dojoType="dijit.layout.ContentPane">
+        <table class='form_table'>
+            <tbody>
+                <tr>
+                    <td>&vandelay.auto.import.noncolliding;</td>
+                    <td colspan='4'>
+                        <input jsId='vlUploadQueueAutoImport2' dojoType='dijit.form.CheckBox'/>
+                    </td>
+                </tr>
+                <tr>
+                    <td>&vandelay.auto.import.auto_overlay_exact;</td>
+                    <td colspan='4'>
+                        <input jsId='vlUploadQueueAutoOverlayExact2' dojoType='dijit.form.CheckBox'/>
+                    </td>
+                </tr>
+                <tr>
+                    <td>&vandelay.auto.import.auto_overlay_1match;</td>
+                    <td colspan='4'>
+                        <input jsId='vlUploadQueueAutoOverlay1Match2' dojoType='dijit.form.CheckBox'/>
+                    </td>
+                </tr>
+                <tr>
+                    <td>&vandelay.auto.import.merge_profile;</td>
+                    <td colspan='4'>
+                        <div jsId='vlUploadMergeProfile2' 
+                            dojoType='dijit.form.FilteringSelect' required='false' labelAttr='name' searchAttr='name'/>
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <button dojoType='dijit.form.Button' jsId='queueItemsImportCancelButton'>Cancel</button>
+                    </td>
+                    <td>
+                        <button dojoType='dijit.form.Button' jsId='queueItemsImportGoButton'>Import</button>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
+
+
 

commit 7e245cb05b5978b19c7f7f5236cff93906e11718
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 16:33:48 2011 -0400

    add error and item import/error summary info to queue summary api call

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index b431422..7f34422 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1128,11 +1128,41 @@ sub retrieve_queue_summary {
     my $search = 'search_vandelay_queued_bib_record';
     $search =~ s/bib/authority/ if $type ne 'bib';
 
-    return {
+    my $summary = {
         queue => $queue,
         total => scalar(@{$e->$search({queue => $queue_id}, {idlist=>1})}),
         imported => scalar(@{$e->$search({queue => $queue_id, import_time => {'!=' => undef}}, {idlist=>1})}),
     };
+
+    my $class = ($type eq 'bib') ? 'vqbr' : 'vqar';
+    $summary->{rec_import_errors} = $e->json_query({
+        select => {$class => [{alias => 'count', column => 'id', transform => 'count', aggregate => 1}]},
+        from => $class,
+        where => {queue => $queue_id, import_error => {'!=' => undef}}
+    })->[0]->{count};
+
+    if($type eq 'bib') {
+        
+        my $query = {
+            select => {vii => [{alias => 'count', column => 'id', transform => 'count', aggregate => 1}]},
+            from => 'vii',
+            where => {
+                record => {
+                    in => {
+                        select => {vqbr => ['id']},
+                        from => 'vqbr',
+                        where => {queue => $queue_id}
+                    }
+                }
+            }
+        };
+
+        $summary->{total_items} = $e->json_query($query)->[0]->{count};
+        $query->{where}->{import_error} = {'!=' => undef};
+        $summary->{item_import_errors} = $e->json_query($query)->[0]->{count};
+    }
+
+    return $summary;
 }
 
 # --------------------------------------------------------------------------------

commit 41432f29df97e0ca44a7ac4dc7163f8ce7900803
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 14:16:35 2011 -0400

    No longer limit to non-imported items by default in queue display

diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index 5c87243..4308898 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -95,8 +95,8 @@
 
                         <span>&vandelay.limit.to.non.imported;</span>
                         <span class='filter_span'>
-                            <input dojoType='dijit.form.CheckBox' jsId='vlQueueGridShowNonImport' 
-                                checked='checked' onchange='retrieveQueuedRecords();'/>
+                            <input dojoType='dijit.form.CheckBox' 
+                                jsId='vlQueueGridShowNonImport' onchange='retrieveQueuedRecords();'/>
                         </span>
 
                         <span>&vandelay.results.per.page;</span>

commit ff2ab6bfd10050306dbbb65e1134703433b6b46a
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 13:45:21 2011 -0400

    hide item import error grid when not in use
    
    misc. bug fixes

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 59dc39b..b431422 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1161,6 +1161,7 @@ sub import_record_asset_list_impl {
             my $item = $e->retrieve_vandelay_import_item($item_id);
             $report_args{progress}++;
             $report_args{import_item} = $item;
+            $report_args{e} = $e;
 
             # --------------------------------------------------------------------------------
             # Find or create the volume
@@ -1199,23 +1200,30 @@ sub import_record_asset_list_impl {
             # see if a valid circ_modifier was provided
             # --------------------------------------------------------------------------------
             if($copy->circ_modifier and not $e->search_config_circ_modifier({code=>$item->circ_modifier})->[0]) {
-                $report_args{import_error} = 'import.item.invalid.circ_modifier';
-                respond_with_status(%report_args, evt => $e->die_event);
+                respond_with_status(
+                    %report_args, 
+                    evt => $e->die_event,
+                    import_error => 'import.item.invalid.circ_modifier'
+                );
                 next;
             }
 
             if($copy->location and not $e->retrieve_asset_copy_location($copy->location)) {
-                $report_args{import_error} = 'import.item.invalid.location';
-                respond_with_status(%report_args, evt => $e->die_event);
+                respond_with_status(
+                    %report_args, 
+                    evt => $e->die_event,
+                    import_error => 'import.item.invalid.location'
+                );
                 next;
             }
 
             if($evt = OpenILS::Application::Cat::AssetCommon->create_copy($e, $vol, $copy)) {
-                try { $e->rollback } otherwise {}; # sometimes calls die_event, sometimes not
-                $report_args{evt} = $evt;
-                $report_args{import_error} = 'import.item.duplicate.barcode' 
-                    if $evt->{textcode} eq 'ITEM_BARCODE_EXISTS';
-                respond_with_status(%report_args);
+                respond_with_status(
+                    %report_args,
+                    evt => $evt,
+                    import_error => ($evt->{textcode} eq 'ITEM_BARCODE_EXISTS') ? 
+                        'import.item.duplicate.barcode' : undef
+                );
                 next;
             }
 
@@ -1243,7 +1251,8 @@ sub import_record_asset_list_impl {
             # --------------------------------------------------------------------------------
             $e->commit;
             $report_args{in_count}++;
-            respond_with_status(%report_args, imported_as => $copy->id)
+            respond_with_status(%report_args, imported_as => $copy->id);
+            $logger->info("vl: successfully imported item " . $item->barcode);
         }
     }
     $roe->rollback;
@@ -1253,6 +1262,7 @@ sub import_record_asset_list_impl {
 
 sub respond_with_status {
     my %args = @_;
+    my $e = $args{e};
 
     #  If the import failed, track the failure reason
 
@@ -1262,8 +1272,9 @@ sub respond_with_status {
     if($error or $evt) {
 
         my $item = $args{import_item};
+        $logger->info("vl: unable to import item " . $item->barcode);
 
-        my $error ||= 'general.unknown';
+        $error ||= 'general.unknown';
         $item->import_error($error);
 
         if($evt) {
@@ -1271,7 +1282,9 @@ sub respond_with_status {
             $item->error_detail($detail);
         }
 
-        my $e = new_editor(xact => 1);
+        # state of the editor is unknown at this point.  Force a rollback and start over.
+        $e->rollback;
+        $e = new_editor(xact => 1);
         $e->update_vandelay_import_item($item);
         $e->commit;
     }
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 344f2ec..490d84c 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -616,9 +616,18 @@ function vlLoadErrorUI(id) {
                 openils.Util.hide(dojo.byId(eid).parentNode);
             }
         }
-    )
-    dojo.byId('vl-error-import-error').innerHTML = rec.import_error();
-    dojo.byId('vl-error-error-detail').innerHTML = rec.error_detail();
+    );
+    var iediv = dojo.byId('vl-error-import-error');
+    var eddiv = dojo.byId('vl-error-error-detail');
+    if(rec.import_error()) {
+        openils.Util.show(iediv.parentNode, 'table-row');
+        openils.Util.show(eddiv.parentNode, 'table-row');
+        iediv.innerHTML = rec.import_error();
+        eddiv.innerHTML = rec.error_detail();
+    } else {
+        openils.Util.hide(iediv.parentNode);
+        openils.Util.hide(eddiv.parentNode);
+    }
 
     var errorItems = rec.import_items().filter(function(i) {return i.import_error()});
     if(errorItems.length) {
@@ -627,6 +636,8 @@ function vlLoadErrorUI(id) {
         var store = new dojo.data.ItemFileReadStore({data:storeData});
         vlImportErrorGrid.setStore(store);
         vlImportErrorGrid.update();
+    } else {
+        openils.Util.hide('vl-import-error-grid-some');
     }
 }
 

commit 19bb47e04e1de3764fc1e3b93be7589bae0d0e14
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 11:38:52 2011 -0400

    show rec summary and import failure reason when appropriate

diff --git a/Open-ILS/web/css/skin/default/vandelay.css b/Open-ILS/web/css/skin/default/vandelay.css
index d60ba06..7476377 100644
--- a/Open-ILS/web/css/skin/default/vandelay.css
+++ b/Open-ILS/web/css/skin/default/vandelay.css
@@ -28,3 +28,9 @@ table.dijitTooltipTable { border-collapse: separate; }
 .export_tr_border td { border-top: 1px solid #808080; }
 .nav_row_div {padding:1px; text-align:center; }
 .toolbar_selected { border: 2px dashed #808080; text-decoration:underline; font-weight:bold;}
+
+#vl-import-error-record { margin: 10px; }
+#vl-import-error-record tr:nth-child(even) {background: #EEE}
+#vl-import-error-record td:first-child { text-decoration: underline; }
+#vl-import-error-record td { padding: 5px; }
+#vl-error-import-error { font-weight: bold; }
diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 68eeb0e..344f2ec 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -588,7 +588,7 @@ function vlFormatViewErrors(chunk) {
     var count = chunk.split(':')[2];
     var links = '';
     if(rec) 
-        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');"><b>Record</b></a><br/>'; // TODO I18N
+        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');">Record</a><br/>'; // TODO I18N
     if(Number(count))
         links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');">Items ('+count+')</a>'; // TODO I18N
     return links;
@@ -598,15 +598,31 @@ function vlFormatViewErrors(chunk) {
 function vlLoadErrorUI(id) {
 
     displayGlobalDiv('vl-import-error-div');
-    openils.Util.show('vl-import-error-grid-some');
     openils.Util.hide('vl-import-error-grid-all');
+    openils.Util.show('vl-import-error-record');
 
     var rec = queuedRecordsMap[id];
 
-    /* TODO: show record attrs and whether it failed import */
+    dojo.byId('vl-error-id').innerHTML = rec.id();
+    dojo.forEach( // TODO sane authority rec. fields
+        ['title', 'author', 'isbn', 'issn', 'upc'],
+        function(field) {
+            var attr =  getRecAttrFromCode(rec, field);
+            var eid = 'vl-error-' + field;
+            if(attr) {
+                openils.Util.show(dojo.byId(eid).parentNode, 'table-row');
+                dojo.byId(eid).innerHTML = attr.attr_value();
+            } else {
+                openils.Util.hide(dojo.byId(eid).parentNode);
+            }
+        }
+    )
+    dojo.byId('vl-error-import-error').innerHTML = rec.import_error();
+    dojo.byId('vl-error-error-detail').innerHTML = rec.error_detail();
 
     var errorItems = rec.import_items().filter(function(i) {return i.import_error()});
-    if(errorItems) {
+    if(errorItems.length) {
+        openils.Util.show('vl-import-error-grid-some');
         storeData = vqbr.toStoreData(errorItems);
         var store = new dojo.data.ItemFileReadStore({data:storeData});
         vlImportErrorGrid.setStore(store);
diff --git a/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2 b/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
index 5490133..6b08db4 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
@@ -6,40 +6,56 @@
 </div>
 
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
-    <table id='import-error-table'>
+    <table id='vl-import-error-record' class='hidden'>
+        <tbody>
+            <tr><td>ID</td><td id='vl-error-id'/></tr>
+            <tr><td>Import Error</td><td id='vl-error-import-error'/></tr>
+            <tr><td>Error Detail</td><td id='vl-error-error-detail'/></tr>
+            <tr><td>Title</td><td id='vl-error-title'/></tr>
+            <tr><td>Author</td><td id='vl-error-author'/></tr>
+            <tr><td>ISBN</td><td id='vl-error-isbn'/></tr>
+            <tr><td>ISSN</td><td id='vl-error-issn'/></tr>
+            <tr><td>UPC</td><td id='vl-error-upc'/></tr>
+        </tbody>
     </table>
 </div>
 
-<div dojoType="dijit.layout.ContentPane" layoutAlign='client' class='hidden' id='vl-import-error-grid-some'>
-    <table  jsId="vlImportErrorGrid"
-            dojoType="openils.widget.AutoGrid"
-            autoHeight='true'
-            fieldOrder="['barcode', 'call_number', 'owning_lib', 'import_error', 'error_detail']"
-            query="{id: '*'}"
-            hidePaginator='true'
-            showColumnPicker='true'
-            columnPickerPrefix='"vandelay.item.import_error"'
-            fmClass='vii'>
-            <thead>
-                <th field='owning_lib' get='vlGetOrg'/>
-                <th field='circ_lib' get='vlGetOrg'/>
-            </thead>
-    </table>
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
+    <div class='hidden' id='vl-import-error-grid-some'>
+        <h3>Item Import Errors</h3>
+        <table  jsId="vlImportErrorGrid"
+                dojoType="openils.widget.AutoGrid"
+                autoHeight='true'
+                fieldOrder="['barcode', 'call_number', 'owning_lib', 'import_error', 'error_detail']"
+                query="{id: '*'}"
+                hidePaginator='true'
+                showColumnPicker='true'
+                columnPickerPrefix='"vandelay.item.import_error"'
+                fmClass='vii'>
+                <thead>
+                    <th field='owning_lib' get='vlGetOrg'/>
+                    <th field='circ_lib' get='vlGetOrg'/>
+                </thead>
+        </table>
+    </div>
 </div>
-<div dojoType="dijit.layout.ContentPane" layoutAlign='client' class='hidden' id='vl-import-error-grid-all'>
-    <table  jsId="vlAllImportErrorGrid"
-            dojoType="openils.widget.AutoGrid"
-            autoHeight='true'
-            fieldOrder="['barcode', 'call_number', 'owning_lib', 'import_error', 'error_detail']"
-            query="{id: '*'}"
-            showPaginator='true'
-            showColumnPicker='true'
-            columnPickerPrefix='"vandelay.item.import_error"'
-            fmClass='vii'>
-            <thead>
-                <th field='owning_lib' get='vlGetOrg'/>
-                <th field='circ_lib' get='vlGetOrg'/>
-            </thead>
-    </table>
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
+    <div class='hidden' id='vl-import-error-grid-all'>
+        <h3>Item Import Errors</h3>
+        <table  jsId="vlAllImportErrorGrid"
+                dojoType="openils.widget.AutoGrid"
+                autoHeight='true'
+                fieldOrder="['barcode', 'call_number', 'owning_lib', 'import_error', 'error_detail']"
+                query="{id: '*'}"
+                showPaginator='true'
+                showColumnPicker='true'
+                columnPickerPrefix='"vandelay.item.import_error"'
+                fmClass='vii'>
+                <thead>
+                    <th field='owning_lib' get='vlGetOrg'/>
+                    <th field='circ_lib' get='vlGetOrg'/>
+                </thead>
+        </table>
+    </div>
 </div>
 

commit f44258e7f54013e6c64f1aeeb617f4b7f016a696
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 11:02:55 2011 -0400

    show item import error counts and rec import error as link to error page;  initial error page

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index 7eece79..68eeb0e 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -57,7 +57,8 @@ var globalDivs = [
     'vl-attr-editor-div',
     'vl-marc-export-div',
     'vl-profile-editor-div',
-    'vl-item-attr-editor-div'
+    'vl-item-attr-editor-div',
+    'vl-import-error-div'
 ];
 
 var authtoken;
@@ -587,12 +588,39 @@ function vlFormatViewErrors(chunk) {
     var count = chunk.split(':')[2];
     var links = '';
     if(rec) 
-        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');"><b>Record</b></a><br/>'; // XXX I18N
+        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');"><b>Record</b></a><br/>'; // TODO I18N
     if(Number(count))
-        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');"><b>Items ('+count+')</b></a>'; // XXX I18N
+        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');">Items ('+count+')</a>'; // TODO I18N
     return links;
 }
 
+//var vlItemErrorColumnPicker;
+function vlLoadErrorUI(id) {
+
+    displayGlobalDiv('vl-import-error-div');
+    openils.Util.show('vl-import-error-grid-some');
+    openils.Util.hide('vl-import-error-grid-all');
+
+    var rec = queuedRecordsMap[id];
+
+    /* TODO: show record attrs and whether it failed import */
+
+    var errorItems = rec.import_items().filter(function(i) {return i.import_error()});
+    if(errorItems) {
+        storeData = vqbr.toStoreData(errorItems);
+        var store = new dojo.data.ItemFileReadStore({data:storeData});
+        vlImportErrorGrid.setStore(store);
+        vlImportErrorGrid.update();
+    }
+}
+
+function vlGetOrg(rowIdx, item) {
+    if(!item) return '';
+    var value = this.grid.store.getValue(item, this.field);
+    if(value) return fieldmapper.aou.findOrgUnit(value).shortname();
+    return '';
+}
+
 function vlFormatViewMatchMARC(id) {
     return '<a href="javascript:void(0);" onclick="vlLoadMARCHtml(' + id + ', true, '+
         'function(){displayGlobalDiv(\'vl-match-div\');});">' + this.name + '</a>';
diff --git a/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2 b/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
new file mode 100644
index 0000000..5490133
--- /dev/null
+++ b/Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2
@@ -0,0 +1,45 @@
+<h1>Import Errors</h1><br/>
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
+    <button dojoType='dijit.form.Button' 
+        onclick="displayGlobalDiv('vl-queue-div');">&#x2196; &vandelay.back.to.import.queue;</button>
+</div>
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
+    <table id='import-error-table'>
+    </table>
+</div>
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client' class='hidden' id='vl-import-error-grid-some'>
+    <table  jsId="vlImportErrorGrid"
+            dojoType="openils.widget.AutoGrid"
+            autoHeight='true'
+            fieldOrder="['barcode', 'call_number', 'owning_lib', 'import_error', 'error_detail']"
+            query="{id: '*'}"
+            hidePaginator='true'
+            showColumnPicker='true'
+            columnPickerPrefix='"vandelay.item.import_error"'
+            fmClass='vii'>
+            <thead>
+                <th field='owning_lib' get='vlGetOrg'/>
+                <th field='circ_lib' get='vlGetOrg'/>
+            </thead>
+    </table>
+</div>
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client' class='hidden' id='vl-import-error-grid-all'>
+    <table  jsId="vlAllImportErrorGrid"
+            dojoType="openils.widget.AutoGrid"
+            autoHeight='true'
+            fieldOrder="['barcode', 'call_number', 'owning_lib', 'import_error', 'error_detail']"
+            query="{id: '*'}"
+            showPaginator='true'
+            showColumnPicker='true'
+            columnPickerPrefix='"vandelay.item.import_error"'
+            fmClass='vii'>
+            <thead>
+                <th field='owning_lib' get='vlGetOrg'/>
+                <th field='circ_lib' get='vlGetOrg'/>
+            </thead>
+    </table>
+</div>
+
diff --git a/Open-ILS/web/templates/default/vandelay/vandelay.tt2 b/Open-ILS/web/templates/default/vandelay/vandelay.tt2
index 2e2a2e0..4ca2f37 100644
--- a/Open-ILS/web/templates/default/vandelay/vandelay.tt2
+++ b/Open-ILS/web/templates/default/vandelay/vandelay.tt2
@@ -40,6 +40,9 @@
 <div dojoType="dijit.layout.ContentPane" layoutAlign='client' id='vl-item-attr-editor-div' class='hidden content'>
     [% INCLUDE 'default/vandelay/inc/item_attrs.tt2' %]
 </div>
+<div dojoType="dijit.layout.ContentPane" layoutAlign='client' id='vl-import-error-div' class='hidden content'>
+    [% INCLUDE 'default/vandelay/inc/import_errors.tt2' %]
+</div>
 
 
 [% END %]

commit d50e03f75c2fa5635e715dcaa8f057f577a508f1
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 09:58:19 2011 -0400

    record / item import failures summar links

diff --git a/Open-ILS/web/js/ui/default/vandelay/vandelay.js b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
index a54005f..7eece79 100644
--- a/Open-ILS/web/js/ui/default/vandelay/vandelay.js
+++ b/Open-ILS/web/js/ui/default/vandelay/vandelay.js
@@ -369,7 +369,7 @@ function retrieveQueuedRecords(type, queueId, onload) {
     var limit = parseInt(sel.options[sel.selectedIndex].value);
     var offset = limit * parseInt(vlQueueDisplayPage.attr('value')-1);
 
-    var params =  [authtoken, queueId, {clear_marc: 1, offset: offset, limit: limit}];
+    var params =  [authtoken, queueId, {clear_marc: 1, offset: offset, limit: limit, flesh_import_items:1}];
     if(vlQueueGridShowNonImport.checked)
         params[2].non_imported = 1;
 
@@ -568,6 +568,31 @@ function vlFormatViewMatches(id) {
     return '<a href="javascript:void(0);" onclick="vlLoadMatchUI(' + id + ');">' + this.name + '</a>';
 }
 
+function vlGetViewErrors(rowIdx, item) {
+    if(item) {
+        var id = this.grid.store.getValue(item, 'id');
+        var rec = queuedRecordsMap[id];
+        // id:rec_error:item_import_error_count
+        return id + ':' + 
+            (rec.import_error() ? 1 : '') + ':' + 
+            rec.import_items().filter(function(i) {return i.import_error()}).length;
+    }
+    return -1
+}
+
+function vlFormatViewErrors(chunk) {
+    if(chunk == -1) return '';
+    var id = chunk.split(':')[0];
+    var rec = chunk.split(':')[1];
+    var count = chunk.split(':')[2];
+    var links = '';
+    if(rec) 
+        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');"><b>Record</b></a><br/>'; // XXX I18N
+    if(Number(count))
+        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');"><b>Items ('+count+')</b></a>'; // XXX I18N
+    return links;
+}
+
 function vlFormatViewMatchMARC(id) {
     return '<a href="javascript:void(0);" onclick="vlLoadMARCHtml(' + id + ', true, '+
         'function(){displayGlobalDiv(\'vl-match-div\');});">' + this.name + '</a>';
diff --git a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2 b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
index e7d6eaf..5c87243 100644
--- a/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
+++ b/Open-ILS/web/templates/default/vandelay/inc/queue.tt2
@@ -137,7 +137,7 @@
                     field='+row_selector'
                     get='vlQueueGridDrawSelectBox'
                     formatter='vlQueueGridFormatSelectBox'
-                    styles='text-align: center;'
+                    styles='text-align: center;width:30px;'
                     nonSelectable='true'>
                         <input id="vl-queue-grid-row-selector" type="checkbox" onclick="vlToggleQueueGridSelect();"></input>
                 </th>
@@ -154,6 +154,12 @@
                     styles='text-align: center;'
                     nonSelectable='true'>&vandelay.matches;</th>
                 <th
+                    field='+get_errors'
+                    get='vlGetViewErrors'
+                    formatter='vlFormatViewErrors'
+                    styles='text-align: center;'
+                    nonSelectable='true'>Import Errors</th>
+                <th
                     field='import_time'
                     styles='text-align: center;'
                     get='vlGetDateTimeField'>&vandelay.import.time;</th>
@@ -189,6 +195,12 @@
                     styles='text-align: center;'
                     nonSelectable='true'>&vandelay.matches;</th>
                 <th
+                    field='+get_errors'
+                    get='vlGetViewErrors'
+                    formatter='vlFormatViewErrors'
+                    styles='text-align: center;'
+                    nonSelectable='true'>Import Errors</th>
+                <th
                     field='import_time'
                     styles='text-align: center;'
                     get='vlGetDateTimeField'>&vandelay.import.time;</th>

commit e14a3f4e11bc91d6cc0100209be4e2147f559301
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 09:57:13 2011 -0400

    added virtual field for import_items to queued record

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 0c59bd4..dd522fa 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -327,6 +327,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Purpose" name="purpose" reporter:datatype="text"/>
 			<field reporter:label="Attributes" name="attributes" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Matches" name="matches" oils_persist:virtual="true" reporter:datatype="link"/>
+			<field reporter:label="Import Items" name="import_items" oils_persist:virtual="true" reporter:datatype="link"/>
 		</fields>
 		<links>
 			<link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
@@ -335,6 +336,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<link field="imported_as" reltype="has_a" key="id" map="" class="bre"/>
             <link field="attributes" reltype="has_many" key="record" map="" class="vqbra"/>
             <link field="matches" reltype="has_many" key="queued_record" map="" class="vbm"/>
+            <link field="import_items" reltype="has_many" key="record" map="" class="vii"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>

commit 7cca21c8c126e98a64ec8677d2981d4a472ca26a
Author: berick <berick at esilibrary.com>
Date:   Mon Apr 25 09:56:58 2011 -0400

    added option to flesh import items on queued record retrieval

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 3516fd3..59dc39b 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -457,10 +457,9 @@ sub retrieve_queued_records {
         'retrieve_vandelay_queued_bib_record' : 'retrieve_vandelay_queued_authority_record';
 
     for my $rec_id (@$record_ids) {
-        my $params = {   
-            flesh => 1,
-            flesh_fields => {$class => ['attributes', 'matches']},
-        };
+        my $flesh = ['attributes', 'matches'];
+        push(@$flesh, 'import_items') if $$options{flesh_import_items};
+        my $params = {flesh => 1, flesh_fields => {$class => $flesh}};
         my $rec = $e->$retrieve([$rec_id->{id}, $params]);
         $rec->clear_marc if $$options{clear_marc};
         $conn->respond($rec);

commit 3585f727e20b8d986e1983213fb6b6677ae169ab
Author: Mike Rylander <mrylander at gmail.com>
Date:   Sat Apr 23 11:47:27 2011 -0400

    Make sort order of imported item retrieval stable

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 9b44160..3516fd3 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -513,7 +513,7 @@ sub retrieve_queue_import_items {
                 }
             }
         },
-        order_by => {'vii' => ['record']}
+        order_by => {'vii' => ['record','id']}
     };
 
     $query->{where} = {'+vii' => {import_error => {'!=' => undef}}}

commit e1a271d2525911e5c1cdcbb4bb495b6295acb5df
Author: berick <berick at esilibrary.com>
Date:   Fri Apr 22 16:58:15 2011 -0400

    added api call open-ils.vandelay.import_item.queue.retrieve for fetching import-items by queue, w/ optional has-import-failure filter

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 0180832..9b44160 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -432,7 +432,7 @@ sub retrieve_queued_records {
     $query->{where}->{import_time} = undef if $$options{non_imported};
     $query->{where}->{import_error} = {'!=' => undef} if $$options{with_rec_import_error};
 
-    if($$options{with_item_import_error}) {
+    if($$options{with_item_import_error} and $type eq 'bib') {
         # limit to recs that have at least 1 item import error
         $query->{from} = {
             $class => {
@@ -470,6 +470,63 @@ sub retrieve_queued_records {
     return undef;
 }
 
+__PACKAGE__->register_method(  
+    api_name    => 'open-ils.vandelay.import_item.queue.retrieve',
+    method      => 'retrieve_queue_import_items',
+    api_level   => 1,
+    argc        => 2,
+    stream      => 1,
+    authoritative => 1,
+    signature => q/
+        Returns Import Item (vii) objects for the selected queue.
+        Filter options:
+            with_import_error : only return items that failed to import
+    /
+);
+
+sub retrieve_queue_import_items {
+    my($self, $conn, $auth, $q_id, $options) = @_;
+
+    $options ||= {};
+    my $limit = $$options{limit} || 20;
+    my $offset = $$options{offset} || 0;
+
+    my $e = new_editor(authtoken => $auth);
+    return $e->event unless $e->checkauth;
+
+    my $queue = $e->retrieve_vandelay_bib_queue($q_id) or return $e->event;
+    my $evt = check_queue_perms($e, 'bib', $queue);
+    return $evt if $evt;
+
+    my $query = {
+        select => {vii => ['id']},
+        from => {
+            vii => {
+                vqbr => {
+                    join => {
+                        'vbq' => {
+                            field => 'id',
+                            fkey => 'queue',
+                            filter => {id => $q_id}
+                        }
+                    }
+                }
+            }
+        },
+        order_by => {'vii' => ['record']}
+    };
+
+    $query->{where} = {'+vii' => {import_error => {'!=' => undef}}}
+        if $$options{with_import_error};
+
+    my $items = $e->json_query($query);
+    for my $item (@$items) {
+        $conn->respond($e->retrieve_vandelay_import_item($item->{id}));
+    }
+
+    return undef;
+}
+
 sub check_queue_perms {
     my($e, $type, $queue) = @_;
     if ($type eq 'bib') {

commit 06e1fa2563792748dcebdabbd0a32efa11d559b4
Author: berick <berick at esilibrary.com>
Date:   Fri Apr 22 16:14:47 2011 -0400

    added with_rec_import_error and with_item_import_error filters to records-by-queue retrieval

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 1b48e9a..0180832 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -396,18 +396,19 @@ __PACKAGE__->register_method(
     signature   => {
         desc => q/Only retrieve queued authority records that have matches against existing records/
     }
-
 );
 
 sub retrieve_queued_records {
     my($self, $conn, $auth, $queue_id, $options) = @_;
-    my $e = new_editor(authtoken => $auth, xact => 1);
-    return $e->die_event unless $e->checkauth;
+
     $options ||= {};
     my $limit = $$options{limit} || 20;
     my $offset = $$options{offset} || 0;
-
     my $type = $self->{record_type};
+
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
+
     my $queue;
     if($type eq 'bib') {
         $queue = $e->retrieve_vandelay_bib_queue($queue_id) or return $e->die_event;
@@ -418,37 +419,53 @@ sub retrieve_queued_records {
     return $evt if ($evt);
 
     my $class = ($type eq 'bib') ? 'vqbr' : 'vqar';
-    my $search = ($type eq 'bib') ? 
-        'search_vandelay_queued_bib_record' : 'search_vandelay_queued_authority_record';
-    my $retrieve = ($type eq 'bib') ? 
-        'retrieve_vandelay_queued_bib_record' : 'retrieve_vandelay_queued_authority_record';
+    my $query = {
+        select => {$class => ['id']},
+        from => $class,
+        where => {queue => $queue_id},
+        distinct => 1,
+        order_by => {$class => ['id']}, 
+        limit => $limit,
+        offset => $offset,
+    };
 
-    my $filter = ($$options{non_imported}) ? {import_time => undef} : {};
+    $query->{where}->{import_time} = undef if $$options{non_imported};
+    $query->{where}->{import_error} = {'!=' => undef} if $$options{with_rec_import_error};
+
+    if($$options{with_item_import_error}) {
+        # limit to recs that have at least 1 item import error
+        $query->{from} = {
+            $class => {
+                vii => {
+                    field => 'record',
+                    fkey => 'id',
+                    filter => {import_error => {'!=' => undef}}
+                }
+            }
+        };
+    }
 
-    my $record_ids;
     if($self->api_name =~ /matches/) {
-        # fetch only matched records
-        $record_ids = queued_records_with_matches($e, $type, $queue_id, $limit, $offset, $filter);
-    } else {
-        # fetch all queue records
-        $record_ids = $e->$search([
-                {queue => $queue_id, %$filter}, 
-                {order_by => {$class => 'id'}, limit => $limit, offset => $offset}
-            ],
-            {idlist => 1}
-        );
-    }
+        # find only records that have matches
+        my $mclass = $type eq 'bib' ? 'vbm' : 'vam';
+        $query->{from} = {$class => {$mclass => {type => 'right'}}};
+    } 
+
+    my $record_ids = $e->json_query($query);
 
+    my $retrieve = ($type eq 'bib') ? 
+        'retrieve_vandelay_queued_bib_record' : 'retrieve_vandelay_queued_authority_record';
 
     for my $rec_id (@$record_ids) {
         my $params = {   
             flesh => 1,
             flesh_fields => {$class => ['attributes', 'matches']},
         };
-        my $rec = $e->$retrieve([$rec_id, $params]);
+        my $rec = $e->$retrieve([$rec_id->{id}, $params]);
         $rec->clear_marc if $$options{clear_marc};
         $conn->respond($rec);
     }
+
     $e->rollback;
     return undef;
 }

commit b61162301e6c81b2be3b0def8438535d2fb4c28b
Author: berick <berick at esilibrary.com>
Date:   Fri Apr 22 13:42:08 2011 -0400

    rec import bug error capturing bug fixes

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 0ea5798..1b48e9a 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -606,59 +606,6 @@ sub queued_records_with_matches {
     return [ map {$_->{queued_record}} @$data ];
 }
 
-# tracks any import errors, commits the current xact, responds to the client
-sub finish_rec_import_attempt {
-    my %args = @_;
-    my $error = $args{import_error} || 'general.unknown';
-    my $evt = $args{evt};
-    my $rec = $args{rec};
-    my $e = $args{e};
-
-    # error tracking
-    if($rec) {
-
-        if($error or $evt) {
-            # since an error occurred, there's no guarantee the transaction wasn't 
-            # rolled back.  force a rollback and create a new editor.
-            $e->rollback;
-            $e = new_editor(xact => 1);
-            $rec->import_error($error);
-
-            if($evt) {
-                my $detail = sprintf("%s : %s", $evt->{textcode}, substr($evt->{desc}, 0, 140));
-                $rec->error_detail($detail);
-            }
-
-            my $method = 'update_vandelay_queued_bib_record';
-            $method =~ s/bib/authority/ if $args{type} eq 'auth';
-            $e->$method($rec) and $e->commit or $e->rollback;
-
-        } else {
-            # successful import
-            $e->commit;
-        }
-
-    } else {
-        # requested queued record was not found
-        $e->rollback;
-    }
-        
-    # respond to client
-    if($args{report_all} or ($args{progress} % $args{step}) == 0) {
-
-        $args{conn}->respond({
-            total => $args{total}, 
-            progress => $args{progress}, 
-            imported => ($rec) ? $rec->id : undef,
-            err_event => $evt
-        });
-
-        # report often at first, climb quickly, then hold steady
-        $args{step} *= 2 unless $args{step} == 256;
-    }
-}
-
-
 
 sub import_record_list_impl {
     my($self, $conn, $rec_ids, $requestor, $args) = @_;
@@ -822,6 +769,8 @@ sub import_record_list_impl {
                     if($res->{$auto_overlay_func} eq 't') {
                         $logger->info("vl: $type auto-overlay succeeded for queued rec " . $rec->id);
                         $imported = 1;
+                    } else {
+                        $logger->info("vl: $type auto-overlay failed for queued rec " . $rec->id);
                     }
 
                 } else {
@@ -894,6 +843,66 @@ sub import_record_list_impl {
     return undef;
 }
 
+# tracks any import errors, commits the current xact, responds to the client
+sub finish_rec_import_attempt {
+    my %args = @_;
+    my $evt = $args{evt};
+    my $rec = $args{rec};
+    my $e = $args{e};
+
+    my $error = $args{import_error};
+    $error = 'general.unknown' if $evt and not $error;
+
+    # error tracking
+    if($rec) {
+
+        if($error or $evt) {
+            # failed import
+            # since an error occurred, there's no guarantee the transaction wasn't 
+            # rolled back.  force a rollback and create a new editor.
+            $e->rollback;
+            $e = new_editor(xact => 1);
+            $rec->import_error($error);
+
+            if($evt) {
+                my $detail = sprintf("%s : %s", $evt->{textcode}, substr($evt->{desc}, 0, 140));
+                $rec->error_detail($detail);
+            }
+
+            my $method = 'update_vandelay_queued_bib_record';
+            $method =~ s/bib/authority/ if $args{type} eq 'auth';
+            $e->$method($rec) and $e->commit or $e->rollback;
+
+        } else {
+            # successful import
+            $rec->clear_import_error;
+            $rec->clear_error_detail;
+            $e->commit;
+        }
+
+    } else {
+        # requested queued record was not found
+        $e->rollback;
+    }
+        
+    # respond to client
+    if($args{report_all} or ($args{progress} % $args{step}) == 0) {
+
+        $args{conn}->respond({
+            total => $args{total}, 
+            progress => $args{progress}, 
+            imported => ($rec) ? $rec->id : undef,
+            err_event => $evt
+        });
+
+        # report often at first, climb quickly, then hold steady
+        $args{step} *= 2 unless $args{step} == 256;
+    }
+}
+
+
+
+
 
 __PACKAGE__->register_method(  
     api_name    => "open-ils.vandelay.bib_queue.owner.retrieve",

commit 2620862d7ebafed7a86c26c682a01dcc2d11f705
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Apr 22 13:19:22 2011 -0400

    remove unused match_attr and actually capture eg_record into eg_id

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 61b49c2..c5740d7 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -1234,7 +1234,6 @@ DECLARE
     eg_marc         TEXT;
     v_marc          TEXT;
     replace_rule    TEXT;
-    match_count     INT;
 BEGIN
 
     SELECT  q.marc INTO v_marc
@@ -1280,8 +1279,6 @@ $$ LANGUAGE PLPGSQL;
 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS BOOL AS $$
 DECLARE
     eg_id           BIGINT;
-    match_count     INT;
-    match_attr      vandelay.bib_attr_definition%ROWTYPE;
 BEGIN
 
     IF lwm_ratio_value IS NULL THEN
@@ -1323,7 +1320,6 @@ CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record ( import_id BIGINT,
 DECLARE
     eg_id           BIGINT;
     match_count     INT;
-    match_attr      vandelay.bib_attr_definition%ROWTYPE;
 BEGIN
 
     PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
@@ -1341,14 +1337,14 @@ BEGIN
     END IF;
 
     -- Check that the one match is on the first 901c
-    PERFORM *
+    SELECT  m.eg_record INTO eg_id
       FROM  vandelay.queued_bib_record q
             JOIN vandelay.bib_match m ON (m.queued_record = q.id)
       WHERE q.id = import_id
             AND m.eg_record = oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]',marc)::BIGINT;
 
     IF NOT FOUND THEN
-        -- RAISE NOTICE 'not a 901c match: %', match_attr.xpath;
+        -- RAISE NOTICE 'not a 901c match';
         RETURN FALSE;
     END IF;
 

commit d5c786e515791eaf53992dac8a6bd11520a661d6
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Apr 22 12:55:37 2011 -0400

    Secondary sort by quality ratio (higher means incoming is more "good" than existing) instead of simple incoming quality, which is a no-op (all the same)

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index f98f6ca..61b49c2 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -1301,7 +1301,10 @@ BEGIN
       WHERE m.queued_record = import_id
             AND r.id = m.eg_record
             AND m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc),0),1)::NUMERIC >= lwm_ratio_value
-      ORDER BY m.match_score DESC, m.quality DESC, id limit 1;
+      ORDER BY  m.match_score DESC,
+                m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc),0),1)::NUMERIC DESC,
+                id
+      LIMIT 1;
 
     IF eg_id IS NULL THEN
         -- RAISE NOTICE 'incoming record is not of hight enough quality';

commit 1aaf8a040116c614eb014fd2582e6bbc702b5e92
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Apr 22 12:33:16 2011 -0400

    functions for performing queue-wide vandelay.auto_overlay_bib_record_with_best

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index eb66bd5..f98f6ca 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -698,10 +698,10 @@ BEGIN
         vandelay.match_set_test_marcxml(my_bib_queue.match_set, NEW.marc) LOOP
 
         INSERT INTO vandelay.bib_match (
-            matched_set, queued_record, eg_record, quality
+            matched_set, queued_record, eg_record, match_score, quality
         ) VALUES (
             my_bib_queue.match_set, NEW.id, test_result.record,
-            test_result.quality
+            test_result.quality, vandelay.incoming_record_quality(NEW.marc)
         );
 
     END LOOP;
@@ -1281,12 +1281,9 @@ CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_i
 DECLARE
     eg_id           BIGINT;
     match_count     INT;
-    existing_qual   INT;
     match_attr      vandelay.bib_attr_definition%ROWTYPE;
 BEGIN
 
-    existing_qual := COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc),0),1);
-
     IF lwm_ratio_value IS NULL THEN
         lwm_ratio_value := 0.0;
     END IF;
@@ -1303,7 +1300,7 @@ BEGIN
             JOIN biblio.record_entry r
       WHERE m.queued_record = import_id
             AND r.id = m.eg_record
-            AND m.quality::NUMERIC / existing_qual::NUMERIC >= lwm_ratio_value
+            AND m.quality::NUMERIC / COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc),0),1)::NUMERIC >= lwm_ratio_value
       ORDER BY m.match_score DESC, m.quality DESC, id limit 1;
 
     IF eg_id IS NULL THEN
@@ -1374,6 +1371,28 @@ BEGIN
 END;
 $$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( queue_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS SETOF BIGINT AS $$
+DECLARE
+    queued_record   vandelay.queued_bib_record%ROWTYPE;
+BEGIN
+
+    FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
+
+        IF vandelay.auto_overlay_bib_record_with_best( queued_record.id, merge_profile_id, lwm_ratio_value ) THEN
+            RETURN NEXT queued_record.id;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+    
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
+    SELECT vandelay.auto_overlay_bib_queue_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
+$$ LANGUAGE SQL;
+
 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue ( queue_id BIGINT ) RETURNS SETOF BIGINT AS $$
     SELECT * FROM vandelay.auto_overlay_bib_queue( $1, NULL );
 $$ LANGUAGE SQL;

commit ca861020320e5a412114655eba10efe120a668a7
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Apr 22 12:30:17 2011 -0400

    match_score column for recording matchiness; functions for auto-overlay based on "bestness"; low-water-mark boundary for best-auto-overlay

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 14a7e14..eb66bd5 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -144,7 +144,8 @@ CREATE TABLE vandelay.bib_match (
 	matched_set 	INT			REFERENCES vandelay.match_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	queued_record	BIGINT		REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    quality         INT         NOT NULL DEFAULT 0
+    quality         INT         NOT NULL DEFAULT 0,
+    match_score     INT         NOT NULL DEFAULT 0
 );
 
 CREATE TABLE vandelay.import_item (
@@ -189,6 +190,7 @@ CREATE TABLE vandelay.merge_profile (
     replace_spec    TEXT,
     strip_spec      TEXT,
     preserve_spec   TEXT,
+    lwm_ratio       NUMERIC,
 	CONSTRAINT vand_merge_prof_owner_name_idx UNIQUE (owner,name),
 	CONSTRAINT add_replace_strip_or_preserve CHECK ((preserve_spec IS NOT NULL OR replace_spec IS NOT NULL) OR (preserve_spec IS NULL AND replace_spec IS NULL))
 );
@@ -1275,6 +1277,48 @@ BEGIN
 END;
 $$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS BOOL AS $$
+DECLARE
+    eg_id           BIGINT;
+    match_count     INT;
+    existing_qual   INT;
+    match_attr      vandelay.bib_attr_definition%ROWTYPE;
+BEGIN
+
+    existing_qual := COALESCE(NULLIF(vandelay.incoming_record_quality(r.marc),0),1);
+
+    IF lwm_ratio_value IS NULL THEN
+        lwm_ratio_value := 0.0;
+    END IF;
+
+    PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
+
+    IF FOUND THEN
+        -- RAISE NOTICE 'already imported, cannot auto-overlay'
+        RETURN FALSE;
+    END IF;
+
+    SELECT  m.eg_record INTO eg_id
+      FROM  vandelay.bib_match m
+            JOIN biblio.record_entry r
+      WHERE m.queued_record = import_id
+            AND r.id = m.eg_record
+            AND m.quality::NUMERIC / existing_qual::NUMERIC >= lwm_ratio_value
+      ORDER BY m.match_score DESC, m.quality DESC, id limit 1;
+
+    IF eg_id IS NULL THEN
+        -- RAISE NOTICE 'incoming record is not of hight enough quality';
+        RETURN FALSE;
+    END IF;
+
+    RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
+    SELECT vandelay.auto_overlay_bib_record_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
+$$ LANGUAGE SQL;
+
 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record ( import_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
 DECLARE
     eg_id           BIGINT;

commit 4de86d2c97b9117db2808681d091f85c79279373
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 22 12:12:30 2011 -0400

    Bugfix for negative matches

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index aee60a6..14a7e14 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -664,9 +664,7 @@ BEGIN
     IF node.bool_op IS NOT NULL THEN
         RETURN node.bool_op;
     ELSE
-        RETURN '(n' || node.id::TEXT || '.id IS ' ||
-            (CASE WHEN node.negate THEN '' ELSE 'NOT ' END) ||  -- sic!
-            'NULL)';
+        RETURN '(n' || node.id::TEXT || '.id IS NOT NULL)';
     END IF;
 END;
 $$ LANGUAGE PLPGSQL;

commit b2f78352b012820c1bbf4a6caa49d2380d9a3145
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 22 11:45:49 2011 -0400

    some changes in the UI to reflect DB side changes
    
    Negation and quality are now ignored for operator nodes, and are not
    settable in the UI.
    
    Also in the UI, you can no longer replace the root node of the tree with
    a non-op node, meaning the root of a tree is always AND or OR.
    
    This is due to some bug that hangs the UI now when you try to save a
    single-non-op-node tree, and it's not worth debugging: just remove the
    feature.

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
index c0586b5..7dd9fa5 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
@@ -9,12 +9,9 @@ dojo.declare(
     "openils.vandelay.TreeDndSource", dijit._tree.dndSource, {
         "_is_replaceable": function(spoint, dpoint, disroot) {
             /* An OP can replace anything, but non-OPs can only replace other
-             * non-OPs, EXCEPT when the dest is the root node (this allows
-             * for simple "trees" with only a single non-OP node.
+             * non-OPs.
              */
-            if (disroot)
-                return true;
-            else if (spoint.bool_op())
+            if (spoint.bool_op())
                 return true;
             else if (!dpoint.bool_op())
                 return true;
diff --git a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
index dfcfc44..5128bd2 100644
--- a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
@@ -205,7 +205,8 @@ function NodeEditor() {
         var nodes = _factories_by_type[type]();
         for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
 
-        this._add_consistent_controls(table);
+        if (type != "bool_op")
+            this._add_consistent_controls(table);
 
         dojo.create(
             "input", {
@@ -231,13 +232,11 @@ function NodeEditor() {
 }
 
 function render_vmsp_label(point, minimal) {
-    /* "minimal" mode has two implications:
-     *  1) for svf, only show the code, not the longer label.
-     *  2) for bool ops, completely ingore negation
+    /* "minimal" has this implication:
+     * for svf, only show the code, not the longer label.
      */
     if (point.bool_op()) {
-        return (!minimal && openils.Util.isTrue(point.negate()) ? "N" : "") +
-            point.bool_op();
+        return point.bool_op();
     } else if (point.svf()) {
         return (openils.Util.isTrue(point.negate()) ? "NOT " : "") + (
             minimal ?  point.svf() :
@@ -364,9 +363,8 @@ function redraw_expression_preview() {
 
 function render_expression_preview(r) {
     if (r.children().length) {
-        return (openils.Util.isTrue(r.negate()) ? "NOT " : "") +
-            "(" + r.children().map(render_expression_preview).join(
-            " " + render_vmsp_label(r, true /* minimal */) + " "
+        return "(" + r.children().map(render_expression_preview).join(
+            " " + render_vmsp_label(r) + " "
         ) + ")";
     } else if (!r.bool_op()) {
         return render_vmsp_label(r, true /* minimal */);

commit c7b99f8d90aa0b3a2be4fc609bfa83e5da261431
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 22 11:29:24 2011 -0400

    Replace vandelay.match_bib_record() with the new tree-y version

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index f64cc06..aee60a6 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -473,23 +473,216 @@ CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstor
     SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
 $_$ LANGUAGE SQL;
 
-CREATE OR REPLACE FUNCTION vandelay.match_bib_record ( ) RETURNS TRIGGER AS $func$
+-- Everything between this comment and the beginning of the definition of
+-- vandelay.match_bib_record() is strictly in service of that function.
+CREATE TYPE vandelay.match_set_test_result AS (record BIGINT, quality INTEGER);
+
+CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
+    match_set_id INTEGER, record_xml TEXT
+) RETURNS SETOF vandelay.match_set_test_result AS $$
+DECLARE
+    tags_rstore HSTORE;
+    svf_rstore  HSTORE;
+    coal        TEXT;
+    joins       TEXT;
+    query_      TEXT;
+    wq          TEXT;
+    qvalue      INTEGER;
+    rec         RECORD;
+BEGIN
+    tags_rstore := vandelay.flatten_marc_hstore(record_xml);
+    svf_rstore := vandelay.extract_rec_attrs(record_xml);
+
+    CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
+    CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
+
+    -- generate the where clause and return that directly (into wq), and as
+    -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
+    wq := vandelay.get_expr_from_match_set(match_set_id);
+
+    query_ := 'SELECT bre.id AS record, ';
+
+    -- qrows table is for the quality bits we add to the SELECT clause
+    SELECT ARRAY_TO_STRING(
+        ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
+    ) INTO coal FROM _vandelay_tmp_qrows;
+
+    -- our query string so far is the SELECT clause and the inital FROM.
+    -- no JOINs yet nor the WHERE clause
+    query_ := query_ || coal || ' AS quality ' || E'\n' ||
+        'FROM biblio.record_entry bre ';
+
+    -- jrows table is for the joins we must make (and the real text conditions)
+    SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
+        FROM _vandelay_tmp_jrows;
+
+    -- add those joins and the where clause to our query.
+    query_ := query_ || joins || E'\n' || 'WHERE ' || wq;
+
+    -- this will return rows of record,quality
+    FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
+        RETURN NEXT rec;
+    END LOOP;
+
+    DROP TABLE _vandelay_tmp_qrows;
+    DROP TABLE _vandelay_tmp_jrows;
+    RETURN;
+END;
+
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
+    record_xml TEXT
+) RETURNS HSTORE AS $$
+BEGIN
+    RETURN (SELECT
+        HSTORE(
+            ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
+            ARRAY_ACCUM(value)
+        )
+        FROM (
+            SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
+                FROM vandelay.flatten_marc(record_xml)
+                GROUP BY tag, subfield ORDER BY tag, subfield
+        ) subquery
+    );
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set(
+    match_set_id INTEGER
+) RETURNS TEXT AS $$
+DECLARE
+    root    vandelay.match_set_point;
+BEGIN
+    SELECT * INTO root FROM vandelay.match_set_point
+        WHERE parent IS NULL AND match_set = match_set_id;
+
+    RETURN vandelay.get_expr_from_match_set_point(root);
+END;
+$$  LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
+    node vandelay.match_set_point
+) RETURNS TEXT AS $$
+DECLARE
+    q           TEXT;
+    i           INTEGER;
+    this_op     TEXT;
+    children    INTEGER[];
+    child       vandelay.match_set_point;
+BEGIN
+    SELECT ARRAY_ACCUM(id) INTO children FROM vandelay.match_set_point
+        WHERE parent = node.id;
+
+    IF ARRAY_LENGTH(children, 1) > 0 THEN
+        this_op := vandelay._get_expr_render_one(node);
+        q := '(';
+        i := 1;
+        WHILE children[i] IS NOT NULL LOOP
+            SELECT * INTO child FROM vandelay.match_set_point
+                WHERE id = children[i];
+            IF i > 1 THEN
+                q := q || ' ' || this_op || ' ';
+            END IF;
+            i := i + 1;
+            q := q || vandelay.get_expr_from_match_set_point(child);
+        END LOOP;
+        q := q || ')';
+        RETURN q;
+    ELSIF node.bool_op IS NULL THEN
+        PERFORM vandelay._get_expr_push_qrow(node);
+        PERFORM vandelay._get_expr_push_jrow(node);
+        RETURN vandelay._get_expr_render_one(node);
+    ELSE
+        RETURN '';
+    END IF;
+END;
+$$  LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_push_qrow(
+    node vandelay.match_set_point
+) RETURNS VOID AS $$
+DECLARE
+BEGIN
+    INSERT INTO _vandelay_tmp_qrows (q) VALUES (node.id);
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
+    node vandelay.match_set_point
+) RETURNS VOID AS $$
+DECLARE
+    jrow        TEXT;
+    my_alias    TEXT;
+    op          TEXT;
+    tagkey      TEXT;
+BEGIN
+    IF node.negate THEN
+        op := '<>';
+    ELSE
+        op := '=';
+    END IF;
+
+    IF node.tag IS NOT NULL THEN
+        tagkey := node.tag;
+        IF node.subfield IS NOT NULL THEN
+            tagkey := tagkey || node.subfield;
+        END IF;
+    END IF;
+
+    my_alias := 'n' || node.id::TEXT;
+
+    jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
+        ' AS quality FROM metabib.';
+    IF node.tag IS NOT NULL THEN
+        jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
+            my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
+            node.tag || '''';
+        IF node.subfield IS NOT NULL THEN
+            jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
+                node.subfield || '''';
+        END IF;
+        jrow := jrow || ' AND (' || my_alias || '.value ' || op ||
+            ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
+    ELSE    -- svf
+        jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
+            my_alias || '.id = bre.id AND (' ||
+            my_alias || '.attrs->''' || node.svf ||
+            ''' ' || op || ' $2->''' || node.svf || '''))';
+    END IF;
+    INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_render_one(
+    node vandelay.match_set_point
+) RETURNS TEXT AS $$
+DECLARE
+    s           TEXT;
+BEGIN
+    IF node.bool_op IS NOT NULL THEN
+        RETURN node.bool_op;
+    ELSE
+        RETURN '(n' || node.id::TEXT || '.id IS ' ||
+            (CASE WHEN node.negate THEN '' ELSE 'NOT ' END) ||  -- sic!
+            'NULL)';
+    END IF;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
 DECLARE
     incoming_existing_id    TEXT;
     my_bib_queue            vandelay.bib_queue%ROWTYPE;
-    my_match_set            vandelay.match_set%ROWTYPE;
-    test                    vandelay.match_set_point%ROWTYPE;
-    potential_matches       BIGINT[];
-    matches                 BIGINT[];
-    rvalue                  TEXT;
-    quality_set             hstore;
+    test_result             vandelay.match_set_test_result%ROWTYPE;
     tmp_rec                 BIGINT;
-    tmp_quality             INT;
-    first_round             BOOL;
 BEGIN
     DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
 
-    incoming_existing_id := oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]',NEW.marc);
+    -- Perfect matches on 901$c exit early with a match with high quality.
+    incoming_existing_id :=
+        oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
 
     IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
         SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
@@ -501,64 +694,17 @@ BEGIN
 
     SELECT * INTO my_bib_queue FROM vandelay.bib_queue WHERE id = NEW.queue;
 
-    first_round := TRUE;
-    -- whew ... here we go ...
-
-
-    -- Commented out until replaced by tree-ish version
-/*
-    FOR test IN SELECT * FROM vandelay.match_set_point WHERE match_set = my_bib_queue.match_set ORDER BY required DESC LOOP
-        IF test.tag IS NOT NULL THEN
-            FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
-                SELECT ARRAY_ACCUM(DISTINCT record) INTO potential_matches FROM metabib.real_full_rec WHERE tag = test.tag AND subfield = test.subfield AND value = rvalue;
-
-                IF first_round THEN
-                    matches := potential_matches;
-                    first_round := FALSE;
-                ELSIF test.required THEN
-                    FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
-                        IF tmp_rec NOT IN (SELECT * FROM UNNEST(potential_matches)) THEN
-                            matches := evergreen.array_remove_item_by_value(matches, tmp_rec);
-                            potential_matches := evergreen.array_remove_item_by_value(potential_matches, tmp_rec);
-                        END IF;
-                    END LOOP;
-                END IF;
+    FOR test_result IN SELECT * FROM
+        vandelay.match_set_test_marcxml(my_bib_queue.match_set, NEW.marc) LOOP
 
-                -- add the quality for this match
-                FOR tmp_rec IN SELECT * FROM UNNEST(potential_matches) LOOP
-                    tmp_quality := COALESCE((quality_set -> tmp_rec::TEXT)::INT, 0);
-                    quality_set := quality_set || hstore(tmp_rec::TEXT, (tmp_quality + test.quality)::TEXT);
-                END LOOP;
-
-            END LOOP;
-        ELSE
-            rvalue := vandelay.vandelay.extract_rec_attrs(xml, ARRAY[test.svf]);
-
-            IF first_round THEN
-                matches := potential_matches;
-                first_round := FALSE;
-            ELSIF test.required THEN
-                FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
-                    IF tmp_rec NOT IN (SELECT * FROM UNNEST(potential_matches)) THEN
-                        matches := evergreen.array_remove_item_by_value(matches, tmp_rec);
-                        potential_matches := evergreen.array_remove_item_by_value(potential_matches, tmp_rec);
-                    END IF;
-                END LOOP;
-            END IF;
-
-            -- add the quality for this match
-            FOR tmp_rec IN SELECT * FROM UNNEST(potential_matches) LOOP
-                tmp_quality := COALESCE((quality_set -> tmp_rec::TEXT)::INT, 0);
-                quality_set := quality_set || hstore(tmp_rec::TEXT, (tmp_quality + test.quality)::TEXT);
-            END LOOP;
-
-        END IF;
-    END LOOP;
+        INSERT INTO vandelay.bib_match (
+            matched_set, queued_record, eg_record, quality
+        ) VALUES (
+            my_bib_queue.match_set, NEW.id, test_result.record,
+            test_result.quality
+        );
 
-    FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
-        INSERT INTO vandelay.bib_match (matched_set, queued_record, eg_record, quality) VALUES (my_bib_queue.match_set, NEW.id, tmp_rec, (quality_set -> tmp_rec::TEXT));
     END LOOP;
-*/
 
     RETURN NEW;
 END;

commit 813ac365b8ebf1e4d2e4434002ab248a0160833e
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Apr 22 11:14:21 2011 -0400

    We don't have a matched_attr column anymore, because we're using the fancy expression tree, so test for 901c match directly

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 3a69144..f64cc06 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -1152,26 +1152,18 @@ BEGIN
         RETURN FALSE;
     END IF;
 
-    SELECT  d.* INTO match_attr
-      FROM  vandelay.bib_attr_definition d
-            JOIN vandelay.queued_bib_record_attr a ON (a.field = d.id)
-            JOIN vandelay.bib_match m ON (m.matched_attr = a.id)
-      WHERE m.queued_record = import_id;
+    -- Check that the one match is on the first 901c
+    PERFORM *
+      FROM  vandelay.queued_bib_record q
+            JOIN vandelay.bib_match m ON (m.queued_record = q.id)
+      WHERE q.id = import_id
+            AND m.eg_record = oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]',marc)::BIGINT;
 
-    IF NOT (match_attr.xpath ~ '@tag="901"' AND match_attr.xpath ~ '@code="c"') THEN
+    IF NOT FOUND THEN
         -- RAISE NOTICE 'not a 901c match: %', match_attr.xpath;
         RETURN FALSE;
     END IF;
 
-    SELECT  m.eg_record INTO eg_id
-      FROM  vandelay.bib_match m
-      WHERE m.queued_record = import_id
-      LIMIT 1;
-
-    IF eg_id IS NULL THEN
-        RETURN FALSE;
-    END IF;
-
     RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
 END;
 $$ LANGUAGE PLPGSQL;

commit a02e77558027149e5a8d405daba583baa394c796
Author: berick <berick at commidore64.esilibrary.com>
Date:   Fri Apr 22 10:38:38 2011 -0400

    initial cut of capturing bib/auth import errors

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index ef02b02..0ea5798 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -606,22 +606,79 @@ sub queued_records_with_matches {
     return [ map {$_->{queued_record}} @$data ];
 }
 
+# tracks any import errors, commits the current xact, responds to the client
+sub finish_rec_import_attempt {
+    my %args = @_;
+    my $error = $args{import_error} || 'general.unknown';
+    my $evt = $args{evt};
+    my $rec = $args{rec};
+    my $e = $args{e};
+
+    # error tracking
+    if($rec) {
+
+        if($error or $evt) {
+            # since an error occurred, there's no guarantee the transaction wasn't 
+            # rolled back.  force a rollback and create a new editor.
+            $e->rollback;
+            $e = new_editor(xact => 1);
+            $rec->import_error($error);
+
+            if($evt) {
+                my $detail = sprintf("%s : %s", $evt->{textcode}, substr($evt->{desc}, 0, 140));
+                $rec->error_detail($detail);
+            }
+
+            my $method = 'update_vandelay_queued_bib_record';
+            $method =~ s/bib/authority/ if $args{type} eq 'auth';
+            $e->$method($rec) and $e->commit or $e->rollback;
+
+        } else {
+            # successful import
+            $e->commit;
+        }
+
+    } else {
+        # requested queued record was not found
+        $e->rollback;
+    }
+        
+    # respond to client
+    if($args{report_all} or ($args{progress} % $args{step}) == 0) {
+
+        $args{conn}->respond({
+            total => $args{total}, 
+            progress => $args{progress}, 
+            imported => ($rec) ? $rec->id : undef,
+            err_event => $evt
+        });
+
+        # report often at first, climb quickly, then hold steady
+        $args{step} *= 2 unless $args{step} == 256;
+    }
+}
+
+
+
 sub import_record_list_impl {
     my($self, $conn, $rec_ids, $requestor, $args) = @_;
 
     my $overlay_map = $args->{overlay_map} || {};
     my $type = $self->{record_type};
-    my $total = @$rec_ids;
-    my $count = 0;
     my %queues;
 
-    my $step = 1;
+    my %report_args = (
+        progress => 0,
+        step => 1,
+        conn => $conn,
+        total => scalar(@$rec_ids),
+        report_all => $$args{report_all}
+    );
 
     my $auto_overlay_exact = $$args{auto_overlay_exact};
     my $auto_overlay_1match = $$args{auto_overlay_1match};
     my $merge_profile = $$args{merge_profile};
     my $bib_source = $$args{bib_source};
-    my $report_all = $$args{report_all};
 
     my $overlay_func = 'vandelay.overlay_bib_record';
     my $auto_overlay_func = 'vandelay.auto_overlay_bib_record';
@@ -632,13 +689,11 @@ sub import_record_list_impl {
     my $update_queue_func = 'update_vandelay_bib_queue';
     my $rec_class = 'vqbr';
 
-    my %bib_sources;
     my $editor = new_editor();
-    my $sources = $editor->search_config_bib_source({id => {'!=' => undef}});
 
-    foreach my $src (@$sources) {
-        $bib_sources{$src->id} = $src->source;
-    }
+    my %bib_sources;
+    my $sources = $editor->search_config_bib_source({id => {'!=' => undef}});
+    $bib_sources{$_->id} = $_->source for @$sources;
 
     if($type eq 'auth') {
         $overlay_func =~ s/bib/auth/o;
@@ -654,12 +709,15 @@ sub import_record_list_impl {
     my @success_rec_ids;
     for my $rec_id (@$rec_ids) {
 
+        my $error = 0;
         my $overlay_target = $overlay_map->{$rec_id};
 
-        my $error = 0;
         my $e = new_editor(xact => 1);
         $e->requestor($requestor);
 
+        $report_args{progress}++;
+        $report_args{e} = $e;
+
         my $rec = $e->$retrieve_func([
             $rec_id,
             {   flesh => 1,
@@ -668,11 +726,12 @@ sub import_record_list_impl {
         ]);
 
         unless($rec) {
-            $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->event});
-            $e->rollback;
+            finish_rec_import_attempt(%report_args, evt => $e->event);
             next;
         }
 
+        $report_args{rec} = $rec;
+
         if($rec->import_time) {
             $e->rollback;
             next;
@@ -784,9 +843,8 @@ sub import_record_list_impl {
                 }
 
                 if($U->event_code($record)) {
-
-                    $e->event($record); 
-                    $e->rollback;
+                    my $import_error = 'import.duplicate.tcn' if $record->{textcode} eq 'TCN_EXISTS';
+                    finish_rec_import_attempt(%report_args, import_error => $import_error, evt => $record);
 
                 } else {
 
@@ -801,16 +859,11 @@ sub import_record_list_impl {
 
         if($imported) {
             push @success_rec_ids, $rec_id;
-            $e->commit;
+            finish_rec_import_attempt(%report_args);
+
         } else {
             # Send an update whenever there's an error
-            $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->event});
-        }
-
-        if($report_all or (++$count % $step) == 0) {
-            $conn->respond({total => $total, progress => $count, imported => $rec_id});
-            # report often at first, climb quickly, then hold steady
-            $step *= 2 unless $step == 256;
+            finish_rec_import_attempt(%report_args, evt => $e->event);
         }
     }
 
@@ -834,9 +887,10 @@ sub import_record_list_impl {
     	$e->rollback;
     }
 
+    # import the copies
     import_record_asset_list_impl($conn, \@success_rec_ids, $requestor);
 
-    $conn->respond({total => $total, progress => $count});
+    $conn->respond({total => $report_args{total}, progress => $report_args{progress}});
     return undef;
 }
 
@@ -1062,8 +1116,14 @@ sub import_record_asset_list_impl {
             # --------------------------------------------------------------------------------
             # see if a valid circ_modifier was provided
             # --------------------------------------------------------------------------------
-            #if($copy->circ_modifier and not $e->retrieve_config_circ_modifier($item->circ_modifier)) {
             if($copy->circ_modifier and not $e->search_config_circ_modifier({code=>$item->circ_modifier})->[0]) {
+                $report_args{import_error} = 'import.item.invalid.circ_modifier';
+                respond_with_status(%report_args, evt => $e->die_event);
+                next;
+            }
+
+            if($copy->location and not $e->retrieve_asset_copy_location($copy->location)) {
+                $report_args{import_error} = 'import.item.invalid.location';
                 respond_with_status(%report_args, evt => $e->die_event);
                 next;
             }
@@ -1071,6 +1131,8 @@ sub import_record_asset_list_impl {
             if($evt = OpenILS::Application::Cat::AssetCommon->create_copy($e, $vol, $copy)) {
                 try { $e->rollback } otherwise {}; # sometimes calls die_event, sometimes not
                 $report_args{evt} = $evt;
+                $report_args{import_error} = 'import.item.duplicate.barcode' 
+                    if $evt->{textcode} eq 'ITEM_BARCODE_EXISTS';
                 respond_with_status(%report_args);
                 next;
             }
@@ -1115,7 +1177,7 @@ sub respond_with_status {
     my $error = $args{import_error};
     my $evt = $args{evt};
 
-    if($error || $evt) {
+    if($error or $evt) {
 
         my $item = $args{import_item};
 
@@ -1123,7 +1185,7 @@ sub respond_with_status {
         $item->import_error($error);
 
         if($evt) {
-            my $detail = sprintf("%s : %s", $evt->{textcode}, substr($evt->{desc}, 0, 120));
+            my $detail = sprintf("%s : %s", $evt->{textcode}, substr($evt->{desc}, 0, 140));
             $item->error_detail($detail);
         }
 
@@ -1136,7 +1198,8 @@ sub respond_with_status {
     $args{step} *= 2 unless $args{step} == 256;
 
     $args{conn}->respond({
-        map { $_ => $args{$_} } qw/total progress success_count/
+        map { $_ => $args{$_} } qw/total progress success_count/,
+        err_event => $evt
     });
 }
 
diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index 25e9c1d..e45999d 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -8657,6 +8657,17 @@ INSERT INTO action_trigger.environment (event_def, path) VALUES
     (37, 'circ_lib.billing_address')
 ;
 
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.circ_modifier', oils_i18n_gettext('import.item.invalid.circ_modifier', 'Import failed due to invalid circulation modifier', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.location', oils_i18n_gettext('import.item.invalid.location', 'Import failed due to invalid copy location', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.tcn', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
+
 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
     'ui.cat.volume_copy_editor.horizontal',
     oils_i18n_gettext(

commit a2a99fc7368401e56c0ff331b39f800ff320ca60
Author: berick <berick at commidore64.esilibrary.com>
Date:   Thu Apr 21 16:55:26 2011 -0400

    initial item-import-failure reporting;  needs testing

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 4d3049a..ef02b02 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -775,6 +775,7 @@ sub import_record_list_impl {
             
                 # No overlay / merge occurred.  Do a traditional record import by creating a new record
             
+                $logger->info("vl: creating new $type record for queued record $rec_id");
                 if($type eq 'bib') {
                     $record = OpenILS::Application::Cat::BibCommon->biblio_record_xml_import($e, $rec->marc, $bib_sources{$rec->bib_source});
                 } else {
@@ -1004,11 +1005,16 @@ sub retrieve_queue_summary {
 sub import_record_asset_list_impl {
     my($conn, $rec_ids, $requestor) = @_;
 
-    my $total = @$rec_ids;
-    my $try_count = 0;
-    my $in_count = 0;
     my $roe = new_editor(xact=> 1, requestor => $requestor);
 
+    my %report_args = (
+        conn => $conn,
+        total => scalar(@$rec_ids),
+        step => 1, # how often to respond
+        progress => 0,
+        in_count => 0,
+    );
+
     for my $rec_id (@$rec_ids) {
         my $rec = $roe->retrieve_vandelay_queued_bib_record($rec_id);
         next unless $rec and $rec->import_time;
@@ -1017,7 +1023,8 @@ sub import_record_asset_list_impl {
         for my $item_id (@$item_ids) {
             my $e = new_editor(requestor => $requestor, xact => 1);
             my $item = $e->retrieve_vandelay_import_item($item_id);
-            $try_count++;
+            $report_args{progress}++;
+            $report_args{import_item} = $item;
 
             # --------------------------------------------------------------------------------
             # Find or create the volume
@@ -1027,7 +1034,7 @@ sub import_record_asset_list_impl {
                     $e, $item->call_number, $rec->imported_as, $item->owning_lib);
 
             if($evt) {
-                respond_with_status($conn, $total, $try_count, $in_count, $evt);
+                respond_with_status(%report_args, evt => $evt);
                 next;
             }
 
@@ -1057,13 +1064,14 @@ sub import_record_asset_list_impl {
             # --------------------------------------------------------------------------------
             #if($copy->circ_modifier and not $e->retrieve_config_circ_modifier($item->circ_modifier)) {
             if($copy->circ_modifier and not $e->search_config_circ_modifier({code=>$item->circ_modifier})->[0]) {
-                respond_with_status($conn, $total, $try_count, $in_count, $e->die_event);
+                respond_with_status(%report_args, evt => $e->die_event);
                 next;
             }
 
             if($evt = OpenILS::Application::Cat::AssetCommon->create_copy($e, $vol, $copy)) {
                 try { $e->rollback } otherwise {}; # sometimes calls die_event, sometimes not
-                respond_with_status($conn, $total, $try_count, $in_count, $evt);
+                $report_args{evt} = $evt;
+                respond_with_status(%report_args);
                 next;
             }
 
@@ -1074,7 +1082,7 @@ sub import_record_asset_list_impl {
                 $e, $copy, '', $item->pub_note, 1) if $item->pub_note;
 
             if($evt) {
-                respond_with_status($conn, $total, $try_count, $in_count, $evt);
+                respond_with_status(%report_args, evt => $evt);
                 next;
             }
 
@@ -1082,7 +1090,7 @@ sub import_record_asset_list_impl {
                 $e, $copy, '', $item->priv_note, 1) if $item->priv_note;
 
             if($evt) {
-                respond_with_status($conn, $total, $try_count, $in_count, $evt);
+                respond_with_status(%report_args, evt => $evt);
                 next;
             }
 
@@ -1090,7 +1098,8 @@ sub import_record_asset_list_impl {
             # Item import succeeded
             # --------------------------------------------------------------------------------
             $e->commit;
-            respond_with_status($conn, $total, $try_count, ++$in_count, undef, imported_as => $copy->id);
+            $report_args{in_count}++;
+            respond_with_status(%report_args, imported_as => $copy->id)
         }
     }
     $roe->rollback;
@@ -1099,12 +1108,36 @@ sub import_record_asset_list_impl {
 
 
 sub respond_with_status {
-    my($conn, $total, $try_count, $success_count, $err, %args) = @_;
-    $conn->respond({
-        total => $total, 
-        progress => $try_count, 
-        err_event => $err, 
-        success_count => $success_count, %args }) if $err or ($try_count % 5 == 0);
+    my %args = @_;
+
+    #  If the import failed, track the failure reason
+
+    my $error = $args{import_error};
+    my $evt = $args{evt};
+
+    if($error || $evt) {
+
+        my $item = $args{import_item};
+
+        my $error ||= 'general.unknown';
+        $item->import_error($error);
+
+        if($evt) {
+            my $detail = sprintf("%s : %s", $evt->{textcode}, substr($evt->{desc}, 0, 120));
+            $item->error_detail($detail);
+        }
+
+        my $e = new_editor(xact => 1);
+        $e->update_vandelay_import_item($item);
+        $e->commit;
+    }
+
+    return unless $args{report_all} or ($args{progress} % $args{step}) == 0;
+    $args{step} *= 2 unless $args{step} == 256;
+
+    $args{conn}->respond({
+        map { $_ => $args{$_} } qw/total progress success_count/
+    });
 }
 
 __PACKAGE__->register_method(  

commit 8aa208b2796b05c74d2a4efa126b71514448e4fc
Author: berick <berick at commidore64.esilibrary.com>
Date:   Thu Apr 21 14:27:41 2011 -0400

    previous cast solution didn't work.  casting inline instead

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index f95c4f1..3a69144 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -492,10 +492,9 @@ BEGIN
     incoming_existing_id := oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]',NEW.marc);
 
     IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
-        incoming_existing_id := incoming_existing_id::bigint;
-        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id;
+        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
         IF tmp_rec IS NOT NULL THEN
-            INSERT INTO vandelay.bib_match (queued_record, eg_record, quality) VALUES ( NEW.id, incoming_existing_id, 9999);
+            INSERT INTO vandelay.bib_match (queued_record, eg_record, quality) VALUES ( NEW.id, incoming_existing_id::bigint, 9999);
             RETURN NEW;
         END IF;
     END IF;

commit 091e82590d2d7b63fa4e882cbc9681b470ba97e2
Author: berick <berick at commidore64.esilibrary.com>
Date:   Thu Apr 21 13:52:49 2011 -0400

    repaired some thinko's in vandelay.match_bib_record stored proc

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 7b4ba02..f95c4f1 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -491,10 +491,11 @@ BEGIN
 
     incoming_existing_id := oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]',NEW.marc);
 
-    IF incoming_existing_id IS NOT NULL THEN
-        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = exact_id;
+    IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
+        incoming_existing_id := incoming_existing_id::bigint;
+        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id;
         IF tmp_rec IS NOT NULL THEN
-            INSERT INTO vandelay.bib_match (queued_record, eg_record, quality) VALUES ( NEW.id, exact_id, 9999);
+            INSERT INTO vandelay.bib_match (queued_record, eg_record, quality) VALUES ( NEW.id, incoming_existing_id, 9999);
             RETURN NEW;
         END IF;
     END IF;

commit c9ee753591520129794452347403801545894caf
Author: berick <berick at commidore64.esilibrary.com>
Date:   Thu Apr 21 13:51:51 2011 -0400

    cleaned out some non-existent fields from vandelay classes

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index c89957b..0c59bd4 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -278,7 +278,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true"/>
 			<field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
 			<field reporter:label="Type" name="queue_type" reporter:datatype="text"/>
-			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
 			<field reporter:label="Item Import Attribute Definition" name="item_attr_def" reporter:datatype="link"/>
 		</fields>
@@ -417,7 +416,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true"/>
 			<field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
 			<field reporter:label="Type" name="queue_type" reporter:datatype="text"/>
-			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
 		</fields>
 		<links>
@@ -472,7 +470,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Description" name="description" reporter:datatype="text" oils_persist:i18n="true"/>
 			<field reporter:label="XPath" name="xpath" reporter:datatype="text"/>
 			<field reporter:label="Remove RegExp" name="remove" reporter:datatype="text"/>
-			<field reporter:label="Is Identifier?" name="ident" reporter:datatype="bool"/>
 		</fields>
 		<links/>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">

commit a050890c67f43f989810bcb76695ff4f13eea938
Author: senator <lebbeous at esilibrary.com>
Date:   Mon Apr 18 13:53:13 2011 -0400

    Working ML method to update a tree

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index aa830ad..4d3049a 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1111,7 +1111,12 @@ __PACKAGE__->register_method(
     api_name    => "open-ils.vandelay.match_set.get_tree",
     method      => "match_set_get_tree",
     api_level   => 1,
-    argc        => 1
+    argc        => 2,
+    signature   => {
+        desc    => q/For a given vms object, return a tree of match set points
+                    represented by a vmsp object with recursively fleshed
+                    children./
+    }
 );
 
 sub match_set_get_tree {
@@ -1137,4 +1142,62 @@ sub match_set_get_tree {
 }
 
 
+__PACKAGE__->register_method(
+    api_name    => "open-ils.vandelay.match_set.update",
+    method      => "match_set_update_tree",
+    api_level   => 1,
+    argc        => 3,
+    signature   => {
+        desc => q/Replace any vmsp objects associated with a given (by ID) vms
+                with the given objects (recursively fleshed vmsp tree)./
+    }
+);
+
+sub _walk_new_vmsp {
+    my ($e, $match_set_id, $node, $parent_id) = @_;
+
+    my $point = new Fieldmapper::vandelay::match_set_point;
+    $point->parent($parent_id);
+    $point->match_set($match_set_id);
+    $point->$_($node->$_) for (qw/bool_op svf tag subfield negate quality/);
+
+    $e->create_vandelay_match_set_point($point) or return $e->die_event;
+
+    $parent_id = $e->data->id;
+    if ($node->children && @{$node->children}) {
+        for (@{$node->children}) {
+            return $e->die_event if
+                _walk_new_vmsp($e, $match_set_id, $_, $parent_id);
+        }
+    }
+
+    return;
+}
+
+sub match_set_update_tree {
+    my ($self, $conn, $authtoken, $match_set_id, $tree) = @_;
+
+    my $e = new_editor("xact" => 1, "authtoken" => $authtoken);
+    $e->checkauth or return $e->die_event;
+
+    my $set = $e->retrieve_vandelay_match_set($match_set_id) or
+        return $e->die_event;
+
+    $e->allowed("ADMIN_IMPORT_MATCH_SET", $set->owner) or
+        return $e->die_event;
+
+    my $existing = $e->search_vandelay_match_set_point([
+        {"match_set" => $match_set_id},
+        {"order_by" => {"vmsp" => "id DESC"}}
+    ]) or return $e->die_event;
+
+    foreach (@$existing) {
+        $e->delete_vandelay_match_set_point($_) or return $e->die_event;
+    }
+
+    _walk_new_vmsp($e, $match_set_id, $tree);
+
+    $e->commit or return $e->die_event;
+}
+
 1;

commit bae118889863f9fa37fbafd195c5d8ed86404f11
Author: senator <lebbeous at esilibrary.com>
Date:   Mon Apr 18 11:20:28 2011 -0400

    Terminology change, more consitent with existing crad editor

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
index 4d92442..7e76613 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
@@ -8,5 +8,6 @@
     "OK": "Ok",
     "POINT_NEEDS_ONE": "A match point must be exactly one of the following: boolean operator, MARC tag/subfield pair, single-value-field.",
     "FAULTY_MARC": "A MARC tag must be identified by three digits, and the subfield must be one non-whitespace, non-control character.",
-    "WORKING_MP_HERE": "Choose from among the three buttons above to add a new match point."
+    "WORKING_MP_HERE": "Choose from among the three buttons above to add a new match point.",
+    "SVF": "Record Attribute"
 }
diff --git a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
index 0c58528..dfcfc44 100644
--- a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
@@ -44,7 +44,7 @@ function NodeEditor() {
             dojo.attr(select, "id", "svf-select");
             var label = dojo.create(
                 "label", {
-                    "for": "svf-select", "innerHTML": "Single-Value-Field:"
+                    "for": "svf-select", "innerHTML": localeStrings.SVF + ":"
                 }
             );
 
diff --git a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
index de7c838..4290e0b 100644
--- a/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
@@ -76,7 +76,7 @@
     </div>
     <div id="vmsp-buttons">
         Add new
-        <button onclick="node_editor.add('svf');">Single-Value-Field</button>
+        <button onclick="node_editor.add('svf');">Record Attribute</button>
         <button onclick="node_editor.add('tag');">MARC Tag and Subfield</button>
         <button onclick="node_editor.add('bool_op');">Boolean Operator</button>
     </div>

commit fe0c574d0fd474cf1145786ed959ceb4df840874
Author: senator <lebbeous at esilibrary.com>
Date:   Mon Apr 18 11:15:20 2011 -0400

    make the "Your Expression" preview more accurate by
    
    showing "NOT (a OR b OR c)" instead of "(a NOR b NOR c)".
    
    This really only matters in the case of a single clause inside the
    negated expression:
    
    Now such a thing shows up as "NOT (a)" versus "(a)"

diff --git a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
index 2212dcd..0c58528 100644
--- a/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
@@ -231,9 +231,12 @@ function NodeEditor() {
 }
 
 function render_vmsp_label(point, minimal) {
-    /* quick and dirty */
+    /* "minimal" mode has two implications:
+     *  1) for svf, only show the code, not the longer label.
+     *  2) for bool ops, completely ingore negation
+     */
     if (point.bool_op()) {
-        return (openils.Util.isTrue(point.negate()) ? "N" : "") +
+        return (!minimal && openils.Util.isTrue(point.negate()) ? "N" : "") +
             point.bool_op();
     } else if (point.svf()) {
         return (openils.Util.isTrue(point.negate()) ? "NOT " : "") + (
@@ -361,8 +364,9 @@ function redraw_expression_preview() {
 
 function render_expression_preview(r) {
     if (r.children().length) {
-        return "(" + r.children().map(render_expression_preview).join(
-            " " + render_vmsp_label(r) + " "
+        return (openils.Util.isTrue(r.negate()) ? "NOT " : "") +
+            "(" + r.children().map(render_expression_preview).join(
+            " " + render_vmsp_label(r, true /* minimal */) + " "
         ) + ")";
     } else if (!r.bool_op()) {
         return render_vmsp_label(r, true /* minimal */);

commit 3a406e5cb384a22ade99069a69cec8d9091073c6
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 15 18:01:22 2011 -0400

    Admin -> Server Administration -> Import Match Sets
    
    That's where you'll find the interface for the match set expression
    editor in the staff client.

diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd
index 4043024..6352be9 100644
--- a/Open-ILS/web/opac/locale/en-US/lang.dtd
+++ b/Open-ILS/web/opac/locale/en-US/lang.dtd
@@ -730,6 +730,7 @@
 <!ENTITY staff.main.menu.admin.server_admin.conify.z3950_source.label "Z39.50 Servers">
 <!ENTITY staff.main.menu.admin.server_admin.conify.circulation_modifier.label "Circulation Modifiers">
 <!ENTITY staff.main.menu.admin.server_admin.conify.org_unit_setting_type "Organization Unit Setting Types">
+<!ENTITY staff.main.menu.admin.server_admin.conify.import_match_set "Import Match Sets">
 <!ENTITY staff.main.menu.admin.server_admin.conify.usr_setting_type "User Setting Types">
 <!ENTITY staff.main.menu.admin.server_admin.conify.config_hard_due_date "Hard Due Date Changes">
 <!ENTITY staff.main.menu.admin.server_admin.conify.config_rule_circ_duration "Circulation Duration Rules">
diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu.js b/Open-ILS/xul/staff_client/chrome/content/main/menu.js
index fc85a8f..79949f4 100644
--- a/Open-ILS/xul/staff_client/chrome/content/main/menu.js
+++ b/Open-ILS/xul/staff_client/chrome/content/main/menu.js
@@ -951,6 +951,10 @@ main.menu.prototype = {
                 ['oncommand'],
                 function(event) { open_eg_web_page('conify/global/config/org_unit_setting_type', null, event); }
             ],
+            'cmd_server_admin_import_match_set' : [
+                ['oncommand'],
+                function(event) { open_eg_web_page('conify/global/vandelay/match_set', null, event); }
+            ],
             'cmd_server_admin_usr_setting_type' : [
                 ['oncommand'],
                 function(event) { open_eg_web_page('conify/global/config/usr_setting_type', null, event); }
diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
index 8c79bbf..741c0ce 100644
--- a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
+++ b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
@@ -182,6 +182,7 @@
              perm="ADMIN_GLOBAL_FLAG"
              />
     <command id="cmd_server_admin_org_unit_setting_type" />
+    <command id="cmd_server_admin_import_match_set" />
     <command id="cmd_server_admin_usr_setting_type" />
     <command id="cmd_server_admin_config_hard_due_date"
              perm="CREATE_CIRC_DURATION DELETE_CIRC_DURATION UPDATE_CIRC_DURATION"
@@ -479,6 +480,7 @@
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.z3950_source.label;" command="cmd_server_admin_z39_source"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.circulation_modifier.label;" command="cmd_server_admin_circ_mod"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.global_flag.label;" command="cmd_server_admin_global_flag"/>
+                <menuitem label="&staff.main.menu.admin.server_admin.conify.import_match_set;" command="cmd_server_admin_import_match_set"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.org_unit_setting_type;" command="cmd_server_admin_org_unit_setting_type"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.usr_setting_type;" command="cmd_server_admin_usr_setting_type"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.config_hard_due_date;" command="cmd_server_admin_config_hard_due_date"/>

commit 9e82b590abbc53e2e2059067028a25e4c25dbef2
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 15 17:56:07 2011 -0400

    move parts to conify namespace

diff --git a/Open-ILS/web/js/ui/default/vandelay/match_set.js b/Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
similarity index 100%
rename from Open-ILS/web/js/ui/default/vandelay/match_set.js
rename to Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
diff --git a/Open-ILS/web/templates/default/vandelay/match_set.tt2 b/Open-ILS/web/templates/default/conify/global/vandelay/match_set.tt2
similarity index 97%
rename from Open-ILS/web/templates/default/vandelay/match_set.tt2
rename to Open-ILS/web/templates/default/conify/global/vandelay/match_set.tt2
index 0d59f54..fad64db 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set.tt2
+++ b/Open-ILS/web/templates/default/conify/global/vandelay/match_set.tt2
@@ -66,7 +66,7 @@
 
     function tree_editor_link(datum) {
         if (!datum) return "";
-        return '<a href="[% ctx.base_path %]/vandelay/match_set_tree?match_set=' +
+        return '<a href="[% ctx.base_path %]/eg/conify/global/vandelay/match_set_tree?match_set=' +
             datum.id + '">' + datum.name + '</a>';
     }
 
diff --git a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
similarity index 97%
rename from Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
rename to Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
index 1ebf055..de7c838 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
@@ -107,5 +107,5 @@
 </div>
 <div jsId="progress_dialog" dojoType="openils.widget.ProgressDialog"></div>
 <script type="text/javascript"
-    src="[% ctx.media_prefix %]/js/ui/default/vandelay/match_set.js"></script>
+    src="[% ctx.media_prefix %]/js/ui/default/conify/global/vandelay/match_set.js"></script>
 [% END %]

commit c78f3ec233c08fa7a3526621f06ea1521a47074c
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 15 17:49:12 2011 -0400

    UI perfection. it previews. it saves...
    
    (or if it would if the ML method were done, but it will be easy now)
    
    I just need to move things to the conify path and add staff client menu
    entries

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
index 0748a2f..c0586b5 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
@@ -30,6 +30,22 @@ dojo.declare(
                 window._tree_dnd_controllers = [];
 
             window._tree_dnd_controllers.push(this);
+
+            dojo.connect(
+                this.tree.model.store, "onNew", this,
+                function() { this.redraw_expression_preview(); }
+            );
+            dojo.connect(
+                this.tree.model.store, "onDelete", this,
+                function() { this.redraw_expression_preview(); }
+            );
+        },
+        "redraw_expression_preview": function() {
+            if (typeof(window.redraw_expression_preview) == "function") {
+                window.redraw_expression_preview();
+            } else {
+                console.log("no redraw_expression_preview function registered");
+            }
         },
         "checkItemAcceptance": function(target, source, position) {
             if (!source._ready || source == this) return;
@@ -101,6 +117,8 @@ dojo.declare(
                 );
             }
 
+            this.redraw_expression_preview();
+
             /* just because this is at the end of the default implementation: */
             this.onDndCancel();
         }
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
index af26d09..e694238 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
@@ -10,32 +10,32 @@ dojo.require("openils.Util");
 function _simple_item(model, item) {
     /* Instead of model.getLabel(), could do
      * model.store.getValue(item, "blah") or something like that ... */
-    return {
-        "label": model.getLabel(item),
-        "match_point": String(model.store.getValue(item, "match_point")),
-        "children": {}
-    };
+    var mp = model.store.getValue(item, "match_point");
+    mp.children([]);
+    return mp;
 }
 
 dojo.declare(
     "openils.vandelay.TreeStoreModel", dijit.tree.TreeStoreModel, {
         "replace_mode": 0,
-        "getSimpleTree": function(item, oncomplete, result) {
+        "get_simple_tree": function(item, oncomplete, result) {
             var self = this;
-            if (!result) result = {};
-
-            var mykey = this.getIdentity(item);
-            result[mykey] = _simple_item(this, item);
-            var child_collector = result[mykey].children;
+            var me;
+            if (!result) {
+                me = result = _simple_item(this, item);
+            } else {
+                me = _simple_item(this, item);
+                result.push(me);
+            }
 
             if (this.mayHaveChildren(item)) {
                 this.getChildren(
                     item, function(children) {
+                        var kids_here = [];
                         for (var i = 0; i < children.length; i++) {
-                            self.getSimpleTree(
-                                children[i], null, child_collector
-                            );
+                            self.get_simple_tree(children[i], null, kids_here);
                         }
+                        me.children(kids_here);
                         if (oncomplete) oncomplete(result);
                     }
                 );
@@ -48,9 +48,5 @@ dojo.declare(
             else
                 return true;
         }
-//        "newItem": function(args, parent) {
-//            if (!this.mayHaveChildren(parent)) return;
-//            return this.inherited(arguments);
-//        }
     }
 );
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
index ae9277e..4d92442 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
@@ -7,5 +7,6 @@
     "NO_CAN_DO": "An error has occurred. Close this interface and try again.",
     "OK": "Ok",
     "POINT_NEEDS_ONE": "A match point must be exactly one of the following: boolean operator, MARC tag/subfield pair, single-value-field.",
-    "FAULTY_MARC": "A MARC tag must be identified by three digits, and the subfield must be one non-whitespace, non-control character."
+    "FAULTY_MARC": "A MARC tag must be identified by three digits, and the subfield must be one non-whitespace, non-control character.",
+    "WORKING_MP_HERE": "Choose from among the three buttons above to add a new match point."
 }
diff --git a/Open-ILS/web/js/ui/default/vandelay/match_set.js b/Open-ILS/web/js/ui/default/vandelay/match_set.js
index 4c32278..2212dcd 100644
--- a/Open-ILS/web/js/ui/default/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/vandelay/match_set.js
@@ -10,7 +10,7 @@ dojo.require("openils.Util");
 dojo.require("openils.PermaCrud");
 dojo.require("openils.widget.ProgressDialog");
 
-var localeStrings, node_editor, _crads, CGI, tree;
+var localeStrings, node_editor, _crads, CGI, tree, match_set;
 
 function _find_crad_by_name(name) {
     for (var i = 0; i < _crads.length; i++) {
@@ -120,7 +120,10 @@ function NodeEditor() {
 
     this.clear = function() {
         this.dnd_source.selectAll().deleteSelectedNodes();
-        dojo.empty(this.node_editor_container);
+        dojo.create(
+            "em", {"innerHTML": localeStrings.WORKING_MP_HERE},
+            this.node_editor_container, "only"
+        );
         this.dnd_source._ready = false;
     };
 
@@ -215,21 +218,28 @@ function NodeEditor() {
         );
 
         dojo.place(table, this.node_editor_container, "only");
-        /* XXX around here attach other data structures to the node */
+
         this.dnd_source.insertNodes(false, [draggable]);
+
+        /* nice */
+        try { dojo.query("select, input", table)[0].focus(); }
+        catch(E) { console.log(String(E)); }
+
     };
 
     this._init.apply(this, arguments);
 }
 
-function render_vmsp_label(point) {
+function render_vmsp_label(point, minimal) {
     /* quick and dirty */
     if (point.bool_op()) {
         return (openils.Util.isTrue(point.negate()) ? "N" : "") +
             point.bool_op();
     } else if (point.svf()) {
-        return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
-            point.svf() + " / " + _find_crad_by_name(point.svf()).label();
+        return (openils.Util.isTrue(point.negate()) ? "NOT " : "") + (
+            minimal ?  point.svf() :
+                (point.svf() + " / " + _find_crad_by_name(point.svf()).label())
+        );
     } else {
         return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
             point.tag() + " \u2021" + point.subfield();
@@ -333,6 +343,62 @@ function render_vms_metadata(match_set) {
     dojo.byId("vms-mtype").innerHTML = match_set.mtype();
 }
 
+function redraw_expression_preview() {
+    tree.model.getRoot(
+        function(root) {
+            tree.model.get_simple_tree(
+                root, function(r) {
+                    dojo.attr(
+                        "expr-preview",
+                        "innerHTML",
+                        render_expression_preview(r)
+                    );
+                }
+            );
+        }
+    );
+}
+
+function render_expression_preview(r) {
+    if (r.children().length) {
+        return "(" + r.children().map(render_expression_preview).join(
+            " " + render_vmsp_label(r) + " "
+        ) + ")";
+    } else if (!r.bool_op()) {
+        return render_vmsp_label(r, true /* minimal */);
+    } else {
+        return "()";
+    }
+}
+
+function save_tree() {
+    progress_dialog.show(true);
+
+    tree.model.getRoot(
+        function(root) {
+            tree.model.get_simple_tree(
+                root, function(r) {
+                    fieldmapper.standardRequest(
+                        ["open-ils.vandelay",
+                            "open-ils.vandelay.match_set.update"],/* XXX TODO */{
+                            "params": [
+                                openils.User.authtoken, match_set.id(), r
+                            ],
+                            "async": true,
+                            "oncomplete": function(r) {
+                                progress_dialog.hide();
+                                /* catch exceptions */
+                                r = openils.Util.readResponse(r);
+
+                                location.href = location.href;
+                            }
+                        }
+                    );
+                }
+            );
+        }
+    );
+}
 function my_init() {
     progress_dialog.show(true);
 
@@ -348,7 +414,9 @@ function my_init() {
         return;
     }
 
-    render_vms_metadata(pcrud.retrieve("vms", CGI.param("match_set")));
+    render_vms_metadata(
+        match_set = pcrud.retrieve("vms", CGI.param("match_set"))
+    );
 
     /* No-one should have hundreds of these or anything, but theoretically
      * this could be problematic with a big enough list of crad objects. */
@@ -400,6 +468,9 @@ function my_init() {
                                          "move." */
         }
     );
+
+    redraw_expression_preview();
+    node_editor.clear();
     progress_dialog.hide();
 }
 
diff --git a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
index 02ac2d4..1ebf055 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
@@ -12,7 +12,15 @@
     #src-pane { float: left; width: 49%; }
     #tree-pane { float: right; width: 50%; }
     #submit-row { clear: both; text-align: center; padding-top: 1.5ex; }
-    #submit-row hr { margin: 1ex 0; }
+    #submit-row hr { margin: 1.5ex 0; }
+    #expr-preview-row { margin: 2ex 0; }
+    #expr-preview {
+        font-family: monospace;
+        font-size: 125%;
+        font-weight: bold;
+        background-color: #000066;
+        color: white;
+    }
     .node-editor { margin-bottom: 1.5ex; }
     .node-editor td { padding: 0.5ex; }
     li { background-color: #ddd; }
@@ -62,8 +70,10 @@
     </tr>
 </table>
 <div class="outer">
-    <div><!-- XXX TODO: consider a read-only display here of the query as built
-        so far from the treet --></div>
+    <div id="expr-preview-row">
+        <em>Your Expression:</em>
+        <span id="expr-preview"></span>
+    </div>
     <div id="vmsp-buttons">
         Add new
         <button onclick="node_editor.add('svf');">Single-Value-Field</button>

commit e04c57f1234925ba9acd681e2d944060a843975a
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 15 14:28:16 2011 -0400

    Match Set Tree editor improvements/cleanup
    
    You can now replace the root node of the tree with anything,
    
    and the children will be automatically deleted.  This makes it easier to
    create a single-node tree with no operators, if the user should so
    desire.
    
    comment cleanup, etc

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
index 832ef6f..0748a2f 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
@@ -7,11 +7,14 @@ dojo.require("dijit._tree.dndSource");
  */
 dojo.declare(
     "openils.vandelay.TreeDndSource", dijit._tree.dndSource, {
-        "_is_replaceable": function(spoint, dpoint) {
+        "_is_replaceable": function(spoint, dpoint, disroot) {
             /* An OP can replace anything, but non-OPs can only replace other
-             * non-OPs
+             * non-OPs, EXCEPT when the dest is the root node (this allows
+             * for simple "trees" with only a single non-OP node.
              */
-            if (spoint.bool_op())
+            if (disroot)
+                return true;
+            else if (spoint.bool_op())
                 return true;
             else if (!dpoint.bool_op())
                 return true;
@@ -32,13 +35,12 @@ dojo.declare(
             if (!source._ready || source == this) return;
 
             if (this.tree.model.replace_mode) {
+                var ditem = dijit.getEnclosingWidget(target).item;
                 return (
                     position == "over" && this._is_replaceable(
                         source.getAllNodes()[0].match_point,
-                        this.tree.model.store.getValue(
-                            dijit.getEnclosingWidget(target).item,
-                            "match_point"
-                        )
+                        this.tree.model.store.getValue(ditem, "match_point"),
+                        ditem === this.tree.model.root
                     )
                 );
             } else {
@@ -82,6 +84,15 @@ dojo.declare(
                 if (k == "id") continue;    /* can't use this / don't need it */
                 store.setValue(item, k, new_params[k]);
             }
+            if (this.tree.model.root === item) {    /* replacing root node */
+                if (!new_params.match_point.bool_op()) {
+                    /* If we're here, we've replaced the root node with
+                     * something that isn't a bool op, so we need to nuke
+                     * any children that the item has.
+                     */
+                    store.setValue(item, "children", []);
+                }
+            }
             if (typeof(window.render_vmsp_label) == "function") {
                 store.setValue(
                     item,
diff --git a/Open-ILS/web/js/ui/default/vandelay/match_set.js b/Open-ILS/web/js/ui/default/vandelay/match_set.js
index 990f21d..4c32278 100644
--- a/Open-ILS/web/js/ui/default/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/vandelay/match_set.js
@@ -222,8 +222,6 @@ function NodeEditor() {
     this._init.apply(this, arguments);
 }
 
-/* XXX replace later with code that will suit this function's purpose
- * as well as that of update_draggable. */
 function render_vmsp_label(point) {
     /* quick and dirty */
     if (point.bool_op()) {
@@ -294,7 +292,6 @@ function new_match_set_tree() {
  *
  */
 function dojoize_match_set_tree(point, refgen) {
-    /* XXX TODO test with deeper trees! */
     var root = false;
     if (!refgen) {
         if (!point) {
@@ -329,7 +326,7 @@ function dojoize_match_set_tree(point, refgen) {
     return results;
 }
 
-function render_match_set_description(match_set) {
+function render_vms_metadata(match_set) {
     dojo.byId("vms-name").innerHTML = match_set.name();
     dojo.byId("vms-owner").innerHTML =
         aou.findOrgUnit(match_set.owner()).name();
@@ -351,14 +348,11 @@ function my_init() {
         return;
     }
 
-    var match_set = pcrud.retrieve("vms", CGI.param("match_set"));
-    render_match_set_description(match_set);
+    render_vms_metadata(pcrud.retrieve("vms", CGI.param("match_set")));
 
-    /* XXX No-one should have hundreds of these or anything, but theoretically
+    /* No-one should have hundreds of these or anything, but theoretically
      * this could be problematic with a big enough list of crad objects. */
-    _crads = pcrud.retrieveAll(
-        "crad", {"order_by": {"crad": "label"}}
-    );
+    _crads = pcrud.retrieveAll("crad", {"order_by": {"crad": "label"}});
 
     var match_set_tree = fieldmapper.standardRequest(
         ["open-ils.vandelay", "open-ils.vandelay.match_set.get_tree"],
@@ -395,12 +389,15 @@ function my_init() {
     dojo.connect(
         src, "onDndDrop", null,
         function(source, nodes, copy, target) {
-            /* XXX because of the... interesting... characteristics of DnD
+            /* Because of the... interesting... characteristics of DnD
              * design in dojo/dijit (at least as of 1.3), this callback will
              * fire both for our working node dndSource and for the tree!
              */
             if (source == this)
-                node_editor.clear();  /* because otherwise this acts like a copy! */
+                node_editor.clear();  /* ... because otherwise this acts like a
+                                         copy operation no matter what the user
+                                         does, even though we really want a
+                                         "move." */
         }
     );
     progress_dialog.hide();

commit 128554e383bafb7f9ac7b0edd3ec17ebb3ff90e0
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 15 12:28:47 2011 -0400

    make sure user can only add reasonbly valid match points to the tree

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
index 30132e3..ae9277e 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
@@ -4,5 +4,8 @@
     "EXACTLY_ONE": "First select exactly one node from the tree on the right side of the screen.",
     "EXIT_REPLACE_MODE": "Exit Replace Mode",
     "ENTER_REPLACE_MODE": "Enter Replace Mode",
-    "NO_CAN_DO": "An error has occurred. Close this interface and try again."
+    "NO_CAN_DO": "An error has occurred. Close this interface and try again.",
+    "OK": "Ok",
+    "POINT_NEEDS_ONE": "A match point must be exactly one of the following: boolean operator, MARC tag/subfield pair, single-value-field.",
+    "FAULTY_MARC": "A MARC tag must be identified by three digits, and the subfield must be one non-whitespace, non-control character."
 }
diff --git a/Open-ILS/web/js/ui/default/vandelay/match_set.js b/Open-ILS/web/js/ui/default/vandelay/match_set.js
index 20fb624..990f21d 100644
--- a/Open-ILS/web/js/ui/default/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/vandelay/match_set.js
@@ -124,6 +124,32 @@ function NodeEditor() {
         this.dnd_source._ready = false;
     };
 
+    this.is_sensible = function(mp) {
+        var need_one = 0;
+        ["tag", "svf", "bool_op"].forEach(
+            function(field) { if (mp[field]()) need_one++; }
+        );
+
+        if (need_one != 1) {
+            alert(localeStrings.POINT_NEEDS_ONE);
+            return false;
+        }
+
+        if (mp.tag()) {
+            if (
+                !mp.tag().match(/^\d{3}$/) ||
+                mp.subfield().length != 1 ||
+                !mp.subfield().match(/\S/) ||
+                mp.subfield().charCodeAt(0) < 32
+            ) {
+                alert(localeStrings.FAULTY_MARC);
+                return false;
+            }
+        }
+
+        return true;
+    };
+
     this.build_vmsp = function() {
         var match_point = new vmsp();
         var controls = dojo.query("[fmfield]", this.node_editor_container);
@@ -132,14 +158,18 @@ function NodeEditor() {
             var value = _simple_value_getter(controls[i]);
             match_point[field](value);
         }
-        return match_point;
+
+        if (!this.is_sensible(match_point)) return null;    /* will alert() */
+        else return match_point;
     };
 
     this.update_draggable = function(draggable) {
-        draggable.match_point = this.build_vmsp();
-        dojo.attr(
-            draggable, "innerHTML", render_vmsp_label(draggable.match_point)
-        );
+        var mp;
+
+        if (!(mp = this.build_vmsp())) return;  /* will alert() */
+
+        draggable.match_point = mp;
+        dojo.attr(draggable, "innerHTML", render_vmsp_label(mp));
         this.dnd_source._ready = true;
     };
 
@@ -176,7 +206,7 @@ function NodeEditor() {
 
         dojo.create(
             "input", {
-                "type": "submit", "value": "Ok",
+                "type": "submit", "value": localeStrings.OK,
                 "onclick": function() { self.update_draggable(draggable); }
             }, dojo.create(
                 "td", {"colspan": 2, "align": "center"},
diff --git a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
index f8572e9..02ac2d4 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
@@ -4,6 +4,7 @@
     h1 { margin: 1ex 0; }
     .outer { clear: both; margin-bottom: 1ex; }
     button { margin: 0 0.5em; }
+    input[type=submit] { padding: 0 0.5em; }
     #tree-here { margin-bottom: 1.5em; }
     #vms-table { padding-bottom: 2ex; }
     #vms-table th { text-align: right; }

commit 5e0b24e22043d49b2b87eb7157df1b97e7713b91
Author: senator <lebbeous at esilibrary.com>
Date:   Fri Apr 15 12:02:07 2011 -0400

    1) implemented working replace mode 2) autocreate new tree on launch if needed

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
index 8ccd836..832ef6f 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
@@ -7,12 +7,15 @@ dojo.require("dijit._tree.dndSource");
  */
 dojo.declare(
     "openils.vandelay.TreeDndSource", dijit._tree.dndSource, {
-        "_is_replaceable": function(src_item, target_item) {
+        "_is_replaceable": function(spoint, dpoint) {
             /* An OP can replace anything, but non-OPs can only replace other
              * non-OPs
              */
-            console.log("src item: " + src_item + " target item: " + target_item);
-            return true;    /* XXX TODO FINISHME */
+            if (spoint.bool_op())
+                return true;
+            else if (!dpoint.bool_op())
+                return true;
+            return false;
         },
         "constructor": function() {
             /* Given a tree object, there seems to be no way to access its
@@ -28,11 +31,14 @@ dojo.declare(
         "checkItemAcceptance": function(target, source, position) {
             if (!source._ready || source == this) return;
 
-            if (this.tree.model._replace_mode) {
+            if (this.tree.model.replace_mode) {
                 return (
                     position == "over" && this._is_replaceable(
                         source.getAllNodes()[0].match_point,
-                        dijit.getEnclosingWidget(target).item.match_point
+                        this.tree.model.store.getValue(
+                            dijit.getEnclosingWidget(target).item,
+                            "match_point"
+                        )
                     )
                 );
             } else {
@@ -47,12 +53,45 @@ dojo.declare(
              * only when we want the item to be draggable */
         },
         "itemCreator": function(nodes, somethingelse) {
-            console.log("gew: " + dijit.getEnclosingWidget(somethingelse).item.name);
-            console.log("dojo.dnd.manager.copy: " + dojo.dnd.manager.copy);
             var default_items = this.inherited(arguments);
             for (var i = 0; i < default_items.length; i++)
                 default_items[i].match_point = nodes[i].match_point;
             return default_items;
+        },
+        "onDndDrop": function(source, nodes, copy) {
+            if (
+                !this.tree.model.replace_mode ||
+                this.containerState != "Over" ||
+                this.dropPosition == "Before" ||
+                this.dropPosition == "After" ||
+                source == this
+            ) {
+                return this.inherited(arguments);
+            }
+
+            /* This method only comes into play for our "replace mode" */
+
+            var target_widget = dijit.getEnclosingWidget(this.targetAnchor);
+            var new_params = this.itemCreator(nodes, this.targetAnchor)[0];
+
+            /* Here, we morph target_widget.item into the new item */
+
+            var store = this.tree.model.store;
+            var item = target_widget.item;
+            for (var k in new_params) {
+                if (k == "id") continue;    /* can't use this / don't need it */
+                store.setValue(item, k, new_params[k]);
+            }
+            if (typeof(window.render_vmsp_label) == "function") {
+                store.setValue(
+                    item,
+                    "name",
+                    window.render_vmsp_label(new_params.match_point)
+                );
+            }
+
+            /* just because this is at the end of the default implementation: */
+            this.onDndCancel();
         }
     }
 );
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
index 2a35b70..af26d09 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
@@ -19,7 +19,7 @@ function _simple_item(model, item) {
 
 dojo.declare(
     "openils.vandelay.TreeStoreModel", dijit.tree.TreeStoreModel, {
-        "_replace_mode": 0,
+        "replace_mode": 0,
         "getSimpleTree": function(item, oncomplete, result) {
             var self = this;
             if (!result) result = {};
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
index ceb72bb..30132e3 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
@@ -3,5 +3,6 @@
     "LEAVE_ROOT_ALONE": "You cannot delete the root node of a tree (but you can replace it).",
     "EXACTLY_ONE": "First select exactly one node from the tree on the right side of the screen.",
     "EXIT_REPLACE_MODE": "Exit Replace Mode",
-    "ENTER_REPLACE_MODE": "Enter Replace Mode"
+    "ENTER_REPLACE_MODE": "Enter Replace Mode",
+    "NO_CAN_DO": "An error has occurred. Close this interface and try again."
 }
diff --git a/Open-ILS/web/js/ui/default/vandelay/match_set.js b/Open-ILS/web/js/ui/default/vandelay/match_set.js
index f9fa8ec..20fb624 100644
--- a/Open-ILS/web/js/ui/default/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/vandelay/match_set.js
@@ -208,14 +208,21 @@ function render_vmsp_label(point) {
     }
 }
 
-function replace_mode() {
-    tree.model._replace_mode ^= 1;
+function replace_mode(explicit) {
+    if (typeof explicit == "undefined")
+        tree.model.replace_mode ^= 1;
+    else
+        tree.model.replace_mode = explicit;
+
     dojo.attr(
         "replacer", "innerHTML",
         localeStrings[
-            (tree.model._replace_mode ? "EXIT" : "ENTER") + "_REPLACE_MODE"
+            (tree.model.replace_mode ? "EXIT" : "ENTER") + "_REPLACE_MODE"
         ]
     );
+    dojo[tree.model.replace_mode ? "addClass" : "removeClass"](
+        "replacer", "replace-mode"
+    );
 }
 
 function delete_selected_in_tree() {
@@ -231,6 +238,19 @@ function delete_selected_in_tree() {
     );
 }
 
+function new_match_set_tree() {
+    var point = new vmsp();
+    point.bool_op("AND");
+    return [
+        {
+            "id": "root",
+            "children": [],
+            "name": render_vmsp_label(point),
+            "match_point": point
+        }
+    ];
+}
+
 /* dojoize_match_set_tree() takes an argument, "point", that is actually a
  * vmsp fieldmapper object with descendants fleshed hierarchically. It turns
  * that into a syntactically flat array but preserving the hierarchy
@@ -247,6 +267,9 @@ function dojoize_match_set_tree(point, refgen) {
     /* XXX TODO test with deeper trees! */
     var root = false;
     if (!refgen) {
+        if (!point) {
+            return new_match_set_tree();
+        }
         refgen = 0;
         root = true;
     }
@@ -292,6 +315,12 @@ function my_init() {
     pcrud = new openils.PermaCrud();
     CGI = new openils.CGI();
 
+    if (!CGI.param("match_set")) {
+        alert(localeStrings.NO_CAN_DO);
+        progress_dialog.hide();
+        return;
+    }
+
     var match_set = pcrud.retrieve("vms", CGI.param("match_set"));
     render_match_set_description(match_set);
 
@@ -331,6 +360,8 @@ function my_init() {
 
     node_editor = new NodeEditor(src, "node-editor-container");
 
+    replace_mode(0);
+
     dojo.connect(
         src, "onDndDrop", null,
         function(source, nodes, copy, target) {
diff --git a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
index 06f1679..f8572e9 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
@@ -15,6 +15,7 @@
     .node-editor { margin-bottom: 1.5ex; }
     .node-editor td { padding: 0.5ex; }
     li { background-color: #ddd; }
+    .replace-mode { background-color: #990; color: #fff; }
 </style>
 <h1>[% ctx.page_title %]</h1>
 <table id="vms-table">
@@ -85,9 +86,7 @@
             <button id="deleter" onclick="delete_selected_in_tree()">
                 Delete Selected Node
             </button>
-            <button id="replacer" onclick="replace_mode()">
-                Enter Replace Mode
-            </button>
+            <button id="replacer" onclick="replace_mode()"></button>
         </div>
     </div>
 </div>

commit 6c32ca2e574f57e2eb57bc29c75b3ff506651a9e
Author: senator <lebbeous at esilibrary.com>
Date:   Thu Apr 14 18:14:32 2011 -0400

    lots of things here, but nearly last big UI todo is...
    
    to implement openils.vandelay.TreeDndSource._is_replaceable() and then
    make the changes to itemCreator

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
index aa5f2f4..8ccd836 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
@@ -7,6 +7,13 @@ dojo.require("dijit._tree.dndSource");
  */
 dojo.declare(
     "openils.vandelay.TreeDndSource", dijit._tree.dndSource, {
+        "_is_replaceable": function(src_item, target_item) {
+            /* An OP can replace anything, but non-OPs can only replace other
+             * non-OPs
+             */
+            console.log("src item: " + src_item + " target item: " + target_item);
+            return true;    /* XXX TODO FINISHME */
+        },
         "constructor": function() {
             /* Given a tree object, there seems to be no way to access its
              * dndController, which seems to be the only thing that knows
@@ -19,18 +26,29 @@ dojo.declare(
             window._tree_dnd_controllers.push(this);
         },
         "checkItemAcceptance": function(target, source, position) {
-            return (
-                source._ready && (
+            if (!source._ready || source == this) return;
+
+            if (this.tree.model._replace_mode) {
+                return (
+                    position == "over" && this._is_replaceable(
+                        source.getAllNodes()[0].match_point,
+                        dijit.getEnclosingWidget(target).item.match_point
+                    )
+                );
+            } else {
+                return (
                     position != "over" ||
                     this.tree.model.mayHaveChildren(
                         dijit.getEnclosingWidget(target).item
                     )
-                )
-            );
+                );
+            }
             /* code in match_set.js makes sure that source._ready gets set true
              * only when we want the item to be draggable */
         },
-        "itemCreator": function(nodes) {
+        "itemCreator": function(nodes, somethingelse) {
+            console.log("gew: " + dijit.getEnclosingWidget(somethingelse).item.name);
+            console.log("dojo.dnd.manager.copy: " + dojo.dnd.manager.copy);
             var default_items = this.inherited(arguments);
             for (var i = 0; i < default_items.length; i++)
                 default_items[i].match_point = nodes[i].match_point;
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
index 61d2821..2a35b70 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
@@ -19,6 +19,7 @@ function _simple_item(model, item) {
 
 dojo.declare(
     "openils.vandelay.TreeStoreModel", dijit.tree.TreeStoreModel, {
+        "_replace_mode": 0,
         "getSimpleTree": function(item, oncomplete, result) {
             var self = this;
             if (!result) result = {};
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
index 1b9f01b..ceb72bb 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
@@ -1,3 +1,7 @@
 {
-    "DEFINE_MP": "Define this match point using the above fields, then drag me to the tree on the right."
+    "DEFINE_MP": "Define this match point using the above fields, then drag me to the tree on the right.",
+    "LEAVE_ROOT_ALONE": "You cannot delete the root node of a tree (but you can replace it).",
+    "EXACTLY_ONE": "First select exactly one node from the tree on the right side of the screen.",
+    "EXIT_REPLACE_MODE": "Exit Replace Mode",
+    "ENTER_REPLACE_MODE": "Enter Replace Mode"
 }
diff --git a/Open-ILS/web/js/ui/default/vandelay/match_set.js b/Open-ILS/web/js/ui/default/vandelay/match_set.js
index 4b7dd90..f9fa8ec 100644
--- a/Open-ILS/web/js/ui/default/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/vandelay/match_set.js
@@ -1,7 +1,6 @@
 dojo.require("dijit.Tree");
 dojo.require("dijit.form.Button");
 dojo.require("dojo.data.ItemFileWriteStore");
-//dojo.require("openils.vandelay.DndSource");
 dojo.require("dojo.dnd.Source");
 dojo.require("openils.vandelay.TreeDndSource");
 dojo.require("openils.vandelay.TreeStoreModel");
@@ -122,47 +121,25 @@ function NodeEditor() {
     this.clear = function() {
         this.dnd_source.selectAll().deleteSelectedNodes();
         dojo.empty(this.node_editor_container);
+        this.dnd_source._ready = false;
     };
 
-    this.update_draggable = function(draggable) {
-        var s = "";
-        draggable.match_point = new vmsp();
-        var had_op = false;
-        dojo.query("[fmfield]", this.node_editor_container).forEach(
-            function(control) {
-                var used_svf = null;
-                var field = dojo.attr(control, "fmfield");
-                var value = _simple_value_getter(control);
-                draggable.match_point[field](value);
-
-                if (field == "subfield")
-                    s += " \u2021";
-                if (field == "svf")
-                    used_svf = value;
-                if (field == "quality")
-                    return;
-                if (field == "bool_op")
-                    had_op = true;
-                if (field == "negate") {
-                    if (value) {
-                        if (had_op)
-                            s = "<strong>N</strong>" + s;
-                        else
-                            s = "<strong>NOT</strong> " + s;
-                    }
-                } else {
-                    s += value;
-                }
+    this.build_vmsp = function() {
+        var match_point = new vmsp();
+        var controls = dojo.query("[fmfield]", this.node_editor_container);
+        for (var i = 0; i < controls.length; i++) {
+            var field = dojo.attr(controls[i], "fmfield");
+            var value = _simple_value_getter(controls[i]);
+            match_point[field](value);
+        }
+        return match_point;
+    };
 
-                if (used_svf !== null) {
-                    var our_crad = _find_crad_by_name(used_svf);
-                    /* XXX i18n, use fmtted strings */
-                    s += " / " + our_crad.label() + "<br /><em>" +
-                        (our_crad.description() || "") + "</em><br />";
-                }
-            }
+    this.update_draggable = function(draggable) {
+        draggable.match_point = this.build_vmsp();
+        dojo.attr(
+            draggable, "innerHTML", render_vmsp_label(draggable.match_point)
         );
-        dojo.attr(draggable, "innerHTML", s);
         this.dnd_source._ready = true;
     };
 
@@ -210,7 +187,6 @@ function NodeEditor() {
         dojo.place(table, this.node_editor_container, "only");
         /* XXX around here attach other data structures to the node */
         this.dnd_source.insertNodes(false, [draggable]);
-        this.dnd_source._ready = false;
     };
 
     this._init.apply(this, arguments);
@@ -218,24 +194,39 @@ function NodeEditor() {
 
 /* XXX replace later with code that will suit this function's purpose
  * as well as that of update_draggable. */
-function display_name_from_point(point) {
+function render_vmsp_label(point) {
     /* quick and dirty */
     if (point.bool_op()) {
-        return (point.negate() == "t" ? "N" : "") + point.bool_op();
+        return (openils.Util.isTrue(point.negate()) ? "N" : "") +
+            point.bool_op();
     } else if (point.svf()) {
-        return (point.negate() == "t" ? "NOT " : "") + point.svf();
+        return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
+            point.svf() + " / " + _find_crad_by_name(point.svf()).label();
     } else {
-        return (point.negate() == "t" ? "NOT " : "") + point.tag() +
-            "\u2021" + point.subfield();
+        return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
+            point.tag() + " \u2021" + point.subfield();
     }
 }
 
-function delete_selected_from_tree() {
+function replace_mode() {
+    tree.model._replace_mode ^= 1;
+    dojo.attr(
+        "replacer", "innerHTML",
+        localeStrings[
+            (tree.model._replace_mode ? "EXIT" : "ENTER") + "_REPLACE_MODE"
+        ]
+    );
+}
+
+function delete_selected_in_tree() {
     /* relies on the fact that we only have one tree that would have
      * registered a dnd controller. */
     _tree_dnd_controllers[0].getSelectedItems().forEach(
         function(item) {
-            tree.model.store.deleteItem(item);
+            if (item === tree.model.root)
+                alert(localeStrings.LEAVE_ROOT_ALONE);
+            else
+                tree.model.store.deleteItem(item);
         }
     );
 }
@@ -264,7 +255,7 @@ function dojoize_match_set_tree(point, refgen) {
     point.children([]);
     var item = {
         "id": (root ? "root" : refgen),
-        "name": display_name_from_point(point),
+        "name": render_vmsp_label(point),
         "match_point": point.clone(),
         "children": []
     };
@@ -292,7 +283,7 @@ function render_match_set_description(match_set) {
     dojo.byId("vms-mtype").innerHTML = match_set.mtype();
 }
 
-function init_test() {
+function my_init() {
     progress_dialog.show(true);
 
     dojo.requireLocalization("openils.vandelay", "match_set");
@@ -315,19 +306,6 @@ function init_test() {
         [openils.User.authtoken, CGI.param("match_set")]
     );
 
-//        {
-//            "identifier": "id", "label": "name", "items": [
-//                {
-//                    "id": "root", "name": "AND",
-//                    "children": [
-//                        {"_reference": "leaf0"}, {"_reference": "leaf1"}
-//                    ]
-//                },
-//                {"id": "leaf0", "name": "nonsense test"},
-//                {"id": "leaf1", "name": "more nonsense"}
-//            ]
-//        }
-
     var store = new dojo.data.ItemFileWriteStore({
         "data": {
             "identifier": "id",
@@ -336,14 +314,14 @@ function init_test() {
         }
     });
 
-    var treeModel = new openils.vandelay.TreeStoreModel({
-        store: store, "query": {"id": "root"}
+    var tree_model = new openils.vandelay.TreeStoreModel({
+        "store": store, "query": {"id": "root"}
     });
 
     var src = new dojo.dnd.Source("src-here");
     tree = new dijit.Tree(
         {
-            "model": treeModel,
+            "model": tree_model,
             "dndController": openils.vandelay.TreeDndSource,
             "dragThreshold": 8,
             "betweenThreshold": 5,
@@ -356,22 +334,15 @@ function init_test() {
     dojo.connect(
         src, "onDndDrop", null,
         function(source, nodes, copy, target) {
-            if (source == this) {
-                var model = target.tree.model;
-                model.getRoot(
-                    function(root) {
-                        model.getSimpleTree(
-                            root, function(results) { alert(js2JSON(results)); }
-                        );
-                    }
-                );
+            /* XXX because of the... interesting... characteristics of DnD
+             * design in dojo/dijit (at least as of 1.3), this callback will
+             * fire both for our working node dndSource and for the tree!
+             */
+            if (source == this)
                 node_editor.clear();  /* because otherwise this acts like a copy! */
-            } else {
-                alert("XXX [src] nodes length is " + nodes.length); /* XXX DEBUG */
-            }
         }
     );
     progress_dialog.hide();
 }
 
-openils.Util.addOnLoad(init_test);
+openils.Util.addOnLoad(my_init);
diff --git a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
index 6796406..06f1679 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
@@ -2,14 +2,16 @@
 [% ctx.page_title = 'Vandelay Match Set Editor' %]
 <style type="text/css">
     h1 { margin: 1ex 0; }
-    .outer { clear: both; }
-    #vmsp-buttons button { margin: 0 0.5em; }
+    .outer { clear: both; margin-bottom: 1ex; }
+    button { margin: 0 0.5em; }
     #tree-here { margin-bottom: 1.5em; }
     #vms-table { padding-bottom: 2ex; }
     #vms-table th { text-align: right; }
     #vms-table td { padding-left: 1em; }
     #src-pane { float: left; width: 49%; }
     #tree-pane { float: right; width: 50%; }
+    #submit-row { clear: both; text-align: center; padding-top: 1.5ex; }
+    #submit-row hr { margin: 1ex 0; }
     .node-editor { margin-bottom: 1.5ex; }
     .node-editor td { padding: 0.5ex; }
     li { background-color: #ddd; }
@@ -61,7 +63,7 @@
     <div><!-- XXX TODO: consider a read-only display here of the query as built
         so far from the treet --></div>
     <div id="vmsp-buttons">
-        New
+        Add new
         <button onclick="node_editor.add('svf');">Single-Value-Field</button>
         <button onclick="node_editor.add('tag');">MARC Tag and Subfield</button>
         <button onclick="node_editor.add('bool_op');">Boolean Operator</button>
@@ -69,6 +71,7 @@
 </div>
 <div class="outer" style="margin-top: 2ex;">
     <div id="src-pane">
+        <div><big>Working Match Point</big></div>
         <div>
             <form id="node-editor-container" onsubmit="return false;"></form>
         </div>
@@ -78,11 +81,20 @@
     <div id="tree-pane">
         <div><big>Your Expression</big></div>
         <div id="tree-here"></div>
-        <button id="deleter" onclick="delete_selected_from_tree()">
-            Deleted Selected Node
-        </button>
+        <div>
+            <button id="deleter" onclick="delete_selected_in_tree()">
+                Delete Selected Node
+            </button>
+            <button id="replacer" onclick="replace_mode()">
+                Enter Replace Mode
+            </button>
+        </div>
     </div>
 </div>
+<div id="submit-row">
+    <hr />
+    <button onclick="save_tree()">Save Changes</button>
+</div>
 <div jsId="progress_dialog" dojoType="openils.widget.ProgressDialog"></div>
 <script type="text/javascript"
     src="[% ctx.media_prefix %]/js/ui/default/vandelay/match_set.js"></script>

commit 18f70edb704e984ff70b7182d4795f5e28ef4168
Author: senator <lebbeous at esilibrary.com>
Date:   Thu Apr 14 11:45:43 2011 -0400

    Ability to delete nodes from working tree. Show metadata about match_set.

diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
index 6438132..aa5f2f4 100644
--- a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
@@ -7,6 +7,17 @@ dojo.require("dijit._tree.dndSource");
  */
 dojo.declare(
     "openils.vandelay.TreeDndSource", dijit._tree.dndSource, {
+        "constructor": function() {
+            /* Given a tree object, there seems to be no way to access its
+             * dndController, which seems to be the only thing that knows
+             * about a tree's selected nodes.  So we register instances
+             * in a global variable in order to find them later. :-(
+             */
+            if (!window._tree_dnd_controllers)
+                window._tree_dnd_controllers = [];
+
+            window._tree_dnd_controllers.push(this);
+        },
         "checkItemAcceptance": function(target, source, position) {
             return (
                 source._ready && (
diff --git a/Open-ILS/web/js/ui/default/vandelay/match_set.js b/Open-ILS/web/js/ui/default/vandelay/match_set.js
index 7d6a74f..4b7dd90 100644
--- a/Open-ILS/web/js/ui/default/vandelay/match_set.js
+++ b/Open-ILS/web/js/ui/default/vandelay/match_set.js
@@ -11,10 +11,7 @@ dojo.require("openils.Util");
 dojo.require("openils.PermaCrud");
 dojo.require("openils.widget.ProgressDialog");
 
-var localeStrings;
-var node_editor;
-var _crads;
-var CGI;
+var localeStrings, node_editor, _crads, CGI, tree;
 
 function _find_crad_by_name(name) {
     for (var i = 0; i < _crads.length; i++) {
@@ -233,6 +230,16 @@ function display_name_from_point(point) {
     }
 }
 
+function delete_selected_from_tree() {
+    /* relies on the fact that we only have one tree that would have
+     * registered a dnd controller. */
+    _tree_dnd_controllers[0].getSelectedItems().forEach(
+        function(item) {
+            tree.model.store.deleteItem(item);
+        }
+    );
+}
+
 /* dojoize_match_set_tree() takes an argument, "point", that is actually a
  * vmsp fieldmapper object with descendants fleshed hierarchically. It turns
  * that into a syntactically flat array but preserving the hierarchy
@@ -278,17 +285,28 @@ function dojoize_match_set_tree(point, refgen) {
     return results;
 }
 
+function render_match_set_description(match_set) {
+    dojo.byId("vms-name").innerHTML = match_set.name();
+    dojo.byId("vms-owner").innerHTML =
+        aou.findOrgUnit(match_set.owner()).name();
+    dojo.byId("vms-mtype").innerHTML = match_set.mtype();
+}
+
 function init_test() {
     progress_dialog.show(true);
 
     dojo.requireLocalization("openils.vandelay", "match_set");
     localeStrings = dojo.i18n.getLocalization("openils.vandelay", "match_set");
 
+    pcrud = new openils.PermaCrud();
     CGI = new openils.CGI();
 
+    var match_set = pcrud.retrieve("vms", CGI.param("match_set"));
+    render_match_set_description(match_set);
+
     /* XXX No-one should have hundreds of these or anything, but theoretically
      * this could be problematic with a big enough list of crad objects. */
-    _crads = new openils.PermaCrud().retrieveAll(
+    _crads = pcrud.retrieveAll(
         "crad", {"order_by": {"crad": "label"}}
     );
 
@@ -322,15 +340,15 @@ function init_test() {
         store: store, "query": {"id": "root"}
     });
 
-    var src = new dojo.dnd.Source("src_here");
-    var tree = new dijit.Tree(
+    var src = new dojo.dnd.Source("src-here");
+    tree = new dijit.Tree(
         {
             "model": treeModel,
             "dndController": openils.vandelay.TreeDndSource,
             "dragThreshold": 8,
             "betweenThreshold": 5,
             "persist": false
-        }, "tree_here"
+        }, "tree-here"
     );
 
     node_editor = new NodeEditor(src, "node-editor-container");
diff --git a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2 b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
index 0cec41c..6796406 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
+++ b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
@@ -1,14 +1,37 @@
 [% WRAPPER 'default/base.tt2' %]
 [% ctx.page_title = 'Vandelay Match Set Editor' %]
 <style type="text/css">
-    h1 { margin: 0.5em 0; }
+    h1 { margin: 1ex 0; }
     .outer { clear: both; }
-    #vmsp-buttons button { padding: 0 1.5em; }
-    .node-editor { margin-bottom: 2em; }
+    #vmsp-buttons button { margin: 0 0.5em; }
+    #tree-here { margin-bottom: 1.5em; }
+    #vms-table { padding-bottom: 2ex; }
+    #vms-table th { text-align: right; }
+    #vms-table td { padding-left: 1em; }
+    #src-pane { float: left; width: 49%; }
+    #tree-pane { float: right; width: 50%; }
+    .node-editor { margin-bottom: 1.5ex; }
     .node-editor td { padding: 0.5ex; }
     li { background-color: #ddd; }
 </style>
 <h1>[% ctx.page_title %]</h1>
+<table id="vms-table">
+    <tbody>
+        <tr>
+            <th>Match set name:</th>
+            <td><strong id="vms-name"></strong></td>
+        </tr>
+        <tr>
+            <th>Owning Library:</th>
+            <td id="vms-owner"></td>
+        </tr>
+        <tr>
+            <th>Type:</th>
+            <td id="vms-mtype"></td>
+        </tr>
+    </tbody>
+</table>
+<hr />
 <!-- XXX TODO
     give some indication of which match set we're editing the tree for
     -->
@@ -38,22 +61,26 @@
     <div><!-- XXX TODO: consider a read-only display here of the query as built
         so far from the treet --></div>
     <div id="vmsp-buttons">
-        <button onclick="node_editor.add('svf');">New Single-Value-Field</button>
-        <button onclick="node_editor.add('tag');">New MARC Tag and Subfield</button>
-        <button onclick="node_editor.add('bool_op');">New Boolean Operator</button>
+        New
+        <button onclick="node_editor.add('svf');">Single-Value-Field</button>
+        <button onclick="node_editor.add('tag');">MARC Tag and Subfield</button>
+        <button onclick="node_editor.add('bool_op');">Boolean Operator</button>
     </div>
 </div>
 <div class="outer" style="margin-top: 2ex;">
-    <div style="float: left; width: 49%">
+    <div id="src-pane">
         <div>
             <form id="node-editor-container" onsubmit="return false;"></form>
         </div>
-        <ul id="src_here"></ul>
+        <ul id="src-here"></ul>
     </div>
 
-    <div style="float: right; width: 50%">
+    <div id="tree-pane">
         <div><big>Your Expression</big></div>
-        <div id="tree_here"></div>
+        <div id="tree-here"></div>
+        <button id="deleter" onclick="delete_selected_from_tree()">
+            Deleted Selected Node
+        </button>
     </div>
 </div>
 <div jsId="progress_dialog" dojoType="openils.widget.ProgressDialog"></div>

commit 752e696bba75275c8e6330ff8fe7444318b9e21f
Author: senator <lebbeous at esilibrary.com>
Date:   Wed Apr 13 18:04:10 2011 -0400

    Add an autogrid-based interface for CRUD ops on vms objects, with links to the tree editor

diff --git a/Open-ILS/web/js/dojo/openils/widget/AutoGrid.js b/Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
index ecbdf95..9e5be71 100644
--- a/Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
+++ b/Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
@@ -640,6 +640,13 @@ if(!dojo._hasResource['openils.widget.AutoGrid']) {
         */
 
         return autoWidget.getDisplayString();
-    }
+    };
+
+    openils.widget.AutoGrid.orgUnitGetter = function(rowIndex, item) {
+        if (!item) return "";
+        return fieldmapper.aou.findOrgUnit(
+            this.grid.store.getValue(item, this.field)
+        ).shortname();
+    };
 }
 
diff --git a/Open-ILS/web/templates/default/vandelay/match_set.tt2 b/Open-ILS/web/templates/default/vandelay/match_set.tt2
index fe5def0..0d59f54 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set.tt2
+++ b/Open-ILS/web/templates/default/vandelay/match_set.tt2
@@ -1,59 +1,94 @@
-[% WRAPPER 'default/base.tt2' %]
-[% ctx.page_title = 'Vandelay Match Set' %]
-<style type="text/css">
-    h1 { margin: 0.5em 0; }
-    .outer { clear: both; }
-    #vmsp-buttons button { padding: 0 1.5em; }
-    .node-editor { margin-bottom: 2em; }
-    .node-editor td { padding: 0.5ex; }
-    li { background-color: #ddd; }
-</style>
-<h1>[% ctx.page_title %]</h1>
-<table class="hidden">
-    <tr consistent-controls="1">
-        <td>
-            <label for="quality-input"
-                title="A relative number representing the impact of this expression on the quality of the overall record match"><!-- XXX tooltipize -->
-                Quality:
-            </label>
-        </td>
-        <td>
-            <input id="quality-input" type="text" value="1"
-                size="4" maxlength="3" fmfield="quality" />
-        </td>
-    </tr>
-    <tr consistent-controls="1">
-        <td>
-            <label for="negate-input">Negate?</label>
-        </td>
-        <td>
-            <input id="negate-input" type="checkbox" fmfield="negate" />
-        </td>
-    </tr>
-</table>
-<div class="outer">
-    <div><!-- XXX TODO: consider a read-only display here of the query as built
-        so far from the treet --></div>
-    <div id="vmsp-buttons">
-        <button onclick="node_editor.add('svf');">New Single-Value-Field</button>
-        <button onclick="node_editor.add('tag');">New MARC Tag and Subfield</button>
-        <button onclick="node_editor.add('bool_op');">New Boolean Operator</button>
-    </div>
-</div>
-<div class="outer" style="margin-top: 2ex;">
-    <div style="float: left; width: 49%">
+[% WRAPPER default/base.tt2 %]
+[% ctx.page_title = 'Vandelay Match Sets' %]
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+    <div dojoType="dijit.layout.ContentPane" layoutAlign="top" class="oils-header-panel">
+        <div>[% ctx.page_title %]</div>
         <div>
-            <form id="node-editor-container" onsubmit="return false;"></form>
+            <button dojoType="dijit.form.Button"
+                onClick="vms_grid.showCreateDialog()">New Match Set</button>
+            <button dojoType="dijit.form.Button"
+                onClick="vms_grid.deleteSelected()">Delete Selected</button>
         </div>
-        <ul id="src_here"></ul>
     </div>
-
-    <div style="float: right; width: 50%">
-        <div><big>Your Expression</big></div>
-        <div id="tree_here"></div>
+    <div>
+        Show sets owned at or below:
+        <select dojoType="openils.widget.OrgUnitFilteringSelect"
+            jsId="context_org_selector"></select>
     </div>
+    <table jsId="vms_grid"
+        dojoType="openils.widget.AutoGrid"
+        query="{id: '*'}"
+        defaultCellWidth="'16em'"
+        fmClass="vms"
+        fieldorder="['name', 'owner']"
+        suppressEditFields="['id']"
+        showPaginator="true"
+        editOnEnter="true">
+        <thead>
+            <tr>
+                <th field="name" get="field_plus_id" formatter="tree_editor_link"></th>
+                <th field="owner" get="openils.widget.AutoGrid.orgUnitGetter">
+                </th>
+            </tr>
+        </thead>
+    </table>
 </div>
-<div jsId="progress_dialog" dojoType="openils.widget.ProgressDialog"></div>
-<script type="text/javascript"
-    src="[% ctx.media_prefix %]/js/ui/default/vandelay/match_set.js"></script>
+<div class="hidden">
+    <select dojoType="dijit.form.FilteringSelect" jsId="mtype_selector">
+        [%# for the origin of these hard coded options, see the definition
+        of vandelay.match_set.mtype in 012.schema.vandelay.sql %]
+        <option value="biblio">biblio</option>
+        <option value="authority">authority</option>
+        <!-- XXX: nah <option value="mfhd">mfhd</option> -->
+    </select>
+</div>
+
+<script type="text/javascript">
+    dojo.require("dijit.form.FilteringSelect");
+    dojo.require("openils.widget.AutoGrid");
+    dojo.require("openils.widget.OrgUnitFilteringSelect");
+
+    var context_org;
+
+    function load_grid(search) {
+        if (!search) search = {"id": {"!=": null}};
+
+        vms_grid.loadAll({"order_by": {"vms": "name"}}, search);
+    }
+
+    function field_plus_id(rowIndex, item) {
+        if (!item) return null;
+        var datum = {};
+        datum[this.field] = this.grid.store.getValue(item, this.field);
+        datum.id = this.grid.store.getValue(item, "id");
+        return datum;
+    }
+
+    function tree_editor_link(datum) {
+        if (!datum) return "";
+        return '<a href="[% ctx.base_path %]/vandelay/match_set_tree?match_set=' +
+            datum.id + '">' + datum.name + '</a>';
+    }
+
+    openils.Util.addOnLoad(
+        function() {
+            new openils.User().buildPermOrgSelector(
+                "ADMIN_IMPORT_MATCH_SET", context_org_selector,
+                null, function() {
+                    context_org_selector.onChange = function() {
+                        context_org = this.attr("value");
+                        vms_grid.resetStore();
+                        load_grid({
+                            "owner": aou.descendantNodeList(context_org, true)
+                        });
+                    };
+                }
+            );
+
+            vms_grid.overrideEditWidgets.mtype = mtype_selector;
+            vms_grid.overrideEditWidgets.mtype.shove = {"create": "biblio"};
+            load_grid();
+        }
+    );
+</script>
 [% END %]
diff --git a/Open-ILS/web/templates/default/vandelay/match_set.tt2 b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
similarity index 93%
copy from Open-ILS/web/templates/default/vandelay/match_set.tt2
copy to Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
index fe5def0..0cec41c 100644
--- a/Open-ILS/web/templates/default/vandelay/match_set.tt2
+++ b/Open-ILS/web/templates/default/vandelay/match_set_tree.tt2
@@ -1,5 +1,5 @@
 [% WRAPPER 'default/base.tt2' %]
-[% ctx.page_title = 'Vandelay Match Set' %]
+[% ctx.page_title = 'Vandelay Match Set Editor' %]
 <style type="text/css">
     h1 { margin: 0.5em 0; }
     .outer { clear: both; }
@@ -9,6 +9,9 @@
     li { background-color: #ddd; }
 </style>
 <h1>[% ctx.page_title %]</h1>
+<!-- XXX TODO
+    give some indication of which match set we're editing the tree for
+    -->
 <table class="hidden">
     <tr consistent-controls="1">
         <td>

commit 55bd5e663a1a7d3e611d124fea3aee68f54daeba
Author: Mike Rylander <mrylander at gmail.com>
Date:   Tue Apr 12 13:41:19 2011 -0400

    Corrected merge from trunk

diff --git a/Open-ILS/src/sql/Pg/000.functions.general.sql b/Open-ILS/src/sql/Pg/000.functions.general.sql
index 52301a4..40ab727 100644
--- a/Open-ILS/src/sql/Pg/000.functions.general.sql
+++ b/Open-ILS/src/sql/Pg/000.functions.general.sql
@@ -15,6 +15,8 @@ $$ LANGUAGE plpgsql;
 
 SELECT evergreen.change_db_setting('search_path', ARRAY['evergreen','public','pg_catalog']);
 
+CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
+
 CREATE OR REPLACE FUNCTION evergreen.lowercase( TEXT ) RETURNS TEXT AS $$
     return lc(shift);
 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index a9506d1..7b4ba02 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -2,8 +2,6 @@ DROP SCHEMA IF EXISTS vandelay CASCADE;
 
 BEGIN;
 
-CREATE OR REPLACE FUNCTION array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
-
 CREATE SCHEMA vandelay;
 
 CREATE TABLE vandelay.match_set (
@@ -520,8 +518,8 @@ BEGIN
                 ELSIF test.required THEN
                     FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
                         IF tmp_rec NOT IN (SELECT * FROM UNNEST(potential_matches)) THEN
-                            matches := array_remove_item_by_value(matches, tmp_rec);
-                            potential_matches := array_remove_item_by_value(potential_matches, tmp_rec);
+                            matches := evergreen.array_remove_item_by_value(matches, tmp_rec);
+                            potential_matches := evergreen.array_remove_item_by_value(potential_matches, tmp_rec);
                         END IF;
                     END LOOP;
                 END IF;
@@ -542,8 +540,8 @@ BEGIN
             ELSIF test.required THEN
                 FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
                     IF tmp_rec NOT IN (SELECT * FROM UNNEST(potential_matches)) THEN
-                        matches := array_remove_item_by_value(matches, tmp_rec);
-                        potential_matches := array_remove_item_by_value(potential_matches, tmp_rec);
+                        matches := evergreen.array_remove_item_by_value(matches, tmp_rec);
+                        potential_matches := evergreen.array_remove_item_by_value(potential_matches, tmp_rec);
                     END IF;
                 END LOOP;
             END IF;

commit 6795405f13dead5dff39cf884fa727c01d6f4636
Author: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>
Date:   Fri Apr 8 17:48:57 2011 -0400

    Initial vmsp tree editor
    
    __ notes __
    
    now we can retrieve a tree from the server and use it as the basis of
    our dijit.Tree widget. Still work to be done. Can't save anything yet.
    
    Note to self: borrow dojo dnd's "copy" operation (as opposed to move) to
    mean replacing a node in the tree, rather than adding to the tree.
    
    Re the permissions I changed, actual users of Evergreen hate having as
    much granularity as there was before, and it just confuses people trying
    to figure out what perms to give to whom.
    
    Note to self 2: add ADMIN_IMPORT_MATCH_SET to ppl
    
    Usability
    
    1) the tree editor will only let bool_op nodes have children
    
    2) you can't put the unset "dummy" node from the leftside onto the tree
    
    incidentally, gave fm objects a toString method that identifies their
    classname hint, as an aid to debugging in general

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 4070fa3..c89957b 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -541,10 +541,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
-				<create permission="CREATE_IMPORT_MATCH_SET ADMIN_IMPORT_IMPORT_MATCH_SET" context_field="owner"/>
-				<retrieve permission="CREATE_IMPORT_MATCH_SET UPDATE_IMPORT_MATCH_SET DELETE_IMPORT_MATCH_SET" context_field="owner"/>
-				<update permission="UPDATE_IMPORT_MATCH_SET" context_field="owner"/>
-				<delete permission="DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+				<create permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
+				<retrieve permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
+				<update permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
+				<delete permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
 			</actions>
 		</permacrud>
 	</class>
@@ -558,25 +558,28 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Coded Field" name="svf" reporter:datatype="link"/>
 			<field reporter:label="Tag" name="tag" reporter:datatype="text"/>
 			<field reporter:label="Subfield" name="subfield" reporter:datatype="text"/>
+            <field reporter:label="Negate" name="negate"  reporter:datatype="bool"/>
 			<field reporter:label="Importance" name="quality" reporter:datatype="int"/>
+			<field reporter:label="Expression Tree Children" name="children" oils_persist:virtual="true" reporter:datatype="link"/>
 		</fields>
 		<links>
 			<link field="parent" reltype="has_a" key="id" map="" class="vmsp"/>
 			<link field="match_set" reltype="has_a" key="id" map="" class="vms"/>
 			<link field="svf" reltype="has_a" key="id" map="" class="crad"/>
+			<link field="children" reltype="has_many" key="parent" map="" class="vmsp"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
-				<create permission="CREATE_IMPORT_MATCH_SET ADMIN_IMPORT_IMPORT_MATCH_SET">
+				<create permission="ADMIN_IMPORT_MATCH_SET">
                     <context link="match_set" field="owner"/>
 				</create>
-				<retrieve permission="CREATE_IMPORT_MATCH_SET UPDATE_IMPORT_MATCH_SET DELETE_IMPORT_MATCH_SET">
+				<retrieve permission="ADMIN_IMPORT_MATCH_SET">
                     <context link="match_set" field="owner"/>
 				</retrieve>
-				<update permission="UPDATE_IMPORT_MATCH_SET">
+				<update permission="ADMIN_IMPORT_MATCH_SET">
                     <context link="match_set" field="owner"/>
 				</update>
-				<delete permission="DELETE_IMPORT_MATCH_SET">
+				<delete permission="ADMIN_IMPORT_MATCH_SET">
                     <context link="match_set" field="owner"/>
 				</delete>
 			</actions>
@@ -598,10 +601,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
-				<create permission="CREATE_IMPORT_MATCH_SET ADMIN_IMPORT_IMPORT_MATCH_SET" context_field="owner"/>
-				<retrieve permission="CREATE_IMPORT_MATCH_SET UPDATE_IMPORT_MATCH_SET DELETE_IMPORT_MATCH_SET" context_field="owner"/>
-				<update permission="UPDATE_IMPORT_MATCH_SET" context_field="owner"/>
-				<delete permission="DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+				<create permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
+				<retrieve permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
+				<update permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
+				<delete permission="ADMIN_IMPORT_MATCH_SET" context_field="owner"/>
 			</actions>
 		</permacrud>
 	</class>
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
index 5c48282..aa830ad 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
@@ -1107,5 +1107,34 @@ sub respond_with_status {
         success_count => $success_count, %args }) if $err or ($try_count % 5 == 0);
 }
 
+__PACKAGE__->register_method(  
+    api_name    => "open-ils.vandelay.match_set.get_tree",
+    method      => "match_set_get_tree",
+    api_level   => 1,
+    argc        => 1
+);
+
+sub match_set_get_tree {
+    my ($self, $conn, $authtoken, $match_set_id) = @_;
+
+    $match_set_id = int($match_set_id) or return;
+
+    my $e = new_editor("authtoken" => $authtoken);
+    $e->checkauth or return $e->die_event;
+
+    my $set = $e->retrieve_vandelay_match_set($match_set_id) or
+        return $e->die_event;
+
+    $e->allowed("ADMIN_IMPORT_MATCH_SET", $set->owner) or
+        return $e->die_event;
+
+    my $tree = $e->search_vandelay_match_set_point([
+        {"match_set" => $match_set_id, "parent" => undef},
+        {"flesh" => -1, "flesh_fields" => {"vmsp" => ["children"]}}
+    ]) or return $e->die_event;
+
+    return pop @$tree;
+}
+
 
 1;
diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index fd3f818..a9506d1 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -23,6 +23,7 @@ CREATE TABLE vandelay.match_set_point (
     svf         TEXT    REFERENCES config.record_attr_definition (name),
     tag         TEXT,
     subfield    TEXT,
+    negate      BOOL    DEFAULT FALSE,
     quality     INT     NOT NULL DEFAULT 1, -- higher is better
     CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
     CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
diff --git a/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js b/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js
index bb369c6..df8b73b 100644
--- a/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js
+++ b/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js
@@ -87,6 +87,14 @@ if(!dojo._hasResource["fieldmapper.Fieldmapper"]){
                 return true;
             }
             return;
+        },
+
+        toString : function() {
+            /* ever so slightly aid debugging */
+            if (this.classname)
+                return "[object fieldmapper." + this.classname + "]";
+            else
+                return Object.prototype.toString();
         }
 
     });
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
new file mode 100644
index 0000000..6438132
--- /dev/null
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
@@ -0,0 +1,29 @@
+dojo.provide("openils.vandelay.TreeDndSource");
+dojo.require("dijit._tree.dndSource");
+
+/* This class specifically serves the eg/vandelay/match_set interface
+ * for editing Vandelay Match Set trees.  It should probably  have a more
+ * specific name that reflects that.
+ */
+dojo.declare(
+    "openils.vandelay.TreeDndSource", dijit._tree.dndSource, {
+        "checkItemAcceptance": function(target, source, position) {
+            return (
+                source._ready && (
+                    position != "over" ||
+                    this.tree.model.mayHaveChildren(
+                        dijit.getEnclosingWidget(target).item
+                    )
+                )
+            );
+            /* code in match_set.js makes sure that source._ready gets set true
+             * only when we want the item to be draggable */
+        },
+        "itemCreator": function(nodes) {
+            var default_items = this.inherited(arguments);
+            for (var i = 0; i < default_items.length; i++)
+                default_items[i].match_point = nodes[i].match_point;
+            return default_items;
+        }
+    }
+);
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
new file mode 100644
index 0000000..61d2821
--- /dev/null
+++ b/Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
@@ -0,0 +1,55 @@
+dojo.provide("openils.vandelay.TreeStoreModel");
+dojo.require("dijit.tree.TreeStoreModel");
+dojo.require("openils.Util");
+
+/* This class specifically serves the eg/vandelay/match_set interface
+ * for editing Vandelay Match Set trees.  It should probably  have a more
+ * specific name that reflects that.
+ */
+
+function _simple_item(model, item) {
+    /* Instead of model.getLabel(), could do
+     * model.store.getValue(item, "blah") or something like that ... */
+    return {
+        "label": model.getLabel(item),
+        "match_point": String(model.store.getValue(item, "match_point")),
+        "children": {}
+    };
+}
+
+dojo.declare(
+    "openils.vandelay.TreeStoreModel", dijit.tree.TreeStoreModel, {
+        "getSimpleTree": function(item, oncomplete, result) {
+            var self = this;
+            if (!result) result = {};
+
+            var mykey = this.getIdentity(item);
+            result[mykey] = _simple_item(this, item);
+            var child_collector = result[mykey].children;
+
+            if (this.mayHaveChildren(item)) {
+                this.getChildren(
+                    item, function(children) {
+                        for (var i = 0; i < children.length; i++) {
+                            self.getSimpleTree(
+                                children[i], null, child_collector
+                            );
+                        }
+                        if (oncomplete) oncomplete(result);
+                    }
+                );
+            }
+        },
+        "mayHaveChildren": function(item) {
+            var match_point = this.store.getValue(item, "match_point");
+            if (match_point)
+                return openils.Util.isTrue(match_point.bool_op());
+            else
+                return true;
+        }
+//        "newItem": function(args, parent) {
+//            if (!this.mayHaveChildren(parent)) return;
+//            return this.inherited(arguments);
+//        }
+    }
+);
diff --git a/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
new file mode 100644
index 0000000..1b9f01b
--- /dev/null
+++ b/Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
@@ -0,0 +1,3 @@
+{
+    "DEFINE_MP": "Define this match point using the above fields, then drag me to the tree on the right."
+}
diff --git a/Open-ILS/web/js/ui/default/vandelay/match_set.js b/Open-ILS/web/js/ui/default/vandelay/match_set.js
new file mode 100644
index 0000000..7d6a74f
--- /dev/null
+++ b/Open-ILS/web/js/ui/default/vandelay/match_set.js
@@ -0,0 +1,359 @@
+dojo.require("dijit.Tree");
+dojo.require("dijit.form.Button");
+dojo.require("dojo.data.ItemFileWriteStore");
+//dojo.require("openils.vandelay.DndSource");
+dojo.require("dojo.dnd.Source");
+dojo.require("openils.vandelay.TreeDndSource");
+dojo.require("openils.vandelay.TreeStoreModel");
+dojo.require("openils.CGI");
+dojo.require("openils.User");
+dojo.require("openils.Util");
+dojo.require("openils.PermaCrud");
+dojo.require("openils.widget.ProgressDialog");
+
+var localeStrings;
+var node_editor;
+var _crads;
+var CGI;
+
+function _find_crad_by_name(name) {
+    for (var i = 0; i < _crads.length; i++) {
+        if (_crads[i].name() == name)
+            return _crads[i];
+    }
+    return null;
+}
+
+function NodeEditor() {
+    var self = this;
+
+    var _svf_select_template = null;
+    var _factories_by_type = {
+        "svf": function() {
+            if (!_svf_select_template) {
+                _svf_select_template = dojo.create(
+                    "select", {"fmfield": "svf"}
+                );
+                for (var i=0; i<_crads.length; i++) {
+                    dojo.create(
+                        "option", {
+                            "value": _crads[i].name(),
+                            "innerHTML": _crads[i].label()
+                        }, _svf_select_template
+                    );
+                }
+            }
+
+            var select = dojo.clone(_svf_select_template);
+            dojo.attr(select, "id", "svf-select");
+            var label = dojo.create(
+                "label", {
+                    "for": "svf-select", "innerHTML": "Single-Value-Field:"
+                }
+            );
+
+            var tr = dojo.create("tr");
+            dojo.place(label, dojo.create("td", null, tr));
+            dojo.place(select, dojo.create("td", null, tr));
+
+            return [tr];
+        },
+        "tag": function() {
+            var rows = [dojo.create("tr"), dojo.create("tr")];
+            dojo.create(
+                "label", {
+                    "for": "tag-input", "innerHTML": "Tag:"
+                }, dojo.create("td", null, rows[0])
+            );
+            dojo.create(
+                "input", {
+                    "id": "tag-input",
+                    "type": "text",
+                    "size": 4,
+                    "maxlength": 3,
+                    "fmfield": "tag"
+                }, dojo.create("td", null, rows[0])
+            );
+            dojo.create(
+                "label", {
+                    "for": "subfield-input", "innerHTML": "Subfield: \u2021"
+                }, dojo.create("td", null, rows[1])
+            );
+            dojo.create(
+                "input", {
+                    "id": "subfield-input",
+                    "type": "text",
+                    "size": 2,
+                    "maxlength": 1,
+                    "fmfield": "subfield"
+                }, dojo.create("td", null, rows[1])
+            );
+            return rows;
+        },
+        "bool_op": function() {
+            var tr = dojo.create("tr");
+            dojo.create(
+                "label",
+                {"for": "operator-select", "innerHTML": "Operator:"},
+                dojo.create("td", null, tr)
+            );
+            var select = dojo.create(
+                "select", {"fmfield": "bool_op", "id": "operator-select"},
+                dojo.create("td", null, tr)
+            );
+            dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
+            dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
+
+            return [tr];
+        }
+    };
+
+    function _simple_value_getter(control) {
+        if (typeof control.selectedIndex != "undefined")
+            return control.options[control.selectedIndex].value;
+        else if (dojo.attr(control, "type") == "checkbox")
+            return control.checked;
+        else
+            return control.value;
+    };
+
+    this._init = function(dnd_source, node_editor_container) {
+        this.dnd_source = dnd_source;
+        this.node_editor_container = dojo.byId(node_editor_container);
+    };
+
+    this.clear = function() {
+        this.dnd_source.selectAll().deleteSelectedNodes();
+        dojo.empty(this.node_editor_container);
+    };
+
+    this.update_draggable = function(draggable) {
+        var s = "";
+        draggable.match_point = new vmsp();
+        var had_op = false;
+        dojo.query("[fmfield]", this.node_editor_container).forEach(
+            function(control) {
+                var used_svf = null;
+                var field = dojo.attr(control, "fmfield");
+                var value = _simple_value_getter(control);
+                draggable.match_point[field](value);
+
+                if (field == "subfield")
+                    s += " \u2021";
+                if (field == "svf")
+                    used_svf = value;
+                if (field == "quality")
+                    return;
+                if (field == "bool_op")
+                    had_op = true;
+                if (field == "negate") {
+                    if (value) {
+                        if (had_op)
+                            s = "<strong>N</strong>" + s;
+                        else
+                            s = "<strong>NOT</strong> " + s;
+                    }
+                } else {
+                    s += value;
+                }
+
+                if (used_svf !== null) {
+                    var our_crad = _find_crad_by_name(used_svf);
+                    /* XXX i18n, use fmtted strings */
+                    s += " / " + our_crad.label() + "<br /><em>" +
+                        (our_crad.description() || "") + "</em><br />";
+                }
+            }
+        );
+        dojo.attr(draggable, "innerHTML", s);
+        this.dnd_source._ready = true;
+    };
+
+    this._add_consistent_controls = function(tgt) {
+        if (!this._consistent_controls) {
+            var trs = dojo.query("[consistent-controls]");
+            this._consistent_controls = [];
+            for (var i = 0; i < trs.length; i++)
+                this._consistent_controls[i] = dojo.clone(trs[i]);
+            dojo.empty(trs[0].parentNode);
+        }
+
+        this._consistent_controls.forEach(
+            function(node) { dojo.place(dojo.clone(node), tgt); }
+        );
+    };
+
+    this.add = function(type) {
+        this.clear();
+
+        /* a representation, not the editing widgets, but will also carry
+         * the fieldmapper object when dragged to the tree */
+        var draggable = dojo.create(
+            "li", {"innerHTML": localeStrings.DEFINE_MP}
+        );
+
+        /* these are the editing widgets */
+        var table = dojo.create("table", {"className": "node-editor"});
+
+        var nodes = _factories_by_type[type]();
+        for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
+
+        this._add_consistent_controls(table);
+
+        dojo.create(
+            "input", {
+                "type": "submit", "value": "Ok",
+                "onclick": function() { self.update_draggable(draggable); }
+            }, dojo.create(
+                "td", {"colspan": 2, "align": "center"},
+                dojo.create("tr", null, table)
+            )
+        );
+
+        dojo.place(table, this.node_editor_container, "only");
+        /* XXX around here attach other data structures to the node */
+        this.dnd_source.insertNodes(false, [draggable]);
+        this.dnd_source._ready = false;
+    };
+
+    this._init.apply(this, arguments);
+}
+
+/* XXX replace later with code that will suit this function's purpose
+ * as well as that of update_draggable. */
+function display_name_from_point(point) {
+    /* quick and dirty */
+    if (point.bool_op()) {
+        return (point.negate() == "t" ? "N" : "") + point.bool_op();
+    } else if (point.svf()) {
+        return (point.negate() == "t" ? "NOT " : "") + point.svf();
+    } else {
+        return (point.negate() == "t" ? "NOT " : "") + point.tag() +
+            "\u2021" + point.subfield();
+    }
+}
+
+/* dojoize_match_set_tree() takes an argument, "point", that is actually a
+ * vmsp fieldmapper object with descendants fleshed hierarchically. It turns
+ * that into a syntactically flat array but preserving the hierarchy
+ * semantically in the language used by dojo data stores, i.e.,
+ *
+ * [
+ *  {'id': 'root', children:[{'_reference': '0'}, {'_reference': '1'}]},
+ *  {'id': '0', children:[]},
+ *  {'id': '1', children:[]}
+ * ],
+ *
+ */
+function dojoize_match_set_tree(point, refgen) {
+    /* XXX TODO test with deeper trees! */
+    var root = false;
+    if (!refgen) {
+        refgen = 0;
+        root = true;
+    }
+
+    var bathwater = point.children();
+    point.children([]);
+    var item = {
+        "id": (root ? "root" : refgen),
+        "name": display_name_from_point(point),
+        "match_point": point.clone(),
+        "children": []
+    };
+    point.children(bathwater);
+
+    var results = [item];
+
+    if (point.children()) {
+        for (var i = 0; i < point.children().length; i++) {
+            var child = point.children()[i];
+            item.children.push({"_reference": ++refgen});
+            results = results.concat(
+                dojoize_match_set_tree(child, refgen)
+            );
+        }
+    }
+
+    return results;
+}
+
+function init_test() {
+    progress_dialog.show(true);
+
+    dojo.requireLocalization("openils.vandelay", "match_set");
+    localeStrings = dojo.i18n.getLocalization("openils.vandelay", "match_set");
+
+    CGI = new openils.CGI();
+
+    /* XXX No-one should have hundreds of these or anything, but theoretically
+     * this could be problematic with a big enough list of crad objects. */
+    _crads = new openils.PermaCrud().retrieveAll(
+        "crad", {"order_by": {"crad": "label"}}
+    );
+
+    var match_set_tree = fieldmapper.standardRequest(
+        ["open-ils.vandelay", "open-ils.vandelay.match_set.get_tree"],
+        [openils.User.authtoken, CGI.param("match_set")]
+    );
+
+//        {
+//            "identifier": "id", "label": "name", "items": [
+//                {
+//                    "id": "root", "name": "AND",
+//                    "children": [
+//                        {"_reference": "leaf0"}, {"_reference": "leaf1"}
+//                    ]
+//                },
+//                {"id": "leaf0", "name": "nonsense test"},
+//                {"id": "leaf1", "name": "more nonsense"}
+//            ]
+//        }
+
+    var store = new dojo.data.ItemFileWriteStore({
+        "data": {
+            "identifier": "id",
+            "label": "name",
+            "items": dojoize_match_set_tree(match_set_tree)
+        }
+    });
+
+    var treeModel = new openils.vandelay.TreeStoreModel({
+        store: store, "query": {"id": "root"}
+    });
+
+    var src = new dojo.dnd.Source("src_here");
+    var tree = new dijit.Tree(
+        {
+            "model": treeModel,
+            "dndController": openils.vandelay.TreeDndSource,
+            "dragThreshold": 8,
+            "betweenThreshold": 5,
+            "persist": false
+        }, "tree_here"
+    );
+
+    node_editor = new NodeEditor(src, "node-editor-container");
+
+    dojo.connect(
+        src, "onDndDrop", null,
+        function(source, nodes, copy, target) {
+            if (source == this) {
+                var model = target.tree.model;
+                model.getRoot(
+                    function(root) {
+                        model.getSimpleTree(
+                            root, function(results) { alert(js2JSON(results)); }
+                        );
+                    }
+                );
+                node_editor.clear();  /* because otherwise this acts like a copy! */
+            } else {
+                alert("XXX [src] nodes length is " + nodes.length); /* XXX DEBUG */
+            }
+        }
+    );
+    progress_dialog.hide();
+}
+
+openils.Util.addOnLoad(init_test);
diff --git a/Open-ILS/web/templates/default/vandelay/match_set.tt2 b/Open-ILS/web/templates/default/vandelay/match_set.tt2
new file mode 100644
index 0000000..fe5def0
--- /dev/null
+++ b/Open-ILS/web/templates/default/vandelay/match_set.tt2
@@ -0,0 +1,59 @@
+[% WRAPPER 'default/base.tt2' %]
+[% ctx.page_title = 'Vandelay Match Set' %]
+<style type="text/css">
+    h1 { margin: 0.5em 0; }
+    .outer { clear: both; }
+    #vmsp-buttons button { padding: 0 1.5em; }
+    .node-editor { margin-bottom: 2em; }
+    .node-editor td { padding: 0.5ex; }
+    li { background-color: #ddd; }
+</style>
+<h1>[% ctx.page_title %]</h1>
+<table class="hidden">
+    <tr consistent-controls="1">
+        <td>
+            <label for="quality-input"
+                title="A relative number representing the impact of this expression on the quality of the overall record match"><!-- XXX tooltipize -->
+                Quality:
+            </label>
+        </td>
+        <td>
+            <input id="quality-input" type="text" value="1"
+                size="4" maxlength="3" fmfield="quality" />
+        </td>
+    </tr>
+    <tr consistent-controls="1">
+        <td>
+            <label for="negate-input">Negate?</label>
+        </td>
+        <td>
+            <input id="negate-input" type="checkbox" fmfield="negate" />
+        </td>
+    </tr>
+</table>
+<div class="outer">
+    <div><!-- XXX TODO: consider a read-only display here of the query as built
+        so far from the treet --></div>
+    <div id="vmsp-buttons">
+        <button onclick="node_editor.add('svf');">New Single-Value-Field</button>
+        <button onclick="node_editor.add('tag');">New MARC Tag and Subfield</button>
+        <button onclick="node_editor.add('bool_op');">New Boolean Operator</button>
+    </div>
+</div>
+<div class="outer" style="margin-top: 2ex;">
+    <div style="float: left; width: 49%">
+        <div>
+            <form id="node-editor-container" onsubmit="return false;"></form>
+        </div>
+        <ul id="src_here"></ul>
+    </div>
+
+    <div style="float: right; width: 50%">
+        <div><big>Your Expression</big></div>
+        <div id="tree_here"></div>
+    </div>
+</div>
+<div jsId="progress_dialog" dojoType="openils.widget.ProgressDialog"></div>
+<script type="text/javascript"
+    src="[% ctx.media_prefix %]/js/ui/default/vandelay/match_set.js"></script>
+[% END %]

commit b1854026fc611c259a66b5c537af6018be981d15
Author: Mike Rylander <mrylander at gmail.com>
Date:   Thu Mar 24 15:37:00 2011 -0400

    Structure match set points as a tree

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 4e5362b..4070fa3 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -552,22 +552,33 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 	<class id="vmsp" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="vandelay::match_set_point" oils_persist:tablename="vandelay.match_set_point" reporter:label="Record Matching Definition">
 		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.match_set_point_id_seq">
 			<field reporter:label="Match Definition ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Expression Tree Parent" name="parent" reporter:datatype="link"/>
 			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
-			<field reporter:label="Coded Field" name="svf" reporter:datatype="text"/>
+			<field reporter:label="Boolean Operator" name="bool_op" reporter:datatype="text"/>
+			<field reporter:label="Coded Field" name="svf" reporter:datatype="link"/>
 			<field reporter:label="Tag" name="tag" reporter:datatype="text"/>
 			<field reporter:label="Subfield" name="subfield" reporter:datatype="text"/>
-			<field reporter:label="Required?" name="required" reporter:datatype="bool"/>
 			<field reporter:label="Importance" name="quality" reporter:datatype="int"/>
 		</fields>
 		<links>
+			<link field="parent" reltype="has_a" key="id" map="" class="vmsp"/>
 			<link field="match_set" reltype="has_a" key="id" map="" class="vms"/>
+			<link field="svf" reltype="has_a" key="id" map="" class="crad"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
-				<create permission="CREATE_IMPORT_MATCH_SET ADMIN_IMPORT_IMPORT_MATCH_SET" context_field="owner"/>
-				<retrieve permission="CREATE_IMPORT_MATCH_SET UPDATE_IMPORT_MATCH_SET DELETE_IMPORT_MATCH_SET" context_field="owner"/>
-				<update permission="UPDATE_IMPORT_MATCH_SET" context_field="owner"/>
-				<delete permission="DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+				<create permission="CREATE_IMPORT_MATCH_SET ADMIN_IMPORT_IMPORT_MATCH_SET">
+                    <context link="match_set" field="owner"/>
+				</create>
+				<retrieve permission="CREATE_IMPORT_MATCH_SET UPDATE_IMPORT_MATCH_SET DELETE_IMPORT_MATCH_SET">
+                    <context link="match_set" field="owner"/>
+				</retrieve>
+				<update permission="UPDATE_IMPORT_MATCH_SET">
+                    <context link="match_set" field="owner"/>
+				</update>
+				<delete permission="DELETE_IMPORT_MATCH_SET">
+                    <context link="match_set" field="owner"/>
+				</delete>
 			</actions>
 		</permacrud>
 	</class>
diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 5b6c0f5..fd3f818 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -17,16 +17,20 @@ CREATE TABLE vandelay.match_set (
 -- Table to define match points, either FF via SVF or tag+subfield
 CREATE TABLE vandelay.match_set_point (
     id          SERIAL  PRIMARY KEY,
-    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id),
-    svf         TEXT    REFERENCES config.record_attr_definition,
+    match_set   INT     REFERENCES vandelay.match_set (id),
+    parent      INT     REFERENCES vandelay.match_set_point (id),
+    bool_op     TEXT    CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
+    svf         TEXT    REFERENCES config.record_attr_definition (name),
     tag         TEXT,
     subfield    TEXT,
-    required    BOOL    NOT NULL DEFAULT TRUE,
     quality     INT     NOT NULL DEFAULT 1, -- higher is better
     CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
-    CONSTRAINT vmsp_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
+    CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
+        (tag IS NOT NULL AND svf IS NULL AND bool_op IS NULL) OR
+        (tag IS NULL AND svf IS NOT NULL AND bool_op IS NULL) OR
+        (tag IS NULL AND svf IS NULL AND bool_op IS NOT NULL)
+    )
 );
-CREATE UNIQUE INDEX vmsp_def_once_per_set ON vandelay.match_set_point (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''));
 
 CREATE TABLE vandelay.match_set_quality (
     id          SERIAL  PRIMARY KEY,
@@ -500,6 +504,10 @@ BEGIN
 
     first_round := TRUE;
     -- whew ... here we go ...
+
+
+    -- Commented out until replaced by tree-ish version
+/*
     FOR test IN SELECT * FROM vandelay.match_set_point WHERE match_set = my_bib_queue.match_set ORDER BY required DESC LOOP
         IF test.tag IS NOT NULL THEN
             FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
@@ -551,6 +559,7 @@ BEGIN
     FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
         INSERT INTO vandelay.bib_match (matched_set, queued_record, eg_record, quality) VALUES (my_bib_queue.match_set, NEW.id, tmp_rec, (quality_set -> tmp_rec::TEXT));
     END LOOP;
+*/
 
     RETURN NEW;
 END;

commit 13041c7e9aabd1f8067ad51efaa2b92b74c2f319
Author: Mike Rylander <mrylander at gmail.com>
Date:   Mon Mar 14 14:47:55 2011 -0400

    Protect bib matching from 901$c which has no corresponding incumbent record; also, boost the quality of an exact match

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 84ca9bd..5b6c0f5 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -489,8 +489,11 @@ BEGIN
     incoming_existing_id := oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]',NEW.marc);
 
     IF incoming_existing_id IS NOT NULL THEN
-        INSERT INTO vandelay.bib_match (field_type, queued_record, eg_record) VALUES ('id', NEW.id, exact_id);
-        RETURN NEW;
+        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = exact_id;
+        IF tmp_rec IS NOT NULL THEN
+            INSERT INTO vandelay.bib_match (queued_record, eg_record, quality) VALUES ( NEW.id, exact_id, 9999);
+            RETURN NEW;
+        END IF;
     END IF;
 
     SELECT * INTO my_bib_queue FROM vandelay.bib_queue WHERE id = NEW.queue;
diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index a010d07..25e9c1d 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -8807,6 +8807,7 @@ INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );

commit e0114f18272c347795c21ac59e9c1e4520e2de65
Author: Mike Rylander <mrylander at gmail.com>
Date:   Mon Mar 14 12:08:58 2011 -0400

    SQL schema cleanup

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index bdfff7a..84ca9bd 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -24,9 +24,9 @@ CREATE TABLE vandelay.match_set_point (
     required    BOOL    NOT NULL DEFAULT TRUE,
     quality     INT     NOT NULL DEFAULT 1, -- higher is better
     CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
-    CONSTRAINT vmsp_need_a_tag_or_a_ff CHECK (tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL)),
-    CONSTRAINT vmsp_def_once_per_set UNIQUE (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''))
+    CONSTRAINT vmsp_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
 );
+CREATE UNIQUE INDEX vmsp_def_once_per_set ON vandelay.match_set_point (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''));
 
 CREATE TABLE vandelay.match_set_quality (
     id          SERIAL  PRIMARY KEY,
@@ -37,9 +37,9 @@ CREATE TABLE vandelay.match_set_quality (
     value       TEXT    NOT NULL,
     quality     INT     NOT NULL DEFAULT 1, -- higher is better
     CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
-    CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK (tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL)),
-    CONSTRAINT vmsq_def_once_per_set UNIQUE (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''))
+    CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
 );
+CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''));
 
 
 CREATE TABLE vandelay.queue (
@@ -48,7 +48,7 @@ CREATE TABLE vandelay.queue (
 	name			TEXT		NOT NULL,
 	complete		BOOL		NOT NULL DEFAULT FALSE,
 	queue_type		TEXT		NOT NULL DEFAULT 'bib' CHECK (queue_type IN ('bib','authority')),
-    match_set       INT         REFERENCES vandelay.match_set (id) DEFERRABLE INITIALLY DEFERRED ON UPDATE CASCADE ON DELETE SET NULL,
+    match_set       INT         REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
 	CONSTRAINT vand_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
 );
 
@@ -122,7 +122,7 @@ CREATE TABLE vandelay.queued_bib_record (
 	queue		    INT		NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	bib_source	    INT		REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
 	imported_as 	BIGINT	REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
-	import_error	INT     REFERENCES vandelay.import_error (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	import_error	TEXT    REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	error_detail	TEXT
 ) INHERITS (vandelay.queued_record);
 ALTER TABLE vandelay.queued_bib_record ADD PRIMARY KEY (id);
@@ -148,7 +148,7 @@ CREATE TABLE vandelay.import_item (
     id              BIGSERIAL   PRIMARY KEY,
     record          BIGINT      NOT NULL REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	import_error	INT         REFERENCES vandelay.import_error (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	import_error	TEXT        REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	error_detail	TEXT,
     owning_lib      INT,
     circ_lib        INT,
@@ -311,7 +311,7 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
-CREAT TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
+CREATE TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
 CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
 
 use MARC::Record;
@@ -515,9 +515,9 @@ BEGIN
                 END IF;
 
                 -- add the quality for this match
-                FOR tmp_rec IN SELECT * FROM UNNEST(potential_matches);
+                FOR tmp_rec IN SELECT * FROM UNNEST(potential_matches) LOOP
                     tmp_quality := COALESCE((quality_set -> tmp_rec::TEXT)::INT, 0);
-                    quality := quality || hstore(tmp_rec::TEXT, (tmp_quality + test.quality)::TEXT);
+                    quality_set := quality_set || hstore(tmp_rec::TEXT, (tmp_quality + test.quality)::TEXT);
                 END LOOP;
 
             END LOOP;
@@ -537,9 +537,9 @@ BEGIN
             END IF;
 
             -- add the quality for this match
-            FOR tmp_rec IN SELECT * FROM UNNEST(potential_matches);
+            FOR tmp_rec IN SELECT * FROM UNNEST(potential_matches) LOOP
                 tmp_quality := COALESCE((quality_set -> tmp_rec::TEXT)::INT, 0);
-                quality := quality || hstore(tmp_rec::TEXT, (tmp_quality + test.quality)::TEXT);
+                quality_set := quality_set || hstore(tmp_rec::TEXT, (tmp_quality + test.quality)::TEXT);
             END LOOP;
 
         END IF;
@@ -1657,7 +1657,7 @@ ALTER TABLE vandelay.authority_queue ADD PRIMARY KEY (id);
 CREATE TABLE vandelay.queued_authority_record (
 	queue		INT	NOT NULL REFERENCES vandelay.authority_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	imported_as	INT	REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
-	import_error	INT     REFERENCES vandelay.import_error (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	import_error	TEXT    REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	error_detail	TEXT
 ) INHERITS (vandelay.queued_record);
 ALTER TABLE vandelay.queued_authority_record ADD PRIMARY KEY (id);
diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index fbe9877..a010d07 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -3186,13 +3186,13 @@ INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES
 INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (2, 'author', oils_i18n_gettext(2, 'Author of work', 'vqbrad', 'description'),'//*[@tag="100" or @tag="110" or @tag="113"]/*[contains("ad", at code)]');
 INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (3, 'language', oils_i18n_gettext(3, 'Language of work', 'vqbrad', 'description'),'//*[@tag="240"]/*[@code="l"][1]');
 INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (4, 'pagination', oils_i18n_gettext(4, 'Pagination', 'vqbrad', 'description'),'//*[@tag="300"]/*[@code="a"][1]');
-INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident, remove ) VALUES (5, 'isbn',oils_i18n_gettext(5, 'ISBN', 'vqbrad', 'description'),'//*[@tag="020"]/*[@code="a"]', TRUE, $r$(?:-|\s.+$)$r$);
-INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident, remove ) VALUES (6, 'issn',oils_i18n_gettext(6, 'ISSN', 'vqbrad', 'description'),'//*[@tag="022"]/*[@code="a"]', TRUE, $r$(?:-|\s.+$)$r$);
+INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, remove ) VALUES (5, 'isbn',oils_i18n_gettext(5, 'ISBN', 'vqbrad', 'description'),'//*[@tag="020"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
+INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, remove ) VALUES (6, 'issn',oils_i18n_gettext(6, 'ISSN', 'vqbrad', 'description'),'//*[@tag="022"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
 INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (7, 'price',oils_i18n_gettext(7, 'Price', 'vqbrad', 'description'),'//*[@tag="020" or @tag="022"]/*[@code="c"][1]');
-INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident ) VALUES (8, 'rec_identifier',oils_i18n_gettext(8, 'Accession Number', 'vqbrad', 'description'),'//*[@tag="001"]', TRUE);
-INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident ) VALUES (9, 'eg_tcn',oils_i18n_gettext(9, 'TCN Value', 'vqbrad', 'description'),'//*[@tag="901"]/*[@code="a"]', TRUE);
-INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident ) VALUES (10, 'eg_tcn_source',oils_i18n_gettext(10, 'TCN Source', 'vqbrad', 'description'),'//*[@tag="901"]/*[@code="b"]', TRUE);
-INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident ) VALUES (11, 'eg_identifier',oils_i18n_gettext(11, 'Internal ID', 'vqbrad', 'description'),'//*[@tag="901"]/*[@code="c"]', TRUE);
+INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath) VALUES (8, 'rec_identifier',oils_i18n_gettext(8, 'Accession Number', 'vqbrad', 'description'),'//*[@tag="001"]');
+INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath) VALUES (9, 'eg_tcn',oils_i18n_gettext(9, 'TCN Value', 'vqbrad', 'description'),'//*[@tag="901"]/*[@code="a"]');
+INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath) VALUES (10, 'eg_tcn_source',oils_i18n_gettext(10, 'TCN Source', 'vqbrad', 'description'),'//*[@tag="901"]/*[@code="b"]');
+INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath) VALUES (11, 'eg_identifier',oils_i18n_gettext(11, 'Internal ID', 'vqbrad', 'description'),'//*[@tag="901"]/*[@code="c"]');
 INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (12, 'publisher',oils_i18n_gettext(12, 'Publisher', 'vqbrad', 'description'),'//*[@tag="260"]/*[@code="b"][1]');
 INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, remove ) VALUES (13, 'pubdate',oils_i18n_gettext(13, 'Publication Date', 'vqbrad', 'description'),'//*[@tag="260"]/*[@code="c"][1]',$r$\D$r$);
 INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (14, 'edition',oils_i18n_gettext(14, 'Edition', 'vqbrad', 'description'),'//*[@tag="250"]/*[@code="a"][1]');
@@ -3246,7 +3246,7 @@ INSERT INTO vandelay.import_item_attr_definition (
     'k'
 );
 
-INSERT INTO vandelay.authority_attr_definition (id, code, description, xpath, ident ) VALUES (1, 'rec_identifier',oils_i18n_gettext(1, 'Identifier', 'vqarad', 'description'),'//*[@tag="001"]', TRUE);
+INSERT INTO vandelay.authority_attr_definition (id, code, description, xpath) VALUES (1, 'rec_identifier',oils_i18n_gettext(1, 'Identifier', 'vqarad', 'description'),'//*[@tag="001"]');
 SELECT SETVAL('vandelay.authority_attr_definition_id_seq'::TEXT, 100);
 
 

commit efba3908ce39499b007161f1c25b0d5de42e10fa
Author: Mike Rylander <mrylander at gmail.com>
Date:   Mon Mar 14 11:43:24 2011 -0400

    Add table and columns for tracking current import/overlay errors per object

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 251df7a..4e5362b 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -186,6 +186,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Import Item ID" name="id" reporter:datatype="id"/>
 			<field reporter:label="Import Record" name="record" reporter:datatype="link"/>
 			<field reporter:label="Attribute Definition" name="definition" reporter:datatype="link"/>
+			<field reporter:label="Import Error" name="import_error" reporter:datatype="link"/>
+			<field reporter:label="Import Error Detail" name="error_detail" reporter:datatype="text"/>
 			<field reporter:label="Owning Library" name="owning_lib" reporter:datatype="int"/>
 			<field reporter:label="Circulating Library" name="circ_lib" reporter:datatype="int"/>
 			<field reporter:label="Call Number" name="call_number" reporter:datatype="text"/>
@@ -207,6 +209,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="OPAC Visible" name="opac_visible" reporter:datatype="bool"/>
 		</fields>
 		<links>
+			<link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
 			<link field="record" reltype="has_a" key="id" map="" class="vqbr"/>
 			<link field="definition" reltype="has_a" key="id" map="" class="viiad"/>
 		</links>
@@ -294,6 +297,23 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		</permacrud>
 	</class>
 
+	<class id="vie" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="vandelay::import_error" oils_persist:tablename="vandelay.import_error" reporter:label="Import/Overlay Error Definitions">
+		<fields oils_persist:primary="code">
+			<field reporter:label="Error Code" name="code" reporter:selector="description" reporter:datatype="id"/>
+			<field reporter:label="Description" name="description" reporter:datatype="text" oils_persist:i18n="true"/>
+		</fields>
+		<links>
+			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="item_attr_def" reltype="has_a" key="id" map="" class="viiad"/>
+			<link field="match_set" reltype="has_a" key="id" map="" class="vms"/>
+		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<retrieve/>
+			</actions>
+		</permacrud>
+	</class>
+
 	<class id="vqbr" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="vandelay::queued_bib_record" oils_persist:tablename="vandelay.queued_bib_record" reporter:label="Queued Bib Record">
 		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.queued_record_id_seq">
 			<field reporter:label="Record ID" name="id" reporter:datatype="id"/>
@@ -303,11 +323,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Queue" name="queue" reporter:datatype="link"/>
 			<field reporter:label="Bib Source" name="bib_source" reporter:datatype="link"/>
 			<field reporter:label="Final Target Record" name="imported_as" reporter:datatype="link"/>
+			<field reporter:label="Import Error" name="import_error" reporter:datatype="link"/>
+			<field reporter:label="Import Error Detail" name="error_detail" reporter:datatype="text"/>
 			<field reporter:label="Purpose" name="purpose" reporter:datatype="text"/>
 			<field reporter:label="Attributes" name="attributes" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Matches" name="matches" oils_persist:virtual="true" reporter:datatype="link"/>
 		</fields>
 		<links>
+			<link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
 			<link field="queue" reltype="has_a" key="id" map="" class="vbq"/>
 			<link field="bib_source" reltype="has_a" key="id" map="" class="cbs"/>
 			<link field="imported_as" reltype="has_a" key="id" map="" class="bre"/>
@@ -419,11 +442,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="MARC" name="marc" reporter:datatype="text"/>
 			<field reporter:label="Queue" name="queue" reporter:datatype="link"/>
 			<field reporter:label="Final Target Record" name="imported_as" reporter:datatype="link"/>
+			<field reporter:label="Import Error" name="import_error" reporter:datatype="link"/>
+			<field reporter:label="Import Error Detail" name="error_detail" reporter:datatype="text"/>
 			<field reporter:label="Purpose" name="purpose" reporter:datatype="text"/>
 			<field reporter:label="Attributes" name="attributes" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Matches" name="matches" oils_persist:virtual="true" reporter:datatype="link"/>
 		</fields>
 		<links>
+			<link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
 			<link field="queue" reltype="has_a" key="id" map="" class="vaq"/>
 			<link field="imported_as" reltype="has_a" key="id" map="" class="are"/>
             <link field="attributes" reltype="has_many" key="record" map="" class="vqara"/>
diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index c4bc1f8..bdfff7a 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -106,6 +106,11 @@ CREATE TABLE vandelay.import_item_attr_definition (
 	CONSTRAINT vand_import_item_attr_def_idx UNIQUE (owner,name)
 );
 
+CREATE TABLE vandelay.import_error (
+    code        TEXT    PRIMARY KEY,
+    description TEXT    NOT NULL -- i18n
+);
+
 CREATE TABLE vandelay.bib_queue (
 	queue_type	    TEXT	NOT NULL DEFAULT 'bib' CHECK (queue_type = 'bib'),
 	item_attr_def	BIGINT REFERENCES vandelay.import_item_attr_definition (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
@@ -114,9 +119,11 @@ CREATE TABLE vandelay.bib_queue (
 ALTER TABLE vandelay.bib_queue ADD PRIMARY KEY (id);
 
 CREATE TABLE vandelay.queued_bib_record (
-	queue		INT		NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	bib_source	INT		REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
-	imported_as	BIGINT	REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
+	queue		    INT		NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	bib_source	    INT		REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
+	imported_as 	BIGINT	REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
+	import_error	INT     REFERENCES vandelay.import_error (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	error_detail	TEXT
 ) INHERITS (vandelay.queued_record);
 ALTER TABLE vandelay.queued_bib_record ADD PRIMARY KEY (id);
 CREATE INDEX queued_bib_record_queue_idx ON vandelay.queued_bib_record (queue);
@@ -137,11 +144,12 @@ CREATE TABLE vandelay.bib_match (
     quality         INT         NOT NULL DEFAULT 0
 );
 
--- DROP TABLE vandelay.import_item CASCADE;
 CREATE TABLE vandelay.import_item (
     id              BIGSERIAL   PRIMARY KEY,
     record          BIGINT      NOT NULL REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	import_error	INT         REFERENCES vandelay.import_error (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	error_detail	TEXT,
     owning_lib      INT,
     circ_lib        INT,
     call_number     TEXT,
@@ -1648,7 +1656,9 @@ ALTER TABLE vandelay.authority_queue ADD PRIMARY KEY (id);
 
 CREATE TABLE vandelay.queued_authority_record (
 	queue		INT	NOT NULL REFERENCES vandelay.authority_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	imported_as	INT	REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
+	imported_as	INT	REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
+	import_error	INT     REFERENCES vandelay.import_error (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	error_detail	TEXT
 ) INHERITS (vandelay.queued_record);
 ALTER TABLE vandelay.queued_authority_record ADD PRIMARY KEY (id);
 CREATE INDEX queued_authority_record_queue_idx ON vandelay.queued_authority_record (queue);
diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index 78ed74c..fbe9877 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -8804,3 +8804,10 @@ INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
     ),
     'bool'
 );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
+

commit 934603b401b6db47f65e3d706283f291cef8d96f
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Mar 11 16:05:07 2011 -0500

    Mostly, new function(s) for bib matching based on SVF and tag+subfield; also, goodly amounts of moving things around

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index c268e8f..251df7a 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -276,11 +276,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
 			<field reporter:label="Type" name="queue_type" reporter:datatype="text"/>
 			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
+			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
 			<field reporter:label="Item Import Attribute Definition" name="item_attr_def" reporter:datatype="link"/>
 		</fields>
 		<links>
 			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
 			<link field="item_attr_def" reltype="has_a" key="id" map="" class="viiad"/>
+			<link field="match_set" reltype="has_a" key="id" map="" class="vms"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
@@ -329,7 +331,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Description" name="description" reporter:datatype="text" oils_persist:i18n="true"/>
 			<field reporter:label="XPath" name="xpath" reporter:datatype="text"/>
 			<field reporter:label="Remove RegExp" name="remove" reporter:datatype="text"/>
-			<field reporter:label="Is Identifier?" name="ident" reporter:datatype="bool"/>
 		</fields>
 		<links/>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
@@ -367,15 +368,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.bib_match_id_seq">
 			<field reporter:label="Match ID" name="id" reporter:datatype="id"/>
 			<field reporter:label="Queued Record" name="queued_record" reporter:datatype="link"/>
-			<field reporter:label="Matched Attribute" name="matched_attr" reporter:datatype="link"/>
+			<field reporter:label="Matched Set" name="matched_set" reporter:datatype="link"/>
 			<field reporter:label="Evergreen Record" name="eg_record" reporter:datatype="link"/>
-			<field reporter:label="Field Type" name="field_type" reporter:datatype="text"/>
 			<field reporter:label="Quality" name="quality" reporter:datatype="text"/>
 		</fields>
 		<links>
 			<link field="queued_record" reltype="has_a" key="id" map="" class="vqbr"/>
 			<link field="eg_record" reltype="has_a" key="id" map="" class="bre"/>
-			<link field="matched_attr" reltype="has_a" key="id" map="" class="vqbra"/>
+			<link field="matched_set" reltype="has_a" key="id" map="" class="vms"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
@@ -395,9 +395,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
 			<field reporter:label="Type" name="queue_type" reporter:datatype="text"/>
 			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
+			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
 		</fields>
 		<links>
 			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
+			<link field="match_set" reltype="has_a" key="id" map="" class="vms"/>
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
@@ -525,7 +527,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.match_set_point_id_seq">
 			<field reporter:label="Match Definition ID" name="id" reporter:datatype="id"/>
 			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
-			<field reporter:label="Fixed Field" name="fixed_field" reporter:datatype="text"/>
+			<field reporter:label="Coded Field" name="svf" reporter:datatype="text"/>
 			<field reporter:label="Tag" name="tag" reporter:datatype="text"/>
 			<field reporter:label="Subfield" name="subfield" reporter:datatype="text"/>
 			<field reporter:label="Required?" name="required" reporter:datatype="bool"/>
@@ -548,7 +550,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.match_set_quality_id_seq">
 			<field reporter:label="Quality Metric ID" name="id" reporter:datatype="id"/>
 			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
-			<field reporter:label="Fixed Field" name="fixed_field" reporter:datatype="text"/>
+			<field reporter:label="Coded Field" name="svf" reporter:datatype="text"/>
 			<field reporter:label="Tag" name="tag" reporter:datatype="text"/>
 			<field reporter:label="Subfield" name="subfield" reporter:datatype="text"/>
 			<field reporter:label="Value" name="value" reporter:datatype="text"/>
diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 9b3e3cf..c4bc1f8 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -2,14 +2,53 @@ DROP SCHEMA IF EXISTS vandelay CASCADE;
 
 BEGIN;
 
+CREATE OR REPLACE FUNCTION array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
+
 CREATE SCHEMA vandelay;
 
+CREATE TABLE vandelay.match_set (
+    id      SERIAL  PRIMARY KEY,
+    name    TEXT        NOT NULL,
+    owner   INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
+    mtype   TEXT        NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
+    CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
+);
+
+-- Table to define match points, either FF via SVF or tag+subfield
+CREATE TABLE vandelay.match_set_point (
+    id          SERIAL  PRIMARY KEY,
+    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id),
+    svf         TEXT    REFERENCES config.record_attr_definition,
+    tag         TEXT,
+    subfield    TEXT,
+    required    BOOL    NOT NULL DEFAULT TRUE,
+    quality     INT     NOT NULL DEFAULT 1, -- higher is better
+    CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
+    CONSTRAINT vmsp_need_a_tag_or_a_ff CHECK (tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL)),
+    CONSTRAINT vmsp_def_once_per_set UNIQUE (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''))
+);
+
+CREATE TABLE vandelay.match_set_quality (
+    id          SERIAL  PRIMARY KEY,
+    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id),
+    svf         TEXT    REFERENCES config.record_attr_definition,
+    tag         TEXT,
+    subfield    TEXT,
+    value       TEXT    NOT NULL,
+    quality     INT     NOT NULL DEFAULT 1, -- higher is better
+    CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
+    CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK (tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL)),
+    CONSTRAINT vmsq_def_once_per_set UNIQUE (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''))
+);
+
+
 CREATE TABLE vandelay.queue (
 	id				BIGSERIAL	PRIMARY KEY,
 	owner			INT			NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
 	name			TEXT		NOT NULL,
 	complete		BOOL		NOT NULL DEFAULT FALSE,
 	queue_type		TEXT		NOT NULL DEFAULT 'bib' CHECK (queue_type IN ('bib','authority')),
+    match_set       INT         REFERENCES vandelay.match_set (id) DEFERRABLE INITIALLY DEFERRED ON UPDATE CASCADE ON DELETE SET NULL,
 	CONSTRAINT vand_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
 );
 
@@ -32,8 +71,7 @@ CREATE TABLE vandelay.bib_attr_definition (
 	code		TEXT	UNIQUE NOT NULL,
 	description	TEXT,
 	xpath		TEXT	NOT NULL,
-	remove		TEXT	NOT NULL DEFAULT '',
-	ident		BOOL	NOT NULL DEFAULT FALSE
+	remove		TEXT	NOT NULL DEFAULT ''
 );
 
 -- Each TEXT field (other than 'name') should hold an XPath predicate for pulling the data needed
@@ -93,8 +131,7 @@ CREATE INDEX queued_bib_record_attr_record_idx ON vandelay.queued_bib_record_att
 
 CREATE TABLE vandelay.bib_match (
 	id				BIGSERIAL	PRIMARY KEY,
-	field_type		TEXT		NOT NULL CHECK (field_type in ('isbn','tcn_value','id')),
-	matched_attr	INT			REFERENCES vandelay.queued_bib_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	matched_set 	INT			REFERENCES vandelay.match_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	queued_record	BIGINT		REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     quality         INT         NOT NULL DEFAULT 0
@@ -145,41 +182,6 @@ CREATE TABLE vandelay.merge_profile (
 	CONSTRAINT add_replace_strip_or_preserve CHECK ((preserve_spec IS NOT NULL OR replace_spec IS NOT NULL) OR (preserve_spec IS NULL AND replace_spec IS NULL))
 );
 
-CREATE TABLE vandelay.match_set (
-    id      SERIAL  PRIMARY KEY,
-    name    TEXT        NOT NULL,
-    owner   INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
-    mtype   TEXT        NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
-    CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
-);
-
--- Table to define match points, either FF via SVF or tag+subfield
-CREATE TABLE vandelay.match_set_point (
-    id          SERIAL  PRIMARY KEY,
-    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id),
-    fixed_field TEXT, -- should exist in config.marc21_ff_pos_map.fixed_field
-    tag         TEXT,
-    subfield    TEXT,
-    required    BOOL    NOT NULL DEFAULT TRUE,
-    quality     INT     NOT NULL DEFAULT 1, -- higher is better
-    CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
-    CONSTRAINT vmsp_need_a_tag_or_a_ff CHECK (tag IS NOT NULL AND fixed_field IS NULL) OR (tag IS NULL AND fixed_field IS NOT NULL)),
-    CONSTRAINT vmsp_def_once_per_set UNIQUE (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(fixed_field,''))
-);
-
-CREATE TABLE vandelay.match_set_quality (
-    id          SERIAL  PRIMARY KEY,
-    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id),
-    fixed_field TEXT, -- should exist in config.marc21_ff_pos_map.fixed_field
-    tag         TEXT,
-    subfield    TEXT,
-    value       TEXT    NOT NULL,
-    quality     INT     NOT NULL DEFAULT 1, -- higher is better
-    CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
-    CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK (tag IS NOT NULL AND fixed_field IS NULL) OR (tag IS NULL AND fixed_field IS NOT NULL)),
-    CONSTRAINT vmsq_def_once_per_set UNIQUE (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(fixed_field,''))
-);
-
 CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
 DECLARE
     ldr         TEXT;
@@ -366,6 +368,183 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
+DECLARE
+    transformed_xml TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_value      TEXT;
+    new_attrs       HSTORE := ''::HSTORE;
+    attr_def        config.record_attr_definition%ROWTYPE;
+BEGIN
+
+    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
+
+        IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+            SELECT  ARRAY_TO_STRING(ARRAY_ACCUM(x.value), COALESCE(attr_def.joiner,' ')) INTO attr_value
+              FROM  vandelay.flatten_marc(xml) AS x
+              WHERE x.tag LIKE attr_def.tag
+                    AND CASE
+                        WHEN attr_def.sf_list IS NOT NULL
+                            THEN POSITION(x.subfield IN attr_def.sf_list) > 0
+                        ELSE TRUE
+                        END
+              GROUP BY x.tag
+              ORDER BY x.tag
+              LIMIT 1;
+
+        ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+            attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
+
+        ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+            SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+
+            -- See if we can skip the XSLT ... it's expensive
+            IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                -- Can't skip the transform
+                IF xfrm.xslt <> '---' THEN
+                    transformed_xml := oils_xslt_process(xml,xfrm.xslt);
+                ELSE
+                    transformed_xml := xml;
+                END IF;
+
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            IF xfrm.name IS NULL THEN
+                -- just grab the marcxml (empty) transform
+                SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
+
+        ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+            SELECT  value::TEXT INTO attr_value
+              FROM  vandelay.marc21_physical_characteristics(xml)
+              WHERE subfield = attr_def.phys_char_sf
+              LIMIT 1; -- Just in case ...
+
+        END IF;
+
+        -- apply index normalizers to attr_value
+        FOR normalizer IN
+            SELECT  n.func AS func,
+                    n.param_count AS param_count,
+                    m.params AS params
+              FROM  config.index_normalizer n
+                    JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+              WHERE attr = attr_def.name
+              ORDER BY m.pos LOOP
+                EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    quote_literal( attr_value ) ||
+                    CASE
+                        WHEN normalizer.param_count > 0
+                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                            ELSE ''
+                        END ||
+                    ')' INTO attr_value;
+
+        END LOOP;
+
+        -- Add the new value to the hstore
+        new_attrs := new_attrs || hstore( attr_def.name, attr_value );
+
+    END LOOP;
+
+    RETURN new_attrs;
+END;
+$_$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
+    SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
+$_$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION vandelay.match_bib_record ( ) RETURNS TRIGGER AS $func$
+DECLARE
+    incoming_existing_id    TEXT;
+    my_bib_queue            vandelay.bib_queue%ROWTYPE;
+    my_match_set            vandelay.match_set%ROWTYPE;
+    test                    vandelay.match_set_point%ROWTYPE;
+    potential_matches       BIGINT[];
+    matches                 BIGINT[];
+    rvalue                  TEXT;
+    quality_set             hstore;
+    tmp_rec                 BIGINT;
+    tmp_quality             INT;
+    first_round             BOOL;
+BEGIN
+    DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
+
+    incoming_existing_id := oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]',NEW.marc);
+
+    IF incoming_existing_id IS NOT NULL THEN
+        INSERT INTO vandelay.bib_match (field_type, queued_record, eg_record) VALUES ('id', NEW.id, exact_id);
+        RETURN NEW;
+    END IF;
+
+    SELECT * INTO my_bib_queue FROM vandelay.bib_queue WHERE id = NEW.queue;
+
+    first_round := TRUE;
+    -- whew ... here we go ...
+    FOR test IN SELECT * FROM vandelay.match_set_point WHERE match_set = my_bib_queue.match_set ORDER BY required DESC LOOP
+        IF test.tag IS NOT NULL THEN
+            FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
+                SELECT ARRAY_ACCUM(DISTINCT record) INTO potential_matches FROM metabib.real_full_rec WHERE tag = test.tag AND subfield = test.subfield AND value = rvalue;
+
+                IF first_round THEN
+                    matches := potential_matches;
+                    first_round := FALSE;
+                ELSIF test.required THEN
+                    FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
+                        IF tmp_rec NOT IN (SELECT * FROM UNNEST(potential_matches)) THEN
+                            matches := array_remove_item_by_value(matches, tmp_rec);
+                            potential_matches := array_remove_item_by_value(potential_matches, tmp_rec);
+                        END IF;
+                    END LOOP;
+                END IF;
+
+                -- add the quality for this match
+                FOR tmp_rec IN SELECT * FROM UNNEST(potential_matches);
+                    tmp_quality := COALESCE((quality_set -> tmp_rec::TEXT)::INT, 0);
+                    quality := quality || hstore(tmp_rec::TEXT, (tmp_quality + test.quality)::TEXT);
+                END LOOP;
+
+            END LOOP;
+        ELSE
+            rvalue := vandelay.vandelay.extract_rec_attrs(xml, ARRAY[test.svf]);
+
+            IF first_round THEN
+                matches := potential_matches;
+                first_round := FALSE;
+            ELSIF test.required THEN
+                FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
+                    IF tmp_rec NOT IN (SELECT * FROM UNNEST(potential_matches)) THEN
+                        matches := array_remove_item_by_value(matches, tmp_rec);
+                        potential_matches := array_remove_item_by_value(potential_matches, tmp_rec);
+                    END IF;
+                END LOOP;
+            END IF;
+
+            -- add the quality for this match
+            FOR tmp_rec IN SELECT * FROM UNNEST(potential_matches);
+                tmp_quality := COALESCE((quality_set -> tmp_rec::TEXT)::INT, 0);
+                quality := quality || hstore(tmp_rec::TEXT, (tmp_quality + test.quality)::TEXT);
+            END LOOP;
+
+        END IF;
+    END LOOP;
+
+    FOR tmp_rec IN SELECT * FROM UNNEST(matches) LOOP
+        INSERT INTO vandelay.bib_match (matched_set, queued_record, eg_record, quality) VALUES (my_bib_queue.match_set, NEW.id, tmp_rec, (quality_set -> tmp_rec::TEXT));
+    END LOOP;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
 CREATE OR REPLACE FUNCTION vandelay.incoming_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
 DECLARE
     out_q   INT := 0;
@@ -380,8 +559,8 @@ BEGIN
                     out_q := out_q + test.quality;
                 END IF;
             END LOOP;
-        ELSIF test.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
-            IF test.value = vandelay.marc21_extract_fixed_field(xml, test.fixed_field) THEN
+        ELSE
+            IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) THEN
                 out_q := out_q + test.quality;
             END IF;
         END IF;
@@ -1458,8 +1637,7 @@ CREATE TABLE vandelay.authority_attr_definition (
 	code		TEXT	UNIQUE NOT NULL,
 	description	TEXT,
 	xpath		TEXT	NOT NULL,
-	remove		TEXT	NOT NULL DEFAULT '',
-	ident		BOOL	NOT NULL DEFAULT FALSE
+	remove		TEXT	NOT NULL DEFAULT ''
 );
 
 CREATE TABLE vandelay.authority_queue (
@@ -1485,9 +1663,10 @@ CREATE INDEX queued_authority_record_attr_record_idx ON vandelay.queued_authorit
 
 CREATE TABLE vandelay.authority_match (
 	id				BIGSERIAL	PRIMARY KEY,
-	matched_attr	INT			REFERENCES vandelay.queued_authority_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+	matched_set 	INT			REFERENCES vandelay.match_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	queued_record	BIGINT		REFERENCES vandelay.queued_authority_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	eg_record		BIGINT		REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
+	eg_record		BIGINT		REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
+    quality         INT         NOT NULL DEFAULT 0
 );
 
 CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
diff --git a/Open-ILS/src/sql/Pg/030.schema.metabib.sql b/Open-ILS/src/sql/Pg/030.schema.metabib.sql
index 3cbdd49..22bc674 100644
--- a/Open-ILS/src/sql/Pg/030.schema.metabib.sql
+++ b/Open-ILS/src/sql/Pg/030.schema.metabib.sql
@@ -1116,7 +1116,7 @@ BEGIN
             IF TG_OP = 'INSERT' OR OLD.deleted THEN -- initial insert OR revivication
                 INSERT INTO metabib.record_attr (id, attrs) VALUES (NEW.id, new_attrs);
             ELSE
-                UPDATE metabib.record_attr SET attrs = attrs || new_attrs WHERE id = NEW.id;
+                UPDATE metabib.record_attr SET attrs = new_attrs WHERE id = NEW.id;
             END IF;
 
         END IF;

commit efa9520b2133321b1649c92eda5db29d1d6d8ec5
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Mar 11 10:12:13 2011 -0500

    Teach fieldmapper about the new vandelay classes

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index ce42aa3..c268e8f 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -275,6 +275,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true"/>
 			<field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
 			<field reporter:label="Type" name="queue_type" reporter:datatype="text"/>
+			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 			<field reporter:label="Item Import Attribute Definition" name="item_attr_def" reporter:datatype="link"/>
 		</fields>
 		<links>
@@ -369,6 +370,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Matched Attribute" name="matched_attr" reporter:datatype="link"/>
 			<field reporter:label="Evergreen Record" name="eg_record" reporter:datatype="link"/>
 			<field reporter:label="Field Type" name="field_type" reporter:datatype="text"/>
+			<field reporter:label="Quality" name="quality" reporter:datatype="text"/>
 		</fields>
 		<links>
 			<link field="queued_record" reltype="has_a" key="id" map="" class="vqbr"/>
@@ -392,6 +394,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true"/>
 			<field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
 			<field reporter:label="Type" name="queue_type" reporter:datatype="text"/>
+			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 		</fields>
 		<links>
 			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
@@ -481,6 +484,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Queued Record" name="queued_record" reporter:datatype="link"/>
 			<field reporter:label="Matched Attribute" name="matched_attr" reporter:datatype="link"/>
 			<field reporter:label="Evergreen Record" name="eg_record" reporter:datatype="link"/>
+			<field reporter:label="Quality" name="quality" reporter:datatype="int"/>
 		</fields>
 		<links>
 			<link field="queued_record" reltype="has_a" key="id" map="" class="vqbr"/>
@@ -497,6 +501,71 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		</permacrud>
 	</class>
 
+	<class id="vms" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="vandelay::match_set" oils_persist:tablename="vandelay.match_set" reporter:label="Record Matching Definition Set">
+		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.match_set_id_seq">
+			<field reporter:label="Match Set ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Name" name="name" reporter:datatype="text"/>
+			<field reporter:label="Owning Library" name="owner" reporter:datatype="link"/>
+			<field reporter:label="Match Set Type" name="mtype" reporter:datatype="text"/>
+		</fields>
+		<links>
+			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
+		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="CREATE_IMPORT_MATCH_SET ADMIN_IMPORT_IMPORT_MATCH_SET" context_field="owner"/>
+				<retrieve permission="CREATE_IMPORT_MATCH_SET UPDATE_IMPORT_MATCH_SET DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+				<update permission="UPDATE_IMPORT_MATCH_SET" context_field="owner"/>
+				<delete permission="DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+			</actions>
+		</permacrud>
+	</class>
+
+	<class id="vmsp" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="vandelay::match_set_point" oils_persist:tablename="vandelay.match_set_point" reporter:label="Record Matching Definition">
+		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.match_set_point_id_seq">
+			<field reporter:label="Match Definition ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
+			<field reporter:label="Fixed Field" name="fixed_field" reporter:datatype="text"/>
+			<field reporter:label="Tag" name="tag" reporter:datatype="text"/>
+			<field reporter:label="Subfield" name="subfield" reporter:datatype="text"/>
+			<field reporter:label="Required?" name="required" reporter:datatype="bool"/>
+			<field reporter:label="Importance" name="quality" reporter:datatype="int"/>
+		</fields>
+		<links>
+			<link field="match_set" reltype="has_a" key="id" map="" class="vms"/>
+		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="CREATE_IMPORT_MATCH_SET ADMIN_IMPORT_IMPORT_MATCH_SET" context_field="owner"/>
+				<retrieve permission="CREATE_IMPORT_MATCH_SET UPDATE_IMPORT_MATCH_SET DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+				<update permission="UPDATE_IMPORT_MATCH_SET" context_field="owner"/>
+				<delete permission="DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+			</actions>
+		</permacrud>
+	</class>
+
+	<class id="vmsq" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="vandelay::match_set_quality" oils_persist:tablename="vandelay.match_set_quality" reporter:label="Record Quality Metric">
+		<fields oils_persist:primary="id" oils_persist:sequence="vandelay.match_set_quality_id_seq">
+			<field reporter:label="Quality Metric ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Match Set" name="match_set" reporter:datatype="link"/>
+			<field reporter:label="Fixed Field" name="fixed_field" reporter:datatype="text"/>
+			<field reporter:label="Tag" name="tag" reporter:datatype="text"/>
+			<field reporter:label="Subfield" name="subfield" reporter:datatype="text"/>
+			<field reporter:label="Value" name="value" reporter:datatype="text"/>
+			<field reporter:label="Importance" name="quality" reporter:datatype="int"/>
+		</fields>
+		<links>
+			<link field="match_set" reltype="has_a" key="id" map="" class="vms"/>
+		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="CREATE_IMPORT_MATCH_SET ADMIN_IMPORT_IMPORT_MATCH_SET" context_field="owner"/>
+				<retrieve permission="CREATE_IMPORT_MATCH_SET UPDATE_IMPORT_MATCH_SET DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+				<update permission="UPDATE_IMPORT_MATCH_SET" context_field="owner"/>
+				<delete permission="DELETE_IMPORT_MATCH_SET" context_field="owner"/>
+			</actions>
+		</permacrud>
+	</class>
 
 	<class id="auoi" controller="open-ils.cstore" oils_obj:fieldmapper="actor::usr_org_unit_opt_in" oils_persist:tablename="actor.usr_org_unit_opt_in" reporter:label="User Sharing Opt-in">
 		<fields oils_persist:primary="id" oils_persist:sequence="actor.usr_org_unit_opt_in_id_seq">

commit 57b7f22475b6b3cdbe3514546c82eb5b2947041c
Author: Mike Rylander <mrylander at gmail.com>
Date:   Thu Mar 10 16:35:44 2011 -0500

    moving functions around; tables for configuring match points and quality metrics

diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
index 7841170..9b3e3cf 100644
--- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
+++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
@@ -18,7 +18,8 @@ CREATE TABLE vandelay.queued_record (
     create_time	TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
     import_time	TIMESTAMP WITH TIME ZONE,
 	purpose		TEXT						NOT NULL DEFAULT 'import' CHECK (purpose IN ('import','overlay')),
-    marc		TEXT                        NOT NULL
+    marc		TEXT                        NOT NULL,
+    quality     INT                         NOT NULL DEFAULT 0
 );
 
 
@@ -95,7 +96,8 @@ CREATE TABLE vandelay.bib_match (
 	field_type		TEXT		NOT NULL CHECK (field_type in ('isbn','tcn_value','id')),
 	matched_attr	INT			REFERENCES vandelay.queued_bib_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	queued_record	BIGINT		REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
+	eg_record		BIGINT		REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    quality         INT         NOT NULL DEFAULT 0
 );
 
 -- DROP TABLE vandelay.import_item CASCADE;
@@ -143,6 +145,251 @@ CREATE TABLE vandelay.merge_profile (
 	CONSTRAINT add_replace_strip_or_preserve CHECK ((preserve_spec IS NOT NULL OR replace_spec IS NOT NULL) OR (preserve_spec IS NULL AND replace_spec IS NULL))
 );
 
+CREATE TABLE vandelay.match_set (
+    id      SERIAL  PRIMARY KEY,
+    name    TEXT        NOT NULL,
+    owner   INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
+    mtype   TEXT        NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
+    CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
+);
+
+-- Table to define match points, either FF via SVF or tag+subfield
+CREATE TABLE vandelay.match_set_point (
+    id          SERIAL  PRIMARY KEY,
+    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id),
+    fixed_field TEXT, -- should exist in config.marc21_ff_pos_map.fixed_field
+    tag         TEXT,
+    subfield    TEXT,
+    required    BOOL    NOT NULL DEFAULT TRUE,
+    quality     INT     NOT NULL DEFAULT 1, -- higher is better
+    CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
+    CONSTRAINT vmsp_need_a_tag_or_a_ff CHECK (tag IS NOT NULL AND fixed_field IS NULL) OR (tag IS NULL AND fixed_field IS NOT NULL)),
+    CONSTRAINT vmsp_def_once_per_set UNIQUE (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(fixed_field,''))
+);
+
+CREATE TABLE vandelay.match_set_quality (
+    id          SERIAL  PRIMARY KEY,
+    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id),
+    fixed_field TEXT, -- should exist in config.marc21_ff_pos_map.fixed_field
+    tag         TEXT,
+    subfield    TEXT,
+    value       TEXT    NOT NULL,
+    quality     INT     NOT NULL DEFAULT 1, -- higher is better
+    CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
+    CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK (tag IS NOT NULL AND fixed_field IS NULL) OR (tag IS NULL AND fixed_field IS NOT NULL)),
+    CONSTRAINT vmsq_def_once_per_set UNIQUE (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(fixed_field,''))
+);
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
+DECLARE
+    ldr         TEXT;
+    tval        TEXT;
+    tval_rec    RECORD;
+    bval        TEXT;
+    bval_rec    RECORD;
+    retval      config.marc21_rec_type_map%ROWTYPE;
+BEGIN
+    ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
+
+    IF ldr IS NULL OR ldr = '' THEN
+        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
+        RETURN retval;
+    END IF;
+
+    SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
+    SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
+
+
+    tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
+    bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
+
+    -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
+
+    SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
+
+
+    IF retval.code IS NULL THEN
+        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
+    END IF;
+
+    RETURN retval;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
+DECLARE
+    rtype       TEXT;
+    ff_pos      RECORD;
+    tag_data    RECORD;
+    val         TEXT;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
+        FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+            val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+            RETURN val;
+        END LOOP;
+        val := REPEAT( ff_pos.default_val, ff_pos.length );
+        RETURN val;
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TYPE biblio.record_ff_map AS (record BIGINT, ff_name TEXT, ff_value TEXT);
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
+DECLARE
+    tag_data    TEXT;
+    rtype       TEXT;
+    ff_pos      RECORD;
+    output      biblio.record_ff_map%ROWTYPE;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
+        output.ff_name  := ff_pos.fixed_field;
+        output.ff_value := NULL;
+
+        FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(tag) || '"]/text()', marc ) ) x(value) LOOP
+            output.ff_value := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+            IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
+            RETURN NEXT output;
+            output.ff_value := NULL;
+        END LOOP;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TYPE biblio.marc21_physical_characteristics AS ( id INT, record BIGINT, ptype TEXT, subfield INT, value INT );
+CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
+DECLARE
+    rowid   INT := 0;
+    _007    TEXT;
+    ptype   config.marc21_physical_characteristic_type_map%ROWTYPE;
+    psf     config.marc21_physical_characteristic_subfield_map%ROWTYPE;
+    pval    config.marc21_physical_characteristic_value_map%ROWTYPE;
+    retval  biblio.marc21_physical_characteristics%ROWTYPE;
+BEGIN
+
+    _007 := oils_xpath_string( '//*[@tag="007"]', marc );
+
+    IF _007 IS NOT NULL AND _007 <> '' THEN
+        SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
+
+        IF ptype.ptype_key IS NOT NULL THEN
+            FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
+                SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
+
+                IF pval.id IS NOT NULL THEN
+                    rowid := rowid + 1;
+                    retval.id := rowid;
+                    retval.ptype := ptype.ptype_key;
+                    retval.subfield := psf.id;
+                    retval.value := pval.id;
+                    RETURN NEXT retval;
+                END IF;
+
+            END LOOP;
+        END IF;
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREAT TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
+CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
+
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+
+my $xml = shift;
+my $r = MARC::Record->new_from_xml( $xml );
+
+return_next( { tag => 'LDR', value => $r->leader } );
+
+for my $f ( $r->fields ) {
+    if ($f->is_control_field) {
+        return_next({ tag => $f->tag, value => $f->data });
+    } else {
+        for my $s ($f->subfields) {
+            return_next({
+                tag      => $f->tag,
+                ind1     => $f->indicator(1),
+                ind2     => $f->indicator(2),
+                subfield => $s->[0],
+                value    => $s->[1]
+            });
+
+            if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
+                my $trim = $f->indicator(2) || 0;
+                return_next({
+                    tag      => 'tnf',
+                    ind1     => $f->indicator(1),
+                    ind2     => $f->indicator(2),
+                    subfield => 'a',
+                    value    => substr( $s->[1], $trim )
+                });
+            }
+        }
+    }
+}
+
+return undef;
+
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION vandelay.flatten_marc ( marc TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
+DECLARE
+    output  vandelay.flat_marc%ROWTYPE;
+    field   RECORD;
+BEGIN
+    FOR field IN SELECT * FROM vandelay.flay_marc( marc ) LOOP
+        output.ind1 := field.ind1;
+        output.ind2 := field.ind2;
+        output.tag := field.tag;
+        output.subfield := field.subfield;
+        IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
+            output.value := naco_normalize(field.value, field.subfield);
+        ELSE
+            output.value := field.value;
+        END IF;
+
+        CONTINUE WHEN output.value IS NULL;
+
+        RETURN NEXT output;
+    END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.incoming_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
+DECLARE
+    out_q   INT := 0;
+    rvalue  TEXT;
+    test    vandelay.match_set_quality%ROWTYPE;
+BEGIN
+
+    FOR test IN SELECT * FROM vandelay.match_set_quality WHERE match_set = match_set_id LOOP
+        IF test.tag IS NOT NULL THEN
+            FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
+                IF test.value = rvalue THEN
+                    out_q := out_q + test.quality;
+                END IF;
+            END LOOP;
+        ELSIF test.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+            IF test.value = vandelay.marc21_extract_fixed_field(xml, test.fixed_field) THEN
+                out_q := out_q + test.quality;
+            END IF;
+        END IF;
+    END LOOP;
+
+    RETURN out_q;
+END;
+$_$ LANGUAGE PLPGSQL;
 
 CREATE TYPE vandelay.tcn_data AS (tcn TEXT, tcn_source TEXT, used BOOL);
 CREATE OR REPLACE FUNCTION vandelay.find_bib_tcn_data ( xml TEXT ) RETURNS SETOF vandelay.tcn_data AS $_$
diff --git a/Open-ILS/src/sql/Pg/030.schema.metabib.sql b/Open-ILS/src/sql/Pg/030.schema.metabib.sql
index fb2c4cb..3cbdd49 100644
--- a/Open-ILS/src/sql/Pg/030.schema.metabib.sql
+++ b/Open-ILS/src/sql/Pg/030.schema.metabib.sql
@@ -417,19 +417,13 @@ DECLARE
 BEGIN
 	SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
 
-	FOR field IN SELECT * FROM biblio.flatten_marc( bib.marc ) LOOP
+	FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
 		output.record := rid;
 		output.ind1 := field.ind1;
 		output.ind2 := field.ind2;
 		output.tag := field.tag;
 		output.subfield := field.subfield;
-		IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
-			output.value := naco_normalize(field.value, field.subfield);
-		ELSE
-			output.value := field.value;
-		END IF;
-
-		CONTINUE WHEN output.value IS NULL;
+		output.value := field.value;
 
 		RETURN NEXT output;
 	END LOOP;
@@ -620,43 +614,6 @@ CREATE OR REPLACE FUNCTION biblio.marc21_extract_all_fixed_fields( rid BIGINT )
     SELECT $1 AS record, ff_name, ff_value FROM vandelay.marc21_extract_all_fixed_fields( (SELECT marc FROM biblio.record_entry WHERE id = $1) );
 $func$ LANGUAGE SQL;
 
-CREATE TYPE biblio.marc21_physical_characteristics AS ( id INT, record BIGINT, ptype TEXT, subfield INT, value INT );
-CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
-DECLARE
-    rowid   INT := 0;
-    _007    TEXT;
-    ptype   config.marc21_physical_characteristic_type_map%ROWTYPE;
-    psf     config.marc21_physical_characteristic_subfield_map%ROWTYPE;
-    pval    config.marc21_physical_characteristic_value_map%ROWTYPE;
-    retval  biblio.marc21_physical_characteristics%ROWTYPE;
-BEGIN
-
-    _007 := oils_xpath_string( '//*[@tag="007"]', marc );
-
-    IF _007 IS NOT NULL AND _007 <> '' THEN
-        SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
-
-        IF ptype.ptype_key IS NOT NULL THEN
-            FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
-                SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
-
-                IF pval.id IS NOT NULL THEN
-                    rowid := rowid + 1;
-                    retval.id := rowid;
-                    retval.ptype := ptype.ptype_key;
-                    retval.subfield := psf.id;
-                    retval.value := pval.id;
-                    RETURN NEXT retval;
-                END IF;
-
-            END LOOP;
-        END IF;
-    END IF;
-
-    RETURN;
-END;
-$func$ LANGUAGE PLPGSQL;
-
 CREATE OR REPLACE FUNCTION biblio.marc21_physical_characteristics( rid BIGINT ) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
     SELECT id, $1 AS record, ptype, subfield, value FROM vandelay.marc21_physical_characteristics( (SELECT marc FROM biblio.record_entry WHERE id = $1) );
 $func$ LANGUAGE SQL;

commit 66144a226706c2ee8cccbcf2c5248f3c4aed404c
Author: Thomas Berezansky <tsbere at mvlc.org>
Date:   Thu Jun 16 17:03:36 2011 -0400

    Delete protection - Ensure you can touch group
    
    Same protection editing a user seems to get:
    If you don't have the right group_application.user perm, reject
    
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
index 5e704b4..1f0fa93 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
@@ -3515,7 +3515,13 @@ sub really_delete_user {
     my $e = new_editor(authtoken => $auth, xact => 1);
     return $e->die_event unless $e->checkauth;
     my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
+    # No deleting yourself - UI is supposed to stop you first, though.
+    return $e->die_event unless $e->requestor->id != $user->id;
     return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
+    # Check if you are allowed to mess with this patron permission group at all
+    my $session = OpenSRF::AppSession->create( "open-ils.storage" );
+    my $evt = group_perm_failed($session, $e->requestor, $user);
+    return $e->die_event($evt) if $evt;
     my $stat = $e->json_query(
         {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0] 
         or return $e->die_event;

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/examples/fm_IDL.xml                       |  135 ++-
 .../lib/OpenILS/Application/Trigger/Reactor.pm     |   51 +
 .../perlmods/lib/OpenILS/Application/Vandelay.pm   |  859 +++++++++--
 Open-ILS/src/sql/Pg/000.functions.general.sql      |    2 +
 Open-ILS/src/sql/Pg/002.schema.config.sql          |    6 +-
 Open-ILS/src/sql/Pg/012.schema.vandelay.sql        | 1066 +++++++++-----
 Open-ILS/src/sql/Pg/030.schema.metabib.sql         |   72 +-
 Open-ILS/src/sql/Pg/800.fkeys.sql                  |    2 +
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |  597 ++++++++-
 Open-ILS/src/sql/Pg/999.functions.global.sql       |  392 ++++-
 .../upgrade/0526.schema.upgrade-dep-tracking.sql   |    4 +-
 ...schema.vandelay-record-matching-and-quality.sql | 1594 ++++++++++++++++++++
 Open-ILS/web/css/skin/default/vandelay.css         |   20 +
 Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js    |    8 +
 .../web/js/dojo/openils/vandelay/TreeDndSource.js  |  123 ++
 .../web/js/dojo/openils/vandelay/TreeStoreModel.js |   52 +
 .../web/js/dojo/openils/vandelay/nls/match_set.js  |   16 +
 Open-ILS/web/js/dojo/openils/widget/AutoGrid.js    |    9 +-
 .../ui/default/conify/global/vandelay/match_set.js |  606 ++++++++
 Open-ILS/web/js/ui/default/vandelay/vandelay.js    |  509 ++++++--
 Open-ILS/web/opac/locale/en-US/lang.dtd            |    1 +
 Open-ILS/web/opac/locale/en-US/vandelay.dtd        |   42 +-
 .../default/conify/global/vandelay/match_set.tt2   |   94 ++
 .../conify/global/vandelay/match_set_tree.tt2      |  150 ++
 .../web/templates/default/vandelay/inc/attrs.tt2   |   10 -
 .../default/vandelay/inc/import_errors.tt2         |   77 +
 .../web/templates/default/vandelay/inc/matches.tt2 |    9 +-
 .../templates/default/vandelay/inc/profiles.tt2    |    9 +-
 .../web/templates/default/vandelay/inc/queue.tt2   |  272 ++--
 .../web/templates/default/vandelay/inc/toolbar.tt2 |    2 +
 .../web/templates/default/vandelay/inc/upload.tt2  |   63 +-
 .../web/templates/default/vandelay/vandelay.tt2    |    5 +
 .../xul/staff_client/chrome/content/main/menu.js   |    4 +
 .../chrome/content/main/menu_frame_menus.xul       |    2 +
 34 files changed, 5998 insertions(+), 865 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0572.schema.vandelay-record-matching-and-quality.sql
 create mode 100644 Open-ILS/web/js/dojo/openils/vandelay/TreeDndSource.js
 create mode 100644 Open-ILS/web/js/dojo/openils/vandelay/TreeStoreModel.js
 create mode 100644 Open-ILS/web/js/dojo/openils/vandelay/nls/match_set.js
 create mode 100644 Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
 create mode 100644 Open-ILS/web/templates/default/conify/global/vandelay/match_set.tt2
 create mode 100644 Open-ILS/web/templates/default/conify/global/vandelay/match_set_tree.tt2
 create mode 100644 Open-ILS/web/templates/default/vandelay/inc/import_errors.tt2


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list