jbe@244: BEGIN; jbe@244: jbe@244: CREATE OR REPLACE VIEW "liquid_feedback_version" AS jbe@244: SELECT * FROM (VALUES ('2.0.9', 2, 0, 9)) jbe@244: AS "subquery"("string", "major", "minor", "revision"); jbe@244: jbe@249: -- Bugfix of error in update script to v2.0.0: jbe@249: ALTER TABLE "battle" ALTER COLUMN "winning_initiative_id" DROP NOT NULL; jbe@249: ALTER TABLE "battle" ALTER COLUMN "losing_initiative_id" DROP NOT NULL; jbe@249: jbe@248: CREATE OR REPLACE VIEW "unit_member_count" AS jbe@248: SELECT jbe@248: "unit"."id" AS "unit_id", jbe@248: count("member"."id") AS "member_count" jbe@248: FROM "unit" jbe@248: LEFT JOIN "privilege" jbe@248: ON "privilege"."unit_id" = "unit"."id" jbe@248: AND "privilege"."voting_right" jbe@248: LEFT JOIN "member" jbe@248: ON "member"."id" = "privilege"."member_id" jbe@248: AND "member"."active" jbe@248: GROUP BY "unit"."id"; jbe@248: jbe@244: COMMENT ON TYPE "delegation_chain_row" IS 'Type of rows returned by "delegation_chain" function'; jbe@244: jbe@244: CREATE FUNCTION "delegation_chain_for_closed_issue" jbe@244: ( "member_id_p" "member"."id"%TYPE, jbe@244: "issue_id_p" "issue"."id"%TYPE ) jbe@244: RETURNS SETOF "delegation_chain_row" jbe@244: LANGUAGE 'plpgsql' STABLE AS $$ jbe@244: DECLARE jbe@244: "output_row" "delegation_chain_row"; jbe@244: "direct_voter_row" "direct_voter"%ROWTYPE; jbe@244: "delegating_voter_row" "delegating_voter"%ROWTYPE; jbe@244: BEGIN jbe@244: "output_row"."index" := 0; jbe@244: "output_row"."member_id" := "member_id_p"; jbe@244: "output_row"."member_valid" := TRUE; jbe@244: "output_row"."participation" := FALSE; jbe@244: "output_row"."overridden" := FALSE; jbe@244: "output_row"."disabled_out" := FALSE; jbe@244: LOOP jbe@244: SELECT INTO "direct_voter_row" * FROM "direct_voter" jbe@244: WHERE "issue_id" = "issue_id_p" jbe@244: AND "member_id" = "output_row"."member_id"; jbe@244: IF "direct_voter_row"."member_id" NOTNULL THEN jbe@244: "output_row"."participation" := TRUE; jbe@244: "output_row"."scope_out" := NULL; jbe@244: "output_row"."disabled_out" := NULL; jbe@244: RETURN NEXT "output_row"; jbe@244: RETURN; jbe@244: END IF; jbe@244: SELECT INTO "delegating_voter_row" * FROM "delegating_voter" jbe@244: WHERE "issue_id" = "issue_id_p" jbe@244: AND "member_id" = "output_row"."member_id"; jbe@244: IF "delegating_voter_row"."member_id" ISNULL THEN jbe@244: RETURN; jbe@244: END IF; jbe@244: "output_row"."scope_out" := "delegating_voter_row"."scope"; jbe@244: RETURN NEXT "output_row"; jbe@244: "output_row"."member_id" := "delegating_voter_row"."delegate_member_ids"[1]; jbe@244: "output_row"."scope_in" := "output_row"."scope_out"; jbe@244: END LOOP; jbe@244: END; jbe@244: $$; jbe@244: jbe@244: COMMENT ON FUNCTION "delegation_chain_for_closed_issue" jbe@244: ( "member"."id"%TYPE, jbe@244: "member"."id"%TYPE ) jbe@244: IS 'Helper function for "delegation_chain" function, handling the special case of closed issues after voting'; jbe@244: jbe@244: DROP FUNCTION "delegation_chain" jbe@244: ( "member"."id"%TYPE, jbe@244: "unit"."id"%TYPE, jbe@244: "area"."id"%TYPE, jbe@244: "issue"."id"%TYPE ); jbe@244: jbe@244: DROP FUNCTION "delegation_chain" jbe@244: ( "member"."id"%TYPE, jbe@244: "unit"."id"%TYPE, jbe@244: "area"."id"%TYPE, jbe@244: "issue"."id"%TYPE, jbe@244: "member"."id"%TYPE ); jbe@244: jbe@244: CREATE FUNCTION "delegation_chain" jbe@244: ( "member_id_p" "member"."id"%TYPE, jbe@244: "unit_id_p" "unit"."id"%TYPE, jbe@244: "area_id_p" "area"."id"%TYPE, jbe@244: "issue_id_p" "issue"."id"%TYPE, jbe@244: "simulate_trustee_id_p" "member"."id"%TYPE DEFAULT NULL ) jbe@244: RETURNS SETOF "delegation_chain_row" jbe@244: LANGUAGE 'plpgsql' STABLE AS $$ jbe@244: DECLARE jbe@244: "scope_v" "delegation_scope"; jbe@244: "unit_id_v" "unit"."id"%TYPE; jbe@244: "area_id_v" "area"."id"%TYPE; jbe@244: "issue_row" "issue"%ROWTYPE; jbe@244: "visited_member_ids" INT4[]; -- "member"."id"%TYPE[] jbe@244: "loop_member_id_v" "member"."id"%TYPE; jbe@244: "output_row" "delegation_chain_row"; jbe@244: "output_rows" "delegation_chain_row"[]; jbe@244: "delegation_row" "delegation"%ROWTYPE; jbe@244: "row_count" INT4; jbe@244: "i" INT4; jbe@244: "loop_v" BOOLEAN; jbe@244: BEGIN jbe@244: IF jbe@244: "unit_id_p" NOTNULL AND jbe@244: "area_id_p" ISNULL AND jbe@244: "issue_id_p" ISNULL jbe@244: THEN jbe@244: "scope_v" := 'unit'; jbe@244: "unit_id_v" := "unit_id_p"; jbe@244: ELSIF jbe@244: "unit_id_p" ISNULL AND jbe@244: "area_id_p" NOTNULL AND jbe@244: "issue_id_p" ISNULL jbe@244: THEN jbe@244: "scope_v" := 'area'; jbe@244: "area_id_v" := "area_id_p"; jbe@244: SELECT "unit_id" INTO "unit_id_v" jbe@244: FROM "area" WHERE "id" = "area_id_v"; jbe@244: ELSIF jbe@244: "unit_id_p" ISNULL AND jbe@244: "area_id_p" ISNULL AND jbe@244: "issue_id_p" NOTNULL jbe@244: THEN jbe@244: SELECT INTO "issue_row" * FROM "issue" WHERE "id" = "issue_id_p"; jbe@244: IF "issue_row"."id" ISNULL THEN jbe@244: RETURN; jbe@244: END IF; jbe@244: IF "issue_row"."closed" NOTNULL THEN jbe@244: IF "simulate_trustee_id_p" NOTNULL THEN jbe@244: RAISE EXCEPTION 'Tried to simulate delegation chain for closed issue.'; jbe@244: END IF; jbe@244: FOR "output_row" IN jbe@244: SELECT * FROM jbe@244: "delegation_chain_for_closed_issue"("member_id_p", "issue_id_p") jbe@244: LOOP jbe@244: RETURN NEXT "output_row"; jbe@244: END LOOP; jbe@244: RETURN; jbe@244: END IF; jbe@244: "scope_v" := 'issue'; jbe@244: SELECT "area_id" INTO "area_id_v" jbe@244: FROM "issue" WHERE "id" = "issue_id_p"; jbe@244: SELECT "unit_id" INTO "unit_id_v" jbe@244: FROM "area" WHERE "id" = "area_id_v"; jbe@244: ELSE jbe@244: RAISE EXCEPTION 'Exactly one of unit_id_p, area_id_p, or issue_id_p must be NOTNULL.'; jbe@244: END IF; jbe@244: "visited_member_ids" := '{}'; jbe@244: "loop_member_id_v" := NULL; jbe@244: "output_rows" := '{}'; jbe@244: "output_row"."index" := 0; jbe@244: "output_row"."member_id" := "member_id_p"; jbe@244: "output_row"."member_valid" := TRUE; jbe@244: "output_row"."participation" := FALSE; jbe@244: "output_row"."overridden" := FALSE; jbe@244: "output_row"."disabled_out" := FALSE; jbe@244: "output_row"."scope_out" := NULL; jbe@244: LOOP jbe@244: IF "visited_member_ids" @> ARRAY["output_row"."member_id"] THEN jbe@244: "loop_member_id_v" := "output_row"."member_id"; jbe@244: ELSE jbe@244: "visited_member_ids" := jbe@244: "visited_member_ids" || "output_row"."member_id"; jbe@244: END IF; jbe@244: IF "output_row"."participation" ISNULL THEN jbe@244: "output_row"."overridden" := NULL; jbe@244: ELSIF "output_row"."participation" THEN jbe@244: "output_row"."overridden" := TRUE; jbe@244: END IF; jbe@244: "output_row"."scope_in" := "output_row"."scope_out"; jbe@244: IF EXISTS ( jbe@244: SELECT NULL FROM "member" JOIN "privilege" jbe@244: ON "privilege"."member_id" = "member"."id" jbe@244: AND "privilege"."unit_id" = "unit_id_v" jbe@244: WHERE "id" = "output_row"."member_id" jbe@244: AND "member"."active" AND "privilege"."voting_right" jbe@244: ) THEN jbe@244: IF "scope_v" = 'unit' THEN jbe@244: SELECT * INTO "delegation_row" FROM "delegation" jbe@244: WHERE "truster_id" = "output_row"."member_id" jbe@244: AND "unit_id" = "unit_id_v"; jbe@244: ELSIF "scope_v" = 'area' THEN jbe@244: "output_row"."participation" := EXISTS ( jbe@244: SELECT NULL FROM "membership" jbe@244: WHERE "area_id" = "area_id_p" jbe@244: AND "member_id" = "output_row"."member_id" jbe@244: ); jbe@244: SELECT * INTO "delegation_row" FROM "delegation" jbe@244: WHERE "truster_id" = "output_row"."member_id" jbe@244: AND ( jbe@244: "unit_id" = "unit_id_v" OR jbe@244: "area_id" = "area_id_v" jbe@244: ) jbe@244: ORDER BY "scope" DESC; jbe@244: ELSIF "scope_v" = 'issue' THEN jbe@244: IF "issue_row"."fully_frozen" ISNULL THEN jbe@244: "output_row"."participation" := EXISTS ( jbe@244: SELECT NULL FROM "interest" jbe@244: WHERE "issue_id" = "issue_id_p" jbe@244: AND "member_id" = "output_row"."member_id" jbe@244: ); jbe@244: ELSE jbe@244: IF "output_row"."member_id" = "member_id_p" THEN jbe@244: "output_row"."participation" := EXISTS ( jbe@244: SELECT NULL FROM "direct_voter" jbe@244: WHERE "issue_id" = "issue_id_p" jbe@244: AND "member_id" = "output_row"."member_id" jbe@244: ); jbe@244: ELSE jbe@244: "output_row"."participation" := NULL; jbe@244: END IF; jbe@244: END IF; jbe@244: SELECT * INTO "delegation_row" FROM "delegation" jbe@244: WHERE "truster_id" = "output_row"."member_id" jbe@244: AND ( jbe@244: "unit_id" = "unit_id_v" OR jbe@244: "area_id" = "area_id_v" OR jbe@244: "issue_id" = "issue_id_p" jbe@244: ) jbe@244: ORDER BY "scope" DESC; jbe@244: END IF; jbe@244: ELSE jbe@244: "output_row"."member_valid" := FALSE; jbe@244: "output_row"."participation" := FALSE; jbe@244: "output_row"."scope_out" := NULL; jbe@244: "delegation_row" := ROW(NULL); jbe@244: END IF; jbe@244: IF jbe@244: "output_row"."member_id" = "member_id_p" AND jbe@244: "simulate_trustee_id_p" NOTNULL jbe@244: THEN jbe@244: "output_row"."scope_out" := "scope_v"; jbe@244: "output_rows" := "output_rows" || "output_row"; jbe@244: "output_row"."member_id" := "simulate_trustee_id_p"; jbe@244: ELSIF "delegation_row"."trustee_id" NOTNULL THEN jbe@244: "output_row"."scope_out" := "delegation_row"."scope"; jbe@244: "output_rows" := "output_rows" || "output_row"; jbe@244: "output_row"."member_id" := "delegation_row"."trustee_id"; jbe@244: ELSIF "delegation_row"."scope" NOTNULL THEN jbe@244: "output_row"."scope_out" := "delegation_row"."scope"; jbe@244: "output_row"."disabled_out" := TRUE; jbe@244: "output_rows" := "output_rows" || "output_row"; jbe@244: EXIT; jbe@244: ELSE jbe@244: "output_row"."scope_out" := NULL; jbe@244: "output_rows" := "output_rows" || "output_row"; jbe@244: EXIT; jbe@244: END IF; jbe@244: EXIT WHEN "loop_member_id_v" NOTNULL; jbe@244: "output_row"."index" := "output_row"."index" + 1; jbe@244: END LOOP; jbe@244: "row_count" := array_upper("output_rows", 1); jbe@244: "i" := 1; jbe@244: "loop_v" := FALSE; jbe@244: LOOP jbe@244: "output_row" := "output_rows"["i"]; jbe@244: EXIT WHEN "output_row" ISNULL; -- NOTE: ISNULL and NOT ... NOTNULL produce different results! jbe@244: IF "loop_v" THEN jbe@244: IF "i" + 1 = "row_count" THEN jbe@244: "output_row"."loop" := 'last'; jbe@244: ELSIF "i" = "row_count" THEN jbe@244: "output_row"."loop" := 'repetition'; jbe@244: ELSE jbe@244: "output_row"."loop" := 'intermediate'; jbe@244: END IF; jbe@244: ELSIF "output_row"."member_id" = "loop_member_id_v" THEN jbe@244: "output_row"."loop" := 'first'; jbe@244: "loop_v" := TRUE; jbe@244: END IF; jbe@244: IF "scope_v" = 'unit' THEN jbe@244: "output_row"."participation" := NULL; jbe@244: END IF; jbe@244: RETURN NEXT "output_row"; jbe@244: "i" := "i" + 1; jbe@244: END LOOP; jbe@244: RETURN; jbe@244: END; jbe@244: $$; jbe@244: jbe@244: COMMENT ON FUNCTION "delegation_chain" jbe@244: ( "member"."id"%TYPE, jbe@244: "unit"."id"%TYPE, jbe@244: "area"."id"%TYPE, jbe@244: "issue"."id"%TYPE, jbe@244: "member"."id"%TYPE ) jbe@244: IS 'Shows a delegation chain for unit, area, or issue; See "delegation_chain_row" type for more information'; jbe@244: jbe@244: CREATE TYPE "delegation_info_loop_type" AS ENUM jbe@244: ('own', 'first', 'first_ellipsis', 'other', 'other_ellipsis'); jbe@244: jbe@244: COMMENT ON TYPE "delegation_info_loop_type" IS 'Type of "delegation_loop" in "delegation_info_type"; ''own'' means loop to self, ''first'' means loop to first trustee, ''first_ellipsis'' means loop to ellipsis after first trustee, ''other'' means loop to other trustee, ''other_ellipsis'' means loop to ellipsis after other trustee'''; jbe@244: jbe@244: CREATE TYPE "delegation_info_type" AS ( jbe@244: "own_participation" BOOLEAN, jbe@244: "own_delegation_scope" "delegation_scope", jbe@244: "first_trustee_id" INT4, jbe@244: "first_trustee_participation" BOOLEAN, jbe@244: "first_trustee_ellipsis" BOOLEAN, jbe@244: "other_trustee_id" INT4, jbe@244: "other_trustee_participation" BOOLEAN, jbe@244: "other_trustee_ellipsis" BOOLEAN, jbe@244: "delegation_loop" "delegation_info_loop_type"); jbe@244: jbe@244: COMMENT ON TYPE "delegation_info_type" IS 'Type of result returned by "delegation_info" function; For meaning of "participation" check comment on "delegation_chain_row" type'; jbe@244: jbe@244: COMMENT ON COLUMN "delegation_info_type"."own_participation" IS 'Member is directly participating'; jbe@244: COMMENT ON COLUMN "delegation_info_type"."own_delegation_scope" IS 'Delegation scope of member'; jbe@244: COMMENT ON COLUMN "delegation_info_type"."first_trustee_id" IS 'Direct trustee of member'; jbe@244: COMMENT ON COLUMN "delegation_info_type"."first_trustee_participation" IS 'Direct trustee of member is participating'; jbe@244: COMMENT ON COLUMN "delegation_info_type"."first_trustee_ellipsis" IS 'Ellipsis in delegation chain after "first_trustee"'; jbe@244: COMMENT ON COLUMN "delegation_info_type"."other_trustee_id" IS 'Another relevant trustee (due to participation)'; jbe@244: COMMENT ON COLUMN "delegation_info_type"."other_trustee_participation" IS 'Another trustee is participating (redundant field: if "other_trustee_id" is set, then "other_trustee_participation" is always TRUE, else "other_trustee_participation" is NULL)'; jbe@244: COMMENT ON COLUMN "delegation_info_type"."other_trustee_ellipsis" IS 'Ellipsis in delegation chain after "other_trustee"'; jbe@244: COMMENT ON COLUMN "delegation_info_type"."delegation_loop" IS 'Non-NULL value, if delegation chain contains a circle; See comment on "delegation_info_loop_type" for details'; jbe@244: jbe@244: CREATE FUNCTION "delegation_info" jbe@244: ( "member_id_p" "member"."id"%TYPE, jbe@244: "unit_id_p" "unit"."id"%TYPE, jbe@244: "area_id_p" "area"."id"%TYPE, jbe@244: "issue_id_p" "issue"."id"%TYPE, jbe@244: "simulate_trustee_id_p" "member"."id"%TYPE DEFAULT NULL ) jbe@244: RETURNS "delegation_info_type" jbe@244: LANGUAGE 'plpgsql' STABLE AS $$ jbe@244: DECLARE jbe@244: "current_row" "delegation_chain_row"; jbe@244: "result" "delegation_info_type"; jbe@244: BEGIN jbe@244: "result"."own_participation" := FALSE; jbe@244: FOR "current_row" IN jbe@244: SELECT * FROM "delegation_chain"( jbe@244: "member_id_p", jbe@244: "unit_id_p", "area_id_p", "issue_id_p", jbe@244: "simulate_trustee_id_p") jbe@244: LOOP jbe@244: IF "current_row"."member_id" = "member_id_p" THEN jbe@244: "result"."own_participation" := "current_row"."participation"; jbe@244: "result"."own_delegation_scope" := "current_row"."scope_out"; jbe@244: IF "current_row"."loop" = 'first' THEN jbe@244: "result"."delegation_loop" := 'own'; jbe@244: END IF; jbe@244: ELSIF jbe@244: "current_row"."member_valid" AND jbe@244: ( "current_row"."loop" ISNULL OR jbe@244: "current_row"."loop" != 'repetition' ) jbe@244: THEN jbe@244: IF "result"."first_trustee_id" ISNULL THEN jbe@244: "result"."first_trustee_id" := "current_row"."member_id"; jbe@244: "result"."first_trustee_participation" := "current_row"."participation"; jbe@244: "result"."first_trustee_ellipsis" := FALSE; jbe@244: IF "current_row"."loop" = 'first' THEN jbe@244: "result"."delegation_loop" := 'first'; jbe@244: END IF; jbe@244: ELSIF "result"."other_trustee_id" ISNULL THEN jbe@247: IF "current_row"."participation" AND NOT "current_row"."overridden" THEN jbe@244: "result"."other_trustee_id" := "current_row"."member_id"; jbe@244: "result"."other_trustee_participation" := TRUE; jbe@244: "result"."other_trustee_ellipsis" := FALSE; jbe@244: IF "current_row"."loop" = 'first' THEN jbe@244: "result"."delegation_loop" := 'other'; jbe@244: END IF; jbe@244: ELSE jbe@244: "result"."first_trustee_ellipsis" := TRUE; jbe@244: IF "current_row"."loop" = 'first' THEN jbe@244: "result"."delegation_loop" := 'first_ellipsis'; jbe@244: END IF; jbe@244: END IF; jbe@244: ELSE jbe@244: "result"."other_trustee_ellipsis" := TRUE; jbe@244: IF "current_row"."loop" = 'first' THEN jbe@244: "result"."delegation_loop" := 'other_ellipsis'; jbe@244: END IF; jbe@244: END IF; jbe@244: END IF; jbe@244: END LOOP; jbe@244: RETURN "result"; jbe@244: END; jbe@244: $$; jbe@244: jbe@244: COMMENT ON FUNCTION "delegation_info" jbe@244: ( "member"."id"%TYPE, jbe@244: "unit"."id"%TYPE, jbe@244: "area"."id"%TYPE, jbe@244: "issue"."id"%TYPE, jbe@244: "member"."id"%TYPE ) jbe@244: IS 'Notable information about a delegation chain for unit, area, or issue; See "delegation_info_type" for more information'; jbe@244: jbe@250: CREATE OR REPLACE FUNCTION "calculate_ranks"("issue_id_p" "issue"."id"%TYPE) jbe@250: RETURNS VOID jbe@250: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@250: DECLARE jbe@250: "issue_row" "issue"%ROWTYPE; jbe@250: "policy_row" "policy"%ROWTYPE; jbe@250: "dimension_v" INTEGER; jbe@250: "vote_matrix" INT4[][]; -- absolute votes jbe@250: "matrix" INT8[][]; -- defeat strength / best paths jbe@250: "i" INTEGER; jbe@250: "j" INTEGER; jbe@250: "k" INTEGER; jbe@250: "battle_row" "battle"%ROWTYPE; jbe@250: "rank_ary" INT4[]; jbe@250: "rank_v" INT4; jbe@250: "done_v" INTEGER; jbe@250: "winners_ary" INTEGER[]; jbe@250: "initiative_id_v" "initiative"."id"%TYPE; jbe@250: BEGIN jbe@250: SELECT * INTO "issue_row" jbe@250: FROM "issue" WHERE "id" = "issue_id_p" jbe@250: FOR UPDATE; jbe@250: SELECT * INTO "policy_row" jbe@250: FROM "policy" WHERE "id" = "issue_row"."policy_id"; jbe@250: SELECT count(1) INTO "dimension_v" jbe@250: FROM "battle_participant" WHERE "issue_id" = "issue_id_p"; jbe@250: -- Create "vote_matrix" with absolute number of votes in pairwise jbe@250: -- comparison: jbe@250: "vote_matrix" := array_fill(NULL::INT4, ARRAY["dimension_v", "dimension_v"]); jbe@250: "i" := 1; jbe@250: "j" := 2; jbe@250: FOR "battle_row" IN jbe@250: SELECT * FROM "battle" WHERE "issue_id" = "issue_id_p" jbe@250: ORDER BY jbe@250: "winning_initiative_id" NULLS LAST, jbe@250: "losing_initiative_id" NULLS LAST jbe@250: LOOP jbe@250: "vote_matrix"["i"]["j"] := "battle_row"."count"; jbe@250: IF "j" = "dimension_v" THEN jbe@250: "i" := "i" + 1; jbe@250: "j" := 1; jbe@250: ELSE jbe@250: "j" := "j" + 1; jbe@250: IF "j" = "i" THEN jbe@250: "j" := "j" + 1; jbe@250: END IF; jbe@250: END IF; jbe@250: END LOOP; jbe@250: IF "i" != "dimension_v" OR "j" != "dimension_v" + 1 THEN jbe@250: RAISE EXCEPTION 'Wrong battle count (should not happen)'; jbe@250: END IF; jbe@250: -- Store defeat strengths in "matrix" using "defeat_strength" jbe@250: -- function: jbe@250: "matrix" := array_fill(NULL::INT8, ARRAY["dimension_v", "dimension_v"]); jbe@250: "i" := 1; jbe@250: LOOP jbe@250: "j" := 1; jbe@250: LOOP jbe@250: IF "i" != "j" THEN jbe@250: "matrix"["i"]["j"] := "defeat_strength"( jbe@250: "vote_matrix"["i"]["j"], jbe@250: "vote_matrix"["j"]["i"] jbe@250: ); jbe@250: END IF; jbe@250: EXIT WHEN "j" = "dimension_v"; jbe@250: "j" := "j" + 1; jbe@250: END LOOP; jbe@250: EXIT WHEN "i" = "dimension_v"; jbe@250: "i" := "i" + 1; jbe@250: END LOOP; jbe@250: -- Find best paths: jbe@250: "i" := 1; jbe@250: LOOP jbe@250: "j" := 1; jbe@250: LOOP jbe@250: IF "i" != "j" THEN jbe@250: "k" := 1; jbe@250: LOOP jbe@250: IF "i" != "k" AND "j" != "k" THEN jbe@250: IF "matrix"["j"]["i"] < "matrix"["i"]["k"] THEN jbe@250: IF "matrix"["j"]["i"] > "matrix"["j"]["k"] THEN jbe@250: "matrix"["j"]["k"] := "matrix"["j"]["i"]; jbe@250: END IF; jbe@250: ELSE jbe@250: IF "matrix"["i"]["k"] > "matrix"["j"]["k"] THEN jbe@250: "matrix"["j"]["k"] := "matrix"["i"]["k"]; jbe@250: END IF; jbe@250: END IF; jbe@250: END IF; jbe@250: EXIT WHEN "k" = "dimension_v"; jbe@250: "k" := "k" + 1; jbe@250: END LOOP; jbe@250: END IF; jbe@250: EXIT WHEN "j" = "dimension_v"; jbe@250: "j" := "j" + 1; jbe@250: END LOOP; jbe@250: EXIT WHEN "i" = "dimension_v"; jbe@250: "i" := "i" + 1; jbe@250: END LOOP; jbe@250: -- Determine order of winners: jbe@250: "rank_ary" := array_fill(NULL::INT4, ARRAY["dimension_v"]); jbe@250: "rank_v" := 1; jbe@250: "done_v" := 0; jbe@250: LOOP jbe@250: "winners_ary" := '{}'; jbe@250: "i" := 1; jbe@250: LOOP jbe@250: IF "rank_ary"["i"] ISNULL THEN jbe@250: "j" := 1; jbe@250: LOOP jbe@250: IF jbe@250: "i" != "j" AND jbe@250: "rank_ary"["j"] ISNULL AND jbe@250: "matrix"["j"]["i"] > "matrix"["i"]["j"] jbe@250: THEN jbe@250: -- someone else is better jbe@250: EXIT; jbe@250: END IF; jbe@250: IF "j" = "dimension_v" THEN jbe@250: -- noone is better jbe@250: "winners_ary" := "winners_ary" || "i"; jbe@250: EXIT; jbe@250: END IF; jbe@250: "j" := "j" + 1; jbe@250: END LOOP; jbe@250: END IF; jbe@250: EXIT WHEN "i" = "dimension_v"; jbe@250: "i" := "i" + 1; jbe@250: END LOOP; jbe@250: "i" := 1; jbe@250: LOOP jbe@250: "rank_ary"["winners_ary"["i"]] := "rank_v"; jbe@250: "done_v" := "done_v" + 1; jbe@250: EXIT WHEN "i" = array_upper("winners_ary", 1); jbe@250: "i" := "i" + 1; jbe@250: END LOOP; jbe@250: EXIT WHEN "done_v" = "dimension_v"; jbe@250: "rank_v" := "rank_v" + 1; jbe@250: END LOOP; jbe@250: -- write preliminary results: jbe@250: "i" := 1; jbe@250: FOR "initiative_id_v" IN jbe@250: SELECT "id" FROM "initiative" jbe@250: WHERE "issue_id" = "issue_id_p" AND "admitted" jbe@250: ORDER BY "id" jbe@250: LOOP jbe@250: UPDATE "initiative" SET jbe@250: "direct_majority" = jbe@250: CASE WHEN "policy_row"."direct_majority_strict" THEN jbe@250: "positive_votes" * "policy_row"."direct_majority_den" > jbe@250: "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes") jbe@250: ELSE jbe@250: "positive_votes" * "policy_row"."direct_majority_den" >= jbe@250: "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes") jbe@250: END jbe@250: AND "positive_votes" >= "policy_row"."direct_majority_positive" jbe@250: AND "issue_row"."voter_count"-"negative_votes" >= jbe@250: "policy_row"."direct_majority_non_negative", jbe@250: "indirect_majority" = jbe@250: CASE WHEN "policy_row"."indirect_majority_strict" THEN jbe@250: "positive_votes" * "policy_row"."indirect_majority_den" > jbe@250: "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes") jbe@250: ELSE jbe@250: "positive_votes" * "policy_row"."indirect_majority_den" >= jbe@250: "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes") jbe@250: END jbe@250: AND "positive_votes" >= "policy_row"."indirect_majority_positive" jbe@250: AND "issue_row"."voter_count"-"negative_votes" >= jbe@250: "policy_row"."indirect_majority_non_negative", jbe@250: "schulze_rank" = "rank_ary"["i"], jbe@250: "better_than_status_quo" = "rank_ary"["i"] < "rank_ary"["dimension_v"], jbe@250: "worse_than_status_quo" = "rank_ary"["i"] > "rank_ary"["dimension_v"], jbe@250: "multistage_majority" = "rank_ary"["i"] >= "rank_ary"["dimension_v"], jbe@250: "reverse_beat_path" = "matrix"["dimension_v"]["i"] >= 0, jbe@250: "eligible" = FALSE, jbe@250: "winner" = FALSE, jbe@250: "rank" = NULL -- NOTE: in cases of manual reset of issue state jbe@250: WHERE "id" = "initiative_id_v"; jbe@250: "i" := "i" + 1; jbe@250: END LOOP; jbe@250: IF "i" != "dimension_v" THEN jbe@250: RAISE EXCEPTION 'Wrong winner count (should not happen)'; jbe@250: END IF; jbe@250: -- take indirect majorities into account: jbe@250: LOOP jbe@250: UPDATE "initiative" SET "indirect_majority" = TRUE jbe@250: FROM ( jbe@250: SELECT "new_initiative"."id" AS "initiative_id" jbe@250: FROM "initiative" "old_initiative" jbe@250: JOIN "initiative" "new_initiative" jbe@250: ON "new_initiative"."issue_id" = "issue_id_p" jbe@250: AND "new_initiative"."indirect_majority" = FALSE jbe@250: JOIN "battle" "battle_win" jbe@250: ON "battle_win"."issue_id" = "issue_id_p" jbe@250: AND "battle_win"."winning_initiative_id" = "new_initiative"."id" jbe@250: AND "battle_win"."losing_initiative_id" = "old_initiative"."id" jbe@250: JOIN "battle" "battle_lose" jbe@250: ON "battle_lose"."issue_id" = "issue_id_p" jbe@250: AND "battle_lose"."losing_initiative_id" = "new_initiative"."id" jbe@250: AND "battle_lose"."winning_initiative_id" = "old_initiative"."id" jbe@250: WHERE "old_initiative"."issue_id" = "issue_id_p" jbe@250: AND "old_initiative"."indirect_majority" = TRUE jbe@250: AND CASE WHEN "policy_row"."indirect_majority_strict" THEN jbe@250: "battle_win"."count" * "policy_row"."indirect_majority_den" > jbe@250: "policy_row"."indirect_majority_num" * jbe@250: ("battle_win"."count"+"battle_lose"."count") jbe@250: ELSE jbe@250: "battle_win"."count" * "policy_row"."indirect_majority_den" >= jbe@250: "policy_row"."indirect_majority_num" * jbe@250: ("battle_win"."count"+"battle_lose"."count") jbe@250: END jbe@250: AND "battle_win"."count" >= "policy_row"."indirect_majority_positive" jbe@250: AND "issue_row"."voter_count"-"battle_lose"."count" >= jbe@250: "policy_row"."indirect_majority_non_negative" jbe@250: ) AS "subquery" jbe@250: WHERE "id" = "subquery"."initiative_id"; jbe@250: EXIT WHEN NOT FOUND; jbe@250: END LOOP; jbe@250: -- set "multistage_majority" for remaining matching initiatives: jbe@250: UPDATE "initiative" SET "multistage_majority" = TRUE jbe@250: FROM ( jbe@250: SELECT "losing_initiative"."id" AS "initiative_id" jbe@250: FROM "initiative" "losing_initiative" jbe@250: JOIN "initiative" "winning_initiative" jbe@250: ON "winning_initiative"."issue_id" = "issue_id_p" jbe@250: AND "winning_initiative"."admitted" jbe@250: JOIN "battle" "battle_win" jbe@250: ON "battle_win"."issue_id" = "issue_id_p" jbe@250: AND "battle_win"."winning_initiative_id" = "winning_initiative"."id" jbe@250: AND "battle_win"."losing_initiative_id" = "losing_initiative"."id" jbe@250: JOIN "battle" "battle_lose" jbe@250: ON "battle_lose"."issue_id" = "issue_id_p" jbe@250: AND "battle_lose"."losing_initiative_id" = "winning_initiative"."id" jbe@250: AND "battle_lose"."winning_initiative_id" = "losing_initiative"."id" jbe@250: WHERE "losing_initiative"."issue_id" = "issue_id_p" jbe@250: AND "losing_initiative"."admitted" jbe@250: AND "winning_initiative"."schulze_rank" < jbe@250: "losing_initiative"."schulze_rank" jbe@250: AND "battle_win"."count" > "battle_lose"."count" jbe@250: AND ( jbe@250: "battle_win"."count" > "winning_initiative"."positive_votes" OR jbe@250: "battle_lose"."count" < "losing_initiative"."negative_votes" ) jbe@250: ) AS "subquery" jbe@250: WHERE "id" = "subquery"."initiative_id"; jbe@250: -- mark eligible initiatives: jbe@250: UPDATE "initiative" SET "eligible" = TRUE jbe@250: WHERE "issue_id" = "issue_id_p" jbe@250: AND "initiative"."direct_majority" jbe@250: AND "initiative"."indirect_majority" jbe@250: AND "initiative"."better_than_status_quo" jbe@250: AND ( jbe@250: "policy_row"."no_multistage_majority" = FALSE OR jbe@250: "initiative"."multistage_majority" = FALSE ) jbe@250: AND ( jbe@250: "policy_row"."no_reverse_beat_path" = FALSE OR jbe@250: "initiative"."reverse_beat_path" = FALSE ); jbe@250: -- mark final winner: jbe@250: UPDATE "initiative" SET "winner" = TRUE jbe@250: FROM ( jbe@250: SELECT "id" AS "initiative_id" jbe@250: FROM "initiative" jbe@250: WHERE "issue_id" = "issue_id_p" AND "eligible" jbe@250: ORDER BY jbe@250: "schulze_rank", jbe@250: "vote_ratio"("positive_votes", "negative_votes"), jbe@250: "id" jbe@250: LIMIT 1 jbe@250: ) AS "subquery" jbe@250: WHERE "id" = "subquery"."initiative_id"; jbe@250: -- write (final) ranks: jbe@250: "rank_v" := 1; jbe@250: FOR "initiative_id_v" IN jbe@250: SELECT "id" jbe@250: FROM "initiative" jbe@250: WHERE "issue_id" = "issue_id_p" AND "admitted" jbe@250: ORDER BY jbe@250: "winner" DESC, jbe@250: "eligible" DESC, jbe@250: "schulze_rank", jbe@250: "vote_ratio"("positive_votes", "negative_votes"), jbe@250: "id" jbe@250: LOOP jbe@250: UPDATE "initiative" SET "rank" = "rank_v" jbe@250: WHERE "id" = "initiative_id_v"; jbe@250: "rank_v" := "rank_v" + 1; jbe@250: END LOOP; jbe@250: -- set schulze rank of status quo and mark issue as finished: jbe@250: UPDATE "issue" SET jbe@250: "status_quo_schulze_rank" = "rank_ary"["dimension_v"], jbe@250: "state" = jbe@250: CASE WHEN EXISTS ( jbe@250: SELECT NULL FROM "initiative" jbe@250: WHERE "issue_id" = "issue_id_p" AND "winner" jbe@250: ) THEN jbe@250: 'finished_with_winner'::"issue_state" jbe@250: ELSE jbe@250: 'finished_without_winner'::"issue_state" jbe@250: END, jbe@250: "ranks_available" = TRUE jbe@250: WHERE "id" = "issue_id_p"; jbe@250: RETURN; jbe@250: END; jbe@250: $$; jbe@250: jbe@244: COMMIT;