jbe@218: BEGIN; jbe@218: jbe@218: -- update version number: jbe@218: CREATE OR REPLACE VIEW "liquid_feedback_version" AS jbe@218: SELECT * FROM (VALUES ('2.0.3', 2, 0, 3)) jbe@218: AS "subquery"("string", "major", "minor", "revision"); jbe@218: jbe@218: -- update function "calculate_ranks"("issue"."id") jbe@218: -- regarding setting "eligible"=FALSE for non-eligible admitted initiatives jbe@218: -- and regarding "rank" ordering: jbe@218: CREATE OR REPLACE FUNCTION "calculate_ranks"("issue_id_p" "issue"."id"%TYPE) jbe@218: RETURNS VOID jbe@218: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@218: DECLARE jbe@218: "issue_row" "issue"%ROWTYPE; jbe@218: "policy_row" "policy"%ROWTYPE; jbe@218: "dimension_v" INTEGER; jbe@218: "vote_matrix" INT4[][]; -- absolute votes jbe@218: "matrix" INT8[][]; -- defeat strength / best paths jbe@218: "i" INTEGER; jbe@218: "j" INTEGER; jbe@218: "k" INTEGER; jbe@218: "battle_row" "battle"%ROWTYPE; jbe@218: "rank_ary" INT4[]; jbe@218: "rank_v" INT4; jbe@218: "done_v" INTEGER; jbe@218: "winners_ary" INTEGER[]; jbe@218: "initiative_id_v" "initiative"."id"%TYPE; jbe@218: BEGIN jbe@218: SELECT * INTO "issue_row" jbe@218: FROM "issue" WHERE "id" = "issue_id_p" jbe@218: FOR UPDATE; jbe@218: SELECT * INTO "policy_row" jbe@218: FROM "policy" WHERE "id" = "issue_row"."policy_id"; jbe@218: SELECT count(1) INTO "dimension_v" jbe@218: FROM "battle_participant" WHERE "issue_id" = "issue_id_p"; jbe@218: -- Create "vote_matrix" with absolute number of votes in pairwise jbe@218: -- comparison: jbe@218: "vote_matrix" := array_fill(NULL::INT4, ARRAY["dimension_v", "dimension_v"]); jbe@218: "i" := 1; jbe@218: "j" := 2; jbe@218: FOR "battle_row" IN jbe@218: SELECT * FROM "battle" WHERE "issue_id" = "issue_id_p" jbe@218: ORDER BY jbe@218: "winning_initiative_id" NULLS LAST, jbe@218: "losing_initiative_id" NULLS LAST jbe@218: LOOP jbe@218: "vote_matrix"["i"]["j"] := "battle_row"."count"; jbe@218: IF "j" = "dimension_v" THEN jbe@218: "i" := "i" + 1; jbe@218: "j" := 1; jbe@218: ELSE jbe@218: "j" := "j" + 1; jbe@218: IF "j" = "i" THEN jbe@218: "j" := "j" + 1; jbe@218: END IF; jbe@218: END IF; jbe@218: END LOOP; jbe@218: IF "i" != "dimension_v" OR "j" != "dimension_v" + 1 THEN jbe@218: RAISE EXCEPTION 'Wrong battle count (should not happen)'; jbe@218: END IF; jbe@218: -- Store defeat strengths in "matrix" using "defeat_strength" jbe@218: -- function: jbe@218: "matrix" := array_fill(NULL::INT8, ARRAY["dimension_v", "dimension_v"]); jbe@218: "i" := 1; jbe@218: LOOP jbe@218: "j" := 1; jbe@218: LOOP jbe@218: IF "i" != "j" THEN jbe@218: "matrix"["i"]["j"] := "defeat_strength"( jbe@218: "vote_matrix"["i"]["j"], jbe@218: "vote_matrix"["j"]["i"] jbe@218: ); jbe@218: END IF; jbe@218: EXIT WHEN "j" = "dimension_v"; jbe@218: "j" := "j" + 1; jbe@218: END LOOP; jbe@218: EXIT WHEN "i" = "dimension_v"; jbe@218: "i" := "i" + 1; jbe@218: END LOOP; jbe@218: -- Find best paths: jbe@218: "i" := 1; jbe@218: LOOP jbe@218: "j" := 1; jbe@218: LOOP jbe@218: IF "i" != "j" THEN jbe@218: "k" := 1; jbe@218: LOOP jbe@218: IF "i" != "k" AND "j" != "k" THEN jbe@218: IF "matrix"["j"]["i"] < "matrix"["i"]["k"] THEN jbe@218: IF "matrix"["j"]["i"] > "matrix"["j"]["k"] THEN jbe@218: "matrix"["j"]["k"] := "matrix"["j"]["i"]; jbe@218: END IF; jbe@218: ELSE jbe@218: IF "matrix"["i"]["k"] > "matrix"["j"]["k"] THEN jbe@218: "matrix"["j"]["k"] := "matrix"["i"]["k"]; jbe@218: END IF; jbe@218: END IF; jbe@218: END IF; jbe@218: EXIT WHEN "k" = "dimension_v"; jbe@218: "k" := "k" + 1; jbe@218: END LOOP; jbe@218: END IF; jbe@218: EXIT WHEN "j" = "dimension_v"; jbe@218: "j" := "j" + 1; jbe@218: END LOOP; jbe@218: EXIT WHEN "i" = "dimension_v"; jbe@218: "i" := "i" + 1; jbe@218: END LOOP; jbe@218: -- Determine order of winners: jbe@218: "rank_ary" := array_fill(NULL::INT4, ARRAY["dimension_v"]); jbe@218: "rank_v" := 1; jbe@218: "done_v" := 0; jbe@218: LOOP jbe@218: "winners_ary" := '{}'; jbe@218: "i" := 1; jbe@218: LOOP jbe@218: IF "rank_ary"["i"] ISNULL THEN jbe@218: "j" := 1; jbe@218: LOOP jbe@218: IF jbe@218: "i" != "j" AND jbe@218: "rank_ary"["j"] ISNULL AND jbe@218: "matrix"["j"]["i"] > "matrix"["i"]["j"] jbe@218: THEN jbe@218: -- someone else is better jbe@218: EXIT; jbe@218: END IF; jbe@218: IF "j" = "dimension_v" THEN jbe@218: -- noone is better jbe@218: "winners_ary" := "winners_ary" || "i"; jbe@218: EXIT; jbe@218: END IF; jbe@218: "j" := "j" + 1; jbe@218: END LOOP; jbe@218: END IF; jbe@218: EXIT WHEN "i" = "dimension_v"; jbe@218: "i" := "i" + 1; jbe@218: END LOOP; jbe@218: "i" := 1; jbe@218: LOOP jbe@218: "rank_ary"["winners_ary"["i"]] := "rank_v"; jbe@218: "done_v" := "done_v" + 1; jbe@218: EXIT WHEN "i" = array_upper("winners_ary", 1); jbe@218: "i" := "i" + 1; jbe@218: END LOOP; jbe@218: EXIT WHEN "done_v" = "dimension_v"; jbe@218: "rank_v" := "rank_v" + 1; jbe@218: END LOOP; jbe@218: -- write preliminary results: jbe@218: "i" := 1; jbe@218: FOR "initiative_id_v" IN jbe@218: SELECT "id" FROM "initiative" jbe@218: WHERE "issue_id" = "issue_id_p" AND "admitted" jbe@218: ORDER BY "id" jbe@218: LOOP jbe@218: UPDATE "initiative" SET jbe@218: "direct_majority" = jbe@218: CASE WHEN "policy_row"."direct_majority_strict" THEN jbe@218: "positive_votes" * "policy_row"."direct_majority_den" > jbe@218: "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes") jbe@218: ELSE jbe@218: "positive_votes" * "policy_row"."direct_majority_den" >= jbe@218: "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes") jbe@218: END jbe@218: AND "positive_votes" >= "policy_row"."direct_majority_positive" jbe@218: AND "issue_row"."voter_count"-"negative_votes" >= jbe@218: "policy_row"."direct_majority_non_negative", jbe@218: "indirect_majority" = jbe@218: CASE WHEN "policy_row"."indirect_majority_strict" THEN jbe@218: "positive_votes" * "policy_row"."indirect_majority_den" > jbe@218: "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes") jbe@218: ELSE jbe@218: "positive_votes" * "policy_row"."indirect_majority_den" >= jbe@218: "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes") jbe@218: END jbe@218: AND "positive_votes" >= "policy_row"."indirect_majority_positive" jbe@218: AND "issue_row"."voter_count"-"negative_votes" >= jbe@218: "policy_row"."indirect_majority_non_negative", jbe@218: "schulze_rank" = "rank_ary"["i"], jbe@218: "better_than_status_quo" = "rank_ary"["i"] < "rank_ary"["dimension_v"], jbe@218: "worse_than_status_quo" = "rank_ary"["i"] > "rank_ary"["dimension_v"], jbe@218: "multistage_majority" = "rank_ary"["i"] >= "rank_ary"["dimension_v"], jbe@218: "reverse_beat_path" = "matrix"["dimension_v"]["i"] >= 0, jbe@218: "eligible" = FALSE, jbe@218: "winner" = FALSE jbe@218: WHERE "id" = "initiative_id_v"; jbe@218: "i" := "i" + 1; jbe@218: END LOOP; jbe@218: IF "i" != "dimension_v" THEN jbe@218: RAISE EXCEPTION 'Wrong winner count (should not happen)'; jbe@218: END IF; jbe@218: -- take indirect majorities into account: jbe@218: LOOP jbe@218: UPDATE "initiative" SET "indirect_majority" = TRUE jbe@218: FROM ( jbe@218: SELECT "new_initiative"."id" AS "initiative_id" jbe@218: FROM "initiative" "old_initiative" jbe@218: JOIN "initiative" "new_initiative" jbe@218: ON "new_initiative"."issue_id" = "issue_id_p" jbe@218: AND "new_initiative"."indirect_majority" = FALSE jbe@218: JOIN "battle" "battle_win" jbe@218: ON "battle_win"."issue_id" = "issue_id_p" jbe@218: AND "battle_win"."winning_initiative_id" = "new_initiative"."id" jbe@218: AND "battle_win"."losing_initiative_id" = "old_initiative"."id" jbe@218: JOIN "battle" "battle_lose" jbe@218: ON "battle_lose"."issue_id" = "issue_id_p" jbe@218: AND "battle_lose"."losing_initiative_id" = "new_initiative"."id" jbe@218: AND "battle_lose"."winning_initiative_id" = "old_initiative"."id" jbe@218: WHERE "old_initiative"."issue_id" = "issue_id_p" jbe@218: AND "old_initiative"."indirect_majority" = TRUE jbe@218: AND CASE WHEN "policy_row"."indirect_majority_strict" THEN jbe@218: "battle_win"."count" * "policy_row"."indirect_majority_den" > jbe@218: "policy_row"."indirect_majority_num" * jbe@218: ("battle_win"."count"+"battle_lose"."count") jbe@218: ELSE jbe@218: "battle_win"."count" * "policy_row"."indirect_majority_den" >= jbe@218: "policy_row"."indirect_majority_num" * jbe@218: ("battle_win"."count"+"battle_lose"."count") jbe@218: END jbe@218: AND "battle_win"."count" >= "policy_row"."indirect_majority_positive" jbe@218: AND "issue_row"."voter_count"-"battle_lose"."count" >= jbe@218: "policy_row"."indirect_majority_non_negative" jbe@218: ) AS "subquery" jbe@218: WHERE "id" = "subquery"."initiative_id"; jbe@218: EXIT WHEN NOT FOUND; jbe@218: END LOOP; jbe@218: -- set "multistage_majority" for remaining matching initiatives: jbe@218: UPDATE "initiative" SET "multistage_majority" = TRUE jbe@218: FROM ( jbe@218: SELECT "losing_initiative"."id" AS "initiative_id" jbe@218: FROM "initiative" "losing_initiative" jbe@218: JOIN "initiative" "winning_initiative" jbe@218: ON "winning_initiative"."issue_id" = "issue_id_p" jbe@218: AND "winning_initiative"."admitted" jbe@218: JOIN "battle" "battle_win" jbe@218: ON "battle_win"."issue_id" = "issue_id_p" jbe@218: AND "battle_win"."winning_initiative_id" = "winning_initiative"."id" jbe@218: AND "battle_win"."losing_initiative_id" = "losing_initiative"."id" jbe@218: JOIN "battle" "battle_lose" jbe@218: ON "battle_lose"."issue_id" = "issue_id_p" jbe@218: AND "battle_lose"."losing_initiative_id" = "winning_initiative"."id" jbe@218: AND "battle_lose"."winning_initiative_id" = "losing_initiative"."id" jbe@218: WHERE "losing_initiative"."issue_id" = "issue_id_p" jbe@218: AND "losing_initiative"."admitted" jbe@218: AND "winning_initiative"."schulze_rank" < jbe@218: "losing_initiative"."schulze_rank" jbe@218: AND "battle_win"."count" > "battle_lose"."count" jbe@218: AND ( jbe@218: "battle_win"."count" > "winning_initiative"."positive_votes" OR jbe@218: "battle_lose"."count" < "losing_initiative"."negative_votes" ) jbe@218: ) AS "subquery" jbe@218: WHERE "id" = "subquery"."initiative_id"; jbe@218: -- mark eligible initiatives: jbe@218: UPDATE "initiative" SET "eligible" = TRUE jbe@218: WHERE "issue_id" = "issue_id_p" jbe@218: AND "initiative"."direct_majority" jbe@218: AND "initiative"."indirect_majority" jbe@218: AND "initiative"."better_than_status_quo" jbe@218: AND ( jbe@218: "policy_row"."no_multistage_majority" = FALSE OR jbe@218: "initiative"."multistage_majority" = FALSE ) jbe@218: AND ( jbe@218: "policy_row"."no_reverse_beat_path" = FALSE OR jbe@218: "initiative"."reverse_beat_path" = FALSE ); jbe@218: -- mark final winner: jbe@218: UPDATE "initiative" SET "winner" = TRUE jbe@218: FROM ( jbe@218: SELECT "id" AS "initiative_id" jbe@218: FROM "initiative" jbe@218: WHERE "issue_id" = "issue_id_p" AND "eligible" jbe@218: ORDER BY jbe@218: "schulze_rank", jbe@218: "vote_ratio"("positive_votes", "negative_votes"), jbe@218: "id" jbe@218: LIMIT 1 jbe@218: ) AS "subquery" jbe@218: WHERE "id" = "subquery"."initiative_id"; jbe@218: -- write (final) ranks: jbe@218: "rank_v" := 1; jbe@218: FOR "initiative_id_v" IN jbe@218: SELECT "id" jbe@218: FROM "initiative" jbe@218: WHERE "issue_id" = "issue_id_p" AND "admitted" jbe@218: ORDER BY jbe@218: "winner" DESC, jbe@218: "eligible" DESC, jbe@218: "schulze_rank", jbe@218: "vote_ratio"("positive_votes", "negative_votes"), jbe@218: "id" jbe@218: LOOP jbe@218: UPDATE "initiative" SET "rank" = "rank_v" jbe@218: WHERE "id" = "initiative_id_v"; jbe@218: "rank_v" := "rank_v" + 1; jbe@218: END LOOP; jbe@218: -- set schulze rank of status quo and mark issue as finished: jbe@218: UPDATE "issue" SET jbe@218: "status_quo_schulze_rank" = "rank_ary"["dimension_v"], jbe@218: "state" = jbe@218: CASE WHEN EXISTS ( jbe@218: SELECT NULL FROM "initiative" jbe@218: WHERE "issue_id" = "issue_id_p" AND "winner" jbe@218: ) THEN jbe@218: 'finished_with_winner'::"issue_state" jbe@218: ELSE jbe@218: 'finished_without_winner'::"issue_state" jbe@218: END, jbe@218: "ranks_available" = TRUE jbe@218: WHERE "id" = "issue_id_p"; jbe@218: RETURN; jbe@218: END; jbe@218: $$; jbe@218: jbe@218: -- set "eligible" to FALSE for all non-eligible jbe@218: -- but admitted initiatives of closed issues: jbe@218: UPDATE "initiative" SET "eligible" = FALSE WHERE "eligible" ISNULL AND "rank" NOTNULL; jbe@218: jbe@218: -- NOTE: "rank" ordering of initiatives of closed issues are jbe@218: -- not changed, as v2.0.2 should not be in productive use yet. jbe@218: jbe@218: COMMIT;