Advertisement
roganhamby

Testing - Circ Policy Script

Feb 9th, 2015
348
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. DROP TABLE IF EXISTS rogan.test_items;
  2.  
  3. CREATE TABLE rogan.test_items AS
  4.         SELECT DISTINCT ON (ac.circ_modifier, paou.shortname) ac.id, ac.circ_lib, paou.id as p_aou, ac.circ_modifier
  5.         FROM asset.COPY ac
  6.         JOIN asset.copy_location acl ON acl.id = ac.LOCATION
  7.         JOIN actor.org_unit aou ON aou.id = ac.circ_lib
  8.         JOIN actor.org_unit paou ON paou.id = aou.parent_ou
  9.         WHERE ac.deleted = FALSE AND ac.status in (0,7) AND ac.circulate = TRUE AND acl.circulate = TRUE AND aou.ou_type = 3
  10.         UNION ALL
  11.         SELECT DISTINCT ON (ac.circ_modifier, paou.shortname) ac.id, ac.circ_lib, paou.id as p_aou, ac.circ_modifier
  12.         FROM asset.COPY ac
  13.         JOIN asset.copy_location acl ON acl.id = ac.LOCATION
  14.         JOIN actor.org_unit aou ON aou.id = ac.circ_lib
  15.         JOIN actor.org_unit paou ON paou.id = aou.parent_ou
  16.         WHERE ac.deleted = FALSE AND ac.status in (0,7) AND ac.circulate = TRUE AND acl.circulate = TRUE AND aou.ou_type = 5
  17.         ;
  18.  
  19. DROP TABLE IF EXISTS rogan.test_patrons;
  20.  
  21.  
  22. CREATE TABLE rogan.test_patrons AS
  23.         SELECT DISTINCT ON (au.profile, paou.shortname) au.id, au.home_ou, paou.id AS p_aou, au.profile
  24.         FROM actor.usr au
  25.         LEFT JOIN actor.usr_standing_penalty pen ON pen.usr = au.id
  26.         JOIN permission.grp_tree pgt ON pgt.id = au.profile
  27.         JOIN actor.org_unit aou ON aou.id = au.home_ou
  28.         JOIN actor.org_unit paou ON paou.id = aou.parent_ou
  29.         -- JOIN money.usr_summary mus on mus.usr = au.id
  30.         WHERE au.deleted = FALSE AND au.barred = FALSE AND au.expire_date > NOW() + INTERVAL '1 day'
  31.         -- AND mus.balance_owed = 0.0
  32.         AND au.active = TRUE AND pen.id IS NULL AND pgt.parent = 2 AND aou.ou_type = 3 AND pgt.NAME NOT ILIKE '%beta%'
  33.                 UNION ALL
  34.         SELECT DISTINCT ON (au.profile, paou.shortname) au.id, au.home_ou,paou.id AS p_aou, au.profile
  35.         FROM actor.usr au
  36.         LEFT JOIN actor.usr_standing_penalty pen ON pen.usr = au.id
  37.         JOIN permission.grp_tree pgt ON pgt.id = au.profile
  38.         JOIN actor.org_unit aou ON aou.id = au.home_ou
  39.         JOIN actor.org_unit paou ON paou.id = aou.parent_ou
  40.         -- JOIN money.usr_summary mus on mus.usr = au.id
  41.         WHERE au.deleted = FALSE AND au.barred = FALSE AND au.expire_date > NOW() + INTERVAL '1 day'
  42.         -- AND mus.balance_owed = 0.0      
  43.         AND au.active = TRUE AND pen.id IS NULL AND pgt.parent = 2 AND aou.ou_type = 5 AND pgt.NAME NOT ILIKE '%beta%'      
  44.         ;
  45.  
  46.  
  47. DROP TABLE IF EXISTS rogan.test_staff;
  48.  
  49. CREATE TABLE rogan.test_staff AS
  50.         SELECT DISTINCT ON (au.profile, au.home_ou) au.id
  51.         FROM actor.usr au
  52.         LEFT JOIN actor.usr_standing_penalty pen ON pen.usr = au.id
  53.         WHERE au.deleted = FALSE AND au.barred = FALSE AND au.expire_date > NOW() + INTERVAL '1 day' AND pen.id IS NULL
  54.         AND au.profile IN (155,156,4,157,158,268,5,179,247,378,12,185,181,3,175)
  55.         ;
  56.  
  57. DROP TABLE IF EXISTS rogan.test_orgs;
  58.  
  59. CREATE TABLE rogan.test_orgs AS
  60.         SELECT DISTINCT ON (paou.shortname) aou.id, aou.parent_ou as p_aou
  61.         FROM actor.org_unit aou JOIN actor.org_unit paou ON paou.id = aou.parent_ou WHERE aou.shortname ilike '%-BK%'
  62.         UNION ALL
  63.         SELECT DISTINCT ON (paou.shortname) aou.id, aou.parent_ou as p_aou
  64.         FROM actor.org_unit aou JOIN actor.org_unit paou ON paou.id = aou.parent_ou
  65.         WHERE aou.shortname not ilike '%-BK%' and aou.shortname not in ('CAL-XP-SR','BCL-BDC')
  66.         UNION ALL
  67.         SELECT aou.id, aou.parent_ou as p_aou
  68.         FROM actor.org_unit aou JOIN actor.org_unit paou ON paou.id = aou.parent_ou
  69.         WHERE aou.shortname in ('CAL-XP-SR','BCL-BDC')
  70.         ;
  71. /*
  72. CREATE TABLE rogan.test_orgs AS
  73.         SELECT aou.id, aou.parent_ou AS p_aou
  74.         FROM actor.org_unit aou where aou.id in
  75.         (154,159,132,139,6,104,111,109,187,178,180,114,172,174,120,
  76.         122,141,143,145,146,192,193,161,164,195,199,200,4,107,167,169,127,123);
  77. */
  78.  
  79. DROP TABLE IF EXISTS rogan.testing;
  80.  
  81. CREATE TABLE rogan.testing AS
  82. SELECT DISTINCT on (o.id, i.circ_modifier, p.profile) o.id AS "org_unit", i.id AS "item", p.id AS "patron"
  83. FROM rogan.test_items i
  84. JOIN rogan.test_patrons p ON p.p_aou = i.p_aou
  85. JOIN rogan.test_orgs o ON o.p_aou = i.p_aou;
  86.  
  87. ALTER TABLE rogan.testing ADD COLUMN id serial;
  88.  
  89. ALTER TABLE rogan.testing ADD COLUMN result INTEGER;
  90.  
  91. DO $$
  92. DECLARE
  93.    min_id       INT;
  94.    max_id       INT;
  95.    this_id      INT;
  96.    item         INT;
  97.    patron       INT;
  98.    org_unit     INT;
  99.    resultx      INT;
  100. BEGIN
  101.  
  102. SELECT MIN(a.id) INTO min_id FROM rogan.testing a;
  103. SELECT MAX(a.id) INTO max_id FROM rogan.testing a;
  104. this_id := min_id;
  105.  
  106. WHILE this_id <= max_id
  107. LOOP
  108.       SELECT t.item INTO item FROM rogan.testing t WHERE t.id = this_id;
  109.       SELECT t.org_unit INTO org_unit FROM rogan.testing t WHERE t.id = this_id;
  110.       SELECT t. patron INTO patron FROM rogan.testing t WHERE t.id = this_id;
  111.       SELECT a.find_circ_matrix_matchpoint INTO resultx FROM
  112.         (SELECT * FROM rogan.find_circ_matrix_matchpoint(org_unit,item,patron,FALSE)) a;
  113.           UPDATE rogan.testing SET result = resultx WHERE id = this_id;
  114.           this_id := this_id + 1;
  115. END LOOP;
  116. END$$
  117.  
  118. SELECT DISTINCT aou.shortname AS "Library", ac.circ_modifier AS "Circ Modifier", pgt.NAME AS "Patron Profile",
  119. coalesce(duration.NAME,'circulation denied') AS "Circ Duration", recure.NAME AS "Recurring Fine Rule", maxfine.NAME AS "Max Fine",
  120. t.result as "circ matchpoint"
  121. FROM rogan.testing t
  122. JOIN actor.org_unit aou ON aou.id = t.org_unit
  123. JOIN asset.COPY ac ON ac.id = t.item
  124. JOIN actor.usr au ON au.id = t.patron
  125. JOIN permission.grp_tree pgt ON pgt.id = au.profile
  126. JOIN config.circ_matrix_matchpoint cmm ON cmm.id = t.result
  127. LEFT JOIN config.rule_recurring_fine recure ON recure.id = cmm.recurring_fine_rule
  128. LEFT JOIN config.rule_max_fine maxfine ON maxfine.id = cmm.max_fine_rule
  129. LEFT JOIN config.rule_circ_duration duration ON duration.id = cmm.duration_rule
  130. WHERE ac.circ_modifier IS NOT NULL AND cmm.active = TRUE
  131. -- and aou.shortname ilike 'acl%' and ac.circ_modifier ilike '%video%'
  132. ORDER BY 1, 3, 2
  133. ;
  134.  
  135.  
  136.  
  137.  
  138. -- ----------------------------------------------------------------------------------------------------------------------
  139. -- ----------------------------------------------------------------------------------------------------------------------
  140. -- ------------------------ let's modify the helper app from 100 ... sql and have it return just the matchpoint
  141. -- ----------------------------------------------------------------------------------------------------------------------
  142. -- ----------------------------------------------------------------------------------------------------------------------
  143.  
  144.  
  145. select * from rogan.find_circ_matrix_matchpoint(127,4938102,2024338,FALSE);
  146.  
  147. DROP FUNCTION rogan.find_circ_matrix_matchpoint( context_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) ;
  148.  
  149. CREATE OR REPLACE FUNCTION rogan.find_circ_matrix_matchpoint( context_ou INT, match_item BIGINT, match_user INT, renewal BOOL )
  150. RETURNS INTEGER AS $func$
  151. DECLARE
  152.     item_object asset.COPY%ROWTYPE;
  153.     user_object actor.usr%ROWTYPE;
  154.     cn_object       asset.call_number%ROWTYPE;
  155.     rec_descriptor  metabib.rec_descriptor%ROWTYPE;
  156.     cur_matchpoint  config.circ_matrix_matchpoint%ROWTYPE;
  157.     matchpoint      config.circ_matrix_matchpoint%ROWTYPE;
  158.     weights         config.circ_matrix_weights%ROWTYPE;
  159.     user_age        INTERVAL;
  160.     my_item_age     INTERVAL;
  161.     denominator     NUMERIC(6,2);
  162.     row_list        INT[];
  163.     result          ACTION.found_circ_matrix_matchpoint;
  164. BEGIN
  165.     -- Assume failure
  166.     result.success = FALSE;
  167.  
  168.     SELECT INTO item_object * FROM asset.COPY   WHERE id = match_item;
  169.     SELECT INTO user_object * FROM actor.usr    WHERE id = match_user;
  170.  
  171.     -- Fetch useful data
  172.     SELECT INTO cn_object       * FROM asset.call_number        WHERE id = item_object.call_number;
  173.     SELECT INTO rec_descriptor  * FROM metabib.rec_descriptor   WHERE record = cn_object.record;
  174.  
  175.     -- Pre-generate this so we only calc it once
  176.     IF user_object.dob IS NOT NULL THEN
  177.         SELECT INTO user_age AGE(user_object.dob);
  178.     END IF;
  179.  
  180.     -- Ditto
  181.     SELECT INTO my_item_age AGE(COALESCE(item_object.active_date, NOW()));
  182.  
  183.     -- Grab the closest set circ weight setting.
  184.     SELECT INTO weights cw.*
  185.       FROM config.weight_assoc wa
  186.            JOIN config.circ_matrix_weights cw ON (cw.id = wa.circ_weights)
  187.            JOIN actor.org_unit_ancestors_distance( context_ou ) d ON (wa.org_unit = d.id)
  188.       WHERE active
  189.       ORDER BY d.distance
  190.       LIMIT 1;
  191.  
  192.     -- No weights? Bad admin! Defaults to handle that anyway.
  193.     IF weights.id IS NULL THEN
  194.         weights.grp                 := 11.0;
  195.         weights.org_unit            := 10.0;
  196.         weights.circ_modifier       := 5.0;
  197.         weights.copy_location       := 5.0;
  198.         weights.marc_type           := 4.0;
  199.         weights.marc_form           := 3.0;
  200.         weights.marc_bib_level      := 2.0;
  201.         weights.marc_vr_format      := 2.0;
  202.         weights.copy_circ_lib       := 8.0;
  203.         weights.copy_owning_lib     := 8.0;
  204.         weights.user_home_ou        := 8.0;
  205.         weights.ref_flag            := 1.0;
  206.         weights.juvenile_flag       := 6.0;
  207.         weights.is_renewal          := 7.0;
  208.         weights.usr_age_lower_bound := 0.0;
  209.         weights.usr_age_upper_bound := 0.0;
  210.         weights.item_age            := 0.0;
  211.     END IF;
  212.  
  213.     -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
  214.     -- If you break your org tree with funky parenting this may be wrong
  215.     -- Note: This CTE is duplicated in the find_hold_matrix_matchpoint function, and it may be a good idea to split it off to a function
  216.     -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
  217.     WITH all_distance(distance) AS (
  218.             SELECT depth AS distance FROM actor.org_unit_type
  219.         UNION
  220.             SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
  221.         )
  222.     SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
  223.  
  224.     -- Loop over all the potential matchpoints
  225.     FOR cur_matchpoint IN
  226.         SELECT m.*
  227.           FROM  config.circ_matrix_matchpoint m
  228.                 /*LEFT*/ JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.grp = upgad.id
  229.                 /*LEFT*/ JOIN actor.org_unit_ancestors_distance( context_ou ) ctoua ON m.org_unit = ctoua.id
  230.                 LEFT JOIN actor.org_unit_ancestors_distance( cn_object.owning_lib ) cnoua ON m.copy_owning_lib = cnoua.id
  231.                 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.copy_circ_lib = iooua.id
  232.                 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou  ) uhoua ON m.user_home_ou = uhoua.id
  233.           WHERE m.active
  234.                 -- Permission Groups
  235.              -- AND (m.grp                      IS NULL OR upgad.id IS NOT NULL) -- Optional Permission Group?
  236.                 -- Org Units
  237.              -- AND (m.org_unit                 IS NULL OR ctoua.id IS NOT NULL) -- Optional Org Unit?
  238.                 AND (m.copy_owning_lib          IS NULL OR cnoua.id IS NOT NULL)
  239.                 AND (m.copy_circ_lib            IS NULL OR iooua.id IS NOT NULL)
  240.                 AND (m.user_home_ou             IS NULL OR uhoua.id IS NOT NULL)
  241.                 -- Circ Type
  242.                 AND (m.is_renewal               IS NULL OR m.is_renewal = renewal)
  243.                 -- Static User Checks
  244.                 AND (m.juvenile_flag            IS NULL OR m.juvenile_flag = user_object.juvenile)
  245.                 AND (m.usr_age_lower_bound      IS NULL OR (user_age IS NOT NULL AND m.usr_age_lower_bound < user_age))
  246.                 AND (m.usr_age_upper_bound      IS NULL OR (user_age IS NOT NULL AND m.usr_age_upper_bound > user_age))
  247.                 -- Static Item Checks
  248.                 AND (m.circ_modifier            IS NULL OR m.circ_modifier = item_object.circ_modifier)
  249.                 AND (m.copy_location            IS NULL OR m.copy_location = item_object.LOCATION)
  250.                 AND (m.marc_type                IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
  251.                 AND (m.marc_form                IS NULL OR m.marc_form = rec_descriptor.item_form)
  252.                 AND (m.marc_bib_level           IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
  253.                 AND (m.marc_vr_format           IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
  254.                 AND (m.ref_flag                 IS NULL OR m.ref_flag = item_object.ref)
  255.                 AND (m.item_age                 IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
  256.           ORDER BY
  257.                 -- Permission Groups
  258.                 CASE WHEN upgad.distance        IS NOT NULL THEN 2^(2*weights.grp - (upgad.distance/denominator)) ELSE 0.0 END +
  259.                 -- Org Units
  260.                 CASE WHEN ctoua.distance        IS NOT NULL THEN 2^(2*weights.org_unit - (ctoua.distance/denominator)) ELSE 0.0 END +
  261.                 CASE WHEN cnoua.distance        IS NOT NULL THEN 2^(2*weights.copy_owning_lib - (cnoua.distance/denominator)) ELSE 0.0 END +
  262.                 CASE WHEN iooua.distance        IS NOT NULL THEN 2^(2*weights.copy_circ_lib - (iooua.distance/denominator)) ELSE 0.0 END +
  263.                 CASE WHEN uhoua.distance        IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
  264.                 -- Circ Type                    -- Note: 4^x is equiv to 2^(2*x)
  265.                 CASE WHEN m.is_renewal          IS NOT NULL THEN 4^weights.is_renewal ELSE 0.0 END +
  266.                 -- Static User Checks
  267.                 CASE WHEN m.juvenile_flag       IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
  268.                 CASE WHEN m.usr_age_lower_bound IS NOT NULL THEN 4^weights.usr_age_lower_bound ELSE 0.0 END +
  269.                 CASE WHEN m.usr_age_upper_bound IS NOT NULL THEN 4^weights.usr_age_upper_bound ELSE 0.0 END +
  270.                 -- Static Item Checks
  271.                 CASE WHEN m.circ_modifier       IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
  272.                 CASE WHEN m.copy_location       IS NOT NULL THEN 4^weights.copy_location ELSE 0.0 END +
  273.                 CASE WHEN m.marc_type           IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
  274.                 CASE WHEN m.marc_form           IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
  275.                 CASE WHEN m.marc_vr_format      IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
  276.                 CASE WHEN m.ref_flag            IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
  277.                 -- Item age has a slight adjustment to weight based on value.
  278.                 -- This should ensure that a shorter age limit comes first when all else is equal.
  279.                 -- NOTE: This assumes that intervals will normally be in days.
  280.                 CASE WHEN m.item_age            IS NOT NULL THEN 4^weights.item_age - 1 + 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC,
  281.                 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
  282.                 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
  283.                 m.id LOOP
  284.  
  285.         -- Record the full matching row list
  286.         row_list := row_list || cur_matchpoint.id;
  287.  
  288.         -- No matchpoint yet?
  289.         IF matchpoint.id IS NULL THEN
  290.             -- Take the entire matchpoint as a starting point
  291.             matchpoint := cur_matchpoint;
  292.             CONTINUE; -- No need to look at this row any more.
  293.         END IF;
  294.  
  295.         -- Incomplete matchpoint?
  296.         IF matchpoint.circulate IS NULL THEN
  297.             matchpoint.circulate := cur_matchpoint.circulate;
  298.         END IF;
  299.         IF matchpoint.duration_rule IS NULL THEN
  300.             matchpoint.duration_rule := cur_matchpoint.duration_rule;
  301.         END IF;
  302.         IF matchpoint.recurring_fine_rule IS NULL THEN
  303.             matchpoint.recurring_fine_rule := cur_matchpoint.recurring_fine_rule;
  304.         END IF;
  305.         IF matchpoint.max_fine_rule IS NULL THEN
  306.             matchpoint.max_fine_rule := cur_matchpoint.max_fine_rule;
  307.         END IF;
  308.         IF matchpoint.hard_due_date IS NULL THEN
  309.             matchpoint.hard_due_date := cur_matchpoint.hard_due_date;
  310.         END IF;
  311.         IF matchpoint.total_copy_hold_ratio IS NULL THEN
  312.             matchpoint.total_copy_hold_ratio := cur_matchpoint.total_copy_hold_ratio;
  313.         END IF;
  314.         IF matchpoint.available_copy_hold_ratio IS NULL THEN
  315.             matchpoint.available_copy_hold_ratio := cur_matchpoint.available_copy_hold_ratio;
  316.         END IF;
  317.         IF matchpoint.renewals IS NULL THEN
  318.             matchpoint.renewals := cur_matchpoint.renewals;
  319.         END IF;
  320.         IF matchpoint.grace_period IS NULL THEN
  321.             matchpoint.grace_period := cur_matchpoint.grace_period;
  322.         END IF;
  323.     END LOOP;
  324.  
  325.     -- Check required fields
  326.     IF matchpoint.circulate             IS NOT NULL AND
  327.        matchpoint.duration_rule         IS NOT NULL AND
  328.        matchpoint.recurring_fine_rule   IS NOT NULL AND
  329.        matchpoint.max_fine_rule         IS NOT NULL THEN
  330.         -- All there? We have a completed match.
  331.         result.success := TRUE;
  332.     END IF;
  333.  
  334.     -- Include the assembled matchpoint, even if it isn't complete
  335.     result.matchpoint := matchpoint;
  336.  
  337.     -- Include (for debugging) the full list of matching rows
  338.     result.buildrows := row_list;
  339.  
  340.     -- Hand the result back to caller
  341.     -- RETURN result;
  342.     RETURN matchpoint.id;
  343.    
  344. END;
  345. $func$ LANGUAGE plpgsql;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement