liquid_feedback_core

changeset 412:399dc1a86398 v3.0.0

Marked version as v3.0.0
author jbe
date Fri Jan 31 12:46:11 2014 +0100 (2014-01-31)
parents 44a07d8f1bb4
children e024c50cfe3d
files LICENSE core.sql update/core-update.v2.2.5-v2.2.6.sql update/core-update.v2.2.5-v3.0.0.sql
line diff
     1.1 --- a/LICENSE	Mon Dec 23 20:22:32 2013 +0100
     1.2 +++ b/LICENSE	Fri Jan 31 12:46:11 2014 +0100
     1.3 @@ -1,4 +1,4 @@
     1.4 -Copyright (c) 2009-2013 Public Software Group e. V., Berlin, Germany
     1.5 +Copyright (c) 2009-2014 Public Software Group e. V., Berlin, Germany
     1.6  
     1.7  Permission is hereby granted, free of charge, to any person obtaining a
     1.8  copy of this software and associated documentation files (the "Software"),
     2.1 --- a/core.sql	Mon Dec 23 20:22:32 2013 +0100
     2.2 +++ b/core.sql	Fri Jan 31 12:46:11 2014 +0100
     2.3 @@ -7,7 +7,7 @@
     2.4  BEGIN;
     2.5  
     2.6  CREATE VIEW "liquid_feedback_version" AS
     2.7 -  SELECT * FROM (VALUES ('2.2.6', 2, 2, 6))
     2.8 +  SELECT * FROM (VALUES ('3.0.0', 3, 0, 0))
     2.9    AS "subquery"("string", "major", "minor", "revision");
    2.10  
    2.11  
     3.1 --- a/update/core-update.v2.2.5-v2.2.6.sql	Mon Dec 23 20:22:32 2013 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,339 +0,0 @@
     3.4 -BEGIN;
     3.5 -
     3.6 -CREATE OR REPLACE VIEW "liquid_feedback_version" AS
     3.7 -  SELECT * FROM (VALUES ('2.2.6', 2, 2, 6))
     3.8 -  AS "subquery"("string", "major", "minor", "revision");
     3.9 -
    3.10 -CREATE TABLE "issue_order_in_admission_state" (
    3.11 -        "id"                    INT8            PRIMARY KEY, --REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
    3.12 -        "order_in_area"         INT4,
    3.13 -        "order_in_unit"         INT4 );
    3.14 -
    3.15 -COMMENT ON TABLE "issue_order_in_admission_state" IS 'Ordering information for issues that are not stored in the "issue" table to avoid locking of multiple issues at once; Filled/updated by "lf_update_issue_order"';
    3.16 -
    3.17 -COMMENT ON COLUMN "issue_order_in_admission_state"."id"            IS 'References "issue" ("id") but has no referential integrity trigger associated, due to performance/locking issues';
    3.18 -COMMENT ON COLUMN "issue_order_in_admission_state"."order_in_area" IS 'Order of issues in admission state within a single area; NULL values sort last';
    3.19 -COMMENT ON COLUMN "issue_order_in_admission_state"."order_in_unit" IS 'Order of issues in admission state within all areas of a unit; NULL values sort last';
    3.20 -
    3.21 -CREATE VIEW "issue_supporter_in_admission_state" AS
    3.22 -  SELECT DISTINCT
    3.23 -    "area"."unit_id",
    3.24 -    "issue"."area_id",
    3.25 -    "issue"."id" AS "issue_id",
    3.26 -    "supporter"."member_id",
    3.27 -    "direct_interest_snapshot"."weight"
    3.28 -  FROM "issue"
    3.29 -  JOIN "area" ON "area"."id" = "issue"."area_id"
    3.30 -  JOIN "supporter" ON "supporter"."issue_id" = "issue"."id"
    3.31 -  JOIN "direct_interest_snapshot"
    3.32 -    ON  "direct_interest_snapshot"."issue_id" = "issue"."id"
    3.33 -    AND "direct_interest_snapshot"."event" = "issue"."latest_snapshot_event"
    3.34 -    AND "direct_interest_snapshot"."member_id" = "supporter"."member_id"
    3.35 -  WHERE "issue"."state" = 'admission'::"issue_state";
    3.36 -
    3.37 -COMMENT ON VIEW "issue_supporter_in_admission_state" IS 'Helper view for "lf_update_issue_order" to allow a (proportional) ordering of issues within an area';
    3.38 -
    3.39 -COMMENT ON COLUMN "initiative"."schulze_rank"           IS 'Schulze-Ranking';
    3.40 -COMMENT ON COLUMN "initiative"."better_than_status_quo" IS 'TRUE, if initiative has a schulze-ranking better than the status quo';
    3.41 -COMMENT ON COLUMN "initiative"."worse_than_status_quo"  IS 'TRUE, if initiative has a schulze-ranking worse than the status quo (DEPRECATED, since schulze-ranking is unique per issue; use "better_than_status_quo"=FALSE)';
    3.42 -COMMENT ON COLUMN "initiative"."winner"                 IS 'Winner is the "eligible" initiative with best "schulze_rank"';
    3.43 -
    3.44 -CREATE OR REPLACE FUNCTION "calculate_ranks"("issue_id_p" "issue"."id"%TYPE)
    3.45 -  RETURNS VOID
    3.46 -  LANGUAGE 'plpgsql' VOLATILE AS $$
    3.47 -    DECLARE
    3.48 -      "issue_row"         "issue"%ROWTYPE;
    3.49 -      "policy_row"        "policy"%ROWTYPE;
    3.50 -      "dimension_v"       INTEGER;
    3.51 -      "vote_matrix"       INT4[][];  -- absolute votes
    3.52 -      "matrix"            INT8[][];  -- defeat strength / best paths
    3.53 -      "i"                 INTEGER;
    3.54 -      "j"                 INTEGER;
    3.55 -      "k"                 INTEGER;
    3.56 -      "battle_row"        "battle"%ROWTYPE;
    3.57 -      "rank_ary"          INT4[];
    3.58 -      "rank_v"            INT4;
    3.59 -      "initiative_id_v"   "initiative"."id"%TYPE;
    3.60 -    BEGIN
    3.61 -      PERFORM "require_transaction_isolation"();
    3.62 -      SELECT * INTO "issue_row"
    3.63 -        FROM "issue" WHERE "id" = "issue_id_p";
    3.64 -      SELECT * INTO "policy_row"
    3.65 -        FROM "policy" WHERE "id" = "issue_row"."policy_id";
    3.66 -      SELECT count(1) INTO "dimension_v"
    3.67 -        FROM "battle_participant" WHERE "issue_id" = "issue_id_p";
    3.68 -      -- Create "vote_matrix" with absolute number of votes in pairwise
    3.69 -      -- comparison:
    3.70 -      "vote_matrix" := array_fill(NULL::INT4, ARRAY["dimension_v", "dimension_v"]);
    3.71 -      "i" := 1;
    3.72 -      "j" := 2;
    3.73 -      FOR "battle_row" IN
    3.74 -        SELECT * FROM "battle" WHERE "issue_id" = "issue_id_p"
    3.75 -        ORDER BY
    3.76 -        "winning_initiative_id" NULLS FIRST,
    3.77 -        "losing_initiative_id" NULLS FIRST
    3.78 -      LOOP
    3.79 -        "vote_matrix"["i"]["j"] := "battle_row"."count";
    3.80 -        IF "j" = "dimension_v" THEN
    3.81 -          "i" := "i" + 1;
    3.82 -          "j" := 1;
    3.83 -        ELSE
    3.84 -          "j" := "j" + 1;
    3.85 -          IF "j" = "i" THEN
    3.86 -            "j" := "j" + 1;
    3.87 -          END IF;
    3.88 -        END IF;
    3.89 -      END LOOP;
    3.90 -      IF "i" != "dimension_v" OR "j" != "dimension_v" + 1 THEN
    3.91 -        RAISE EXCEPTION 'Wrong battle count (should not happen)';
    3.92 -      END IF;
    3.93 -      -- Store defeat strengths in "matrix" using "defeat_strength"
    3.94 -      -- function:
    3.95 -      "matrix" := array_fill(NULL::INT8, ARRAY["dimension_v", "dimension_v"]);
    3.96 -      "i" := 1;
    3.97 -      LOOP
    3.98 -        "j" := 1;
    3.99 -        LOOP
   3.100 -          IF "i" != "j" THEN
   3.101 -            "matrix"["i"]["j"] := "defeat_strength"(
   3.102 -              "vote_matrix"["i"]["j"],
   3.103 -              "vote_matrix"["j"]["i"]
   3.104 -            );
   3.105 -          END IF;
   3.106 -          EXIT WHEN "j" = "dimension_v";
   3.107 -          "j" := "j" + 1;
   3.108 -        END LOOP;
   3.109 -        EXIT WHEN "i" = "dimension_v";
   3.110 -        "i" := "i" + 1;
   3.111 -      END LOOP;
   3.112 -      -- Find best paths:
   3.113 -      "i" := 1;
   3.114 -      LOOP
   3.115 -        "j" := 1;
   3.116 -        LOOP
   3.117 -          IF "i" != "j" THEN
   3.118 -            "k" := 1;
   3.119 -            LOOP
   3.120 -              IF "i" != "k" AND "j" != "k" THEN
   3.121 -                IF "matrix"["j"]["i"] < "matrix"["i"]["k"] THEN
   3.122 -                  IF "matrix"["j"]["i"] > "matrix"["j"]["k"] THEN
   3.123 -                    "matrix"["j"]["k"] := "matrix"["j"]["i"];
   3.124 -                  END IF;
   3.125 -                ELSE
   3.126 -                  IF "matrix"["i"]["k"] > "matrix"["j"]["k"] THEN
   3.127 -                    "matrix"["j"]["k"] := "matrix"["i"]["k"];
   3.128 -                  END IF;
   3.129 -                END IF;
   3.130 -              END IF;
   3.131 -              EXIT WHEN "k" = "dimension_v";
   3.132 -              "k" := "k" + 1;
   3.133 -            END LOOP;
   3.134 -          END IF;
   3.135 -          EXIT WHEN "j" = "dimension_v";
   3.136 -          "j" := "j" + 1;
   3.137 -        END LOOP;
   3.138 -        EXIT WHEN "i" = "dimension_v";
   3.139 -        "i" := "i" + 1;
   3.140 -      END LOOP;
   3.141 -      -- Determine order of winners:
   3.142 -      "rank_ary" := array_fill(NULL::INT4, ARRAY["dimension_v"]);
   3.143 -      "rank_v" := 1;
   3.144 -      LOOP
   3.145 -        "i" := 1;
   3.146 -        LOOP
   3.147 -          IF "rank_ary"["i"] ISNULL THEN
   3.148 -            "j" := 1;
   3.149 -            LOOP
   3.150 -              IF
   3.151 -                "i" != "j" AND
   3.152 -                "rank_ary"["j"] ISNULL AND
   3.153 -                ( "matrix"["j"]["i"] > "matrix"["i"]["j"] OR
   3.154 -                  -- tie-breaking by "id"
   3.155 -                  ( "matrix"["j"]["i"] = "matrix"["i"]["j"] AND
   3.156 -                    "j" < "i" ) )
   3.157 -              THEN
   3.158 -                -- someone else is better
   3.159 -                EXIT;
   3.160 -              END IF;
   3.161 -              "j" := "j" + 1;
   3.162 -              IF "j" = "dimension_v" + 1 THEN
   3.163 -                -- noone is better
   3.164 -                "rank_ary"["i"] := "rank_v";
   3.165 -                EXIT;
   3.166 -              END IF;
   3.167 -            END LOOP;
   3.168 -            EXIT WHEN "j" = "dimension_v" + 1;
   3.169 -          END IF;
   3.170 -          "i" := "i" + 1;
   3.171 -          IF "i" > "dimension_v" THEN
   3.172 -            RAISE EXCEPTION 'Schulze ranking does not compute (should not happen)';
   3.173 -          END IF;
   3.174 -        END LOOP;
   3.175 -        EXIT WHEN "rank_v" = "dimension_v";
   3.176 -        "rank_v" := "rank_v" + 1;
   3.177 -      END LOOP;
   3.178 -      -- write preliminary results:
   3.179 -      "i" := 2;  -- omit status quo with "i" = 1
   3.180 -      FOR "initiative_id_v" IN
   3.181 -        SELECT "id" FROM "initiative"
   3.182 -        WHERE "issue_id" = "issue_id_p" AND "admitted"
   3.183 -        ORDER BY "id"
   3.184 -      LOOP
   3.185 -        UPDATE "initiative" SET
   3.186 -          "direct_majority" =
   3.187 -            CASE WHEN "policy_row"."direct_majority_strict" THEN
   3.188 -              "positive_votes" * "policy_row"."direct_majority_den" >
   3.189 -              "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
   3.190 -            ELSE
   3.191 -              "positive_votes" * "policy_row"."direct_majority_den" >=
   3.192 -              "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
   3.193 -            END
   3.194 -            AND "positive_votes" >= "policy_row"."direct_majority_positive"
   3.195 -            AND "issue_row"."voter_count"-"negative_votes" >=
   3.196 -                "policy_row"."direct_majority_non_negative",
   3.197 -            "indirect_majority" =
   3.198 -            CASE WHEN "policy_row"."indirect_majority_strict" THEN
   3.199 -              "positive_votes" * "policy_row"."indirect_majority_den" >
   3.200 -              "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
   3.201 -            ELSE
   3.202 -              "positive_votes" * "policy_row"."indirect_majority_den" >=
   3.203 -              "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
   3.204 -            END
   3.205 -            AND "positive_votes" >= "policy_row"."indirect_majority_positive"
   3.206 -            AND "issue_row"."voter_count"-"negative_votes" >=
   3.207 -                "policy_row"."indirect_majority_non_negative",
   3.208 -          "schulze_rank"           = "rank_ary"["i"],
   3.209 -          "better_than_status_quo" = "rank_ary"["i"] < "rank_ary"[1],
   3.210 -          "worse_than_status_quo"  = "rank_ary"["i"] > "rank_ary"[1],
   3.211 -          "multistage_majority"    = "rank_ary"["i"] >= "rank_ary"[1],
   3.212 -          "reverse_beat_path"      = "matrix"[1]["i"] >= 0,
   3.213 -          "eligible"               = FALSE,
   3.214 -          "winner"                 = FALSE,
   3.215 -          "rank"                   = NULL  -- NOTE: in cases of manual reset of issue state
   3.216 -          WHERE "id" = "initiative_id_v";
   3.217 -        "i" := "i" + 1;
   3.218 -      END LOOP;
   3.219 -      IF "i" != "dimension_v" + 1 THEN
   3.220 -        RAISE EXCEPTION 'Wrong winner count (should not happen)';
   3.221 -      END IF;
   3.222 -      -- take indirect majorities into account:
   3.223 -      LOOP
   3.224 -        UPDATE "initiative" SET "indirect_majority" = TRUE
   3.225 -          FROM (
   3.226 -            SELECT "new_initiative"."id" AS "initiative_id"
   3.227 -            FROM "initiative" "old_initiative"
   3.228 -            JOIN "initiative" "new_initiative"
   3.229 -              ON "new_initiative"."issue_id" = "issue_id_p"
   3.230 -              AND "new_initiative"."indirect_majority" = FALSE
   3.231 -            JOIN "battle" "battle_win"
   3.232 -              ON "battle_win"."issue_id" = "issue_id_p"
   3.233 -              AND "battle_win"."winning_initiative_id" = "new_initiative"."id"
   3.234 -              AND "battle_win"."losing_initiative_id" = "old_initiative"."id"
   3.235 -            JOIN "battle" "battle_lose"
   3.236 -              ON "battle_lose"."issue_id" = "issue_id_p"
   3.237 -              AND "battle_lose"."losing_initiative_id" = "new_initiative"."id"
   3.238 -              AND "battle_lose"."winning_initiative_id" = "old_initiative"."id"
   3.239 -            WHERE "old_initiative"."issue_id" = "issue_id_p"
   3.240 -            AND "old_initiative"."indirect_majority" = TRUE
   3.241 -            AND CASE WHEN "policy_row"."indirect_majority_strict" THEN
   3.242 -              "battle_win"."count" * "policy_row"."indirect_majority_den" >
   3.243 -              "policy_row"."indirect_majority_num" *
   3.244 -              ("battle_win"."count"+"battle_lose"."count")
   3.245 -            ELSE
   3.246 -              "battle_win"."count" * "policy_row"."indirect_majority_den" >=
   3.247 -              "policy_row"."indirect_majority_num" *
   3.248 -              ("battle_win"."count"+"battle_lose"."count")
   3.249 -            END
   3.250 -            AND "battle_win"."count" >= "policy_row"."indirect_majority_positive"
   3.251 -            AND "issue_row"."voter_count"-"battle_lose"."count" >=
   3.252 -                "policy_row"."indirect_majority_non_negative"
   3.253 -          ) AS "subquery"
   3.254 -          WHERE "id" = "subquery"."initiative_id";
   3.255 -        EXIT WHEN NOT FOUND;
   3.256 -      END LOOP;
   3.257 -      -- set "multistage_majority" for remaining matching initiatives:
   3.258 -      UPDATE "initiative" SET "multistage_majority" = TRUE
   3.259 -        FROM (
   3.260 -          SELECT "losing_initiative"."id" AS "initiative_id"
   3.261 -          FROM "initiative" "losing_initiative"
   3.262 -          JOIN "initiative" "winning_initiative"
   3.263 -            ON "winning_initiative"."issue_id" = "issue_id_p"
   3.264 -            AND "winning_initiative"."admitted"
   3.265 -          JOIN "battle" "battle_win"
   3.266 -            ON "battle_win"."issue_id" = "issue_id_p"
   3.267 -            AND "battle_win"."winning_initiative_id" = "winning_initiative"."id"
   3.268 -            AND "battle_win"."losing_initiative_id" = "losing_initiative"."id"
   3.269 -          JOIN "battle" "battle_lose"
   3.270 -            ON "battle_lose"."issue_id" = "issue_id_p"
   3.271 -            AND "battle_lose"."losing_initiative_id" = "winning_initiative"."id"
   3.272 -            AND "battle_lose"."winning_initiative_id" = "losing_initiative"."id"
   3.273 -          WHERE "losing_initiative"."issue_id" = "issue_id_p"
   3.274 -          AND "losing_initiative"."admitted"
   3.275 -          AND "winning_initiative"."schulze_rank" <
   3.276 -              "losing_initiative"."schulze_rank"
   3.277 -          AND "battle_win"."count" > "battle_lose"."count"
   3.278 -          AND (
   3.279 -            "battle_win"."count" > "winning_initiative"."positive_votes" OR
   3.280 -            "battle_lose"."count" < "losing_initiative"."negative_votes" )
   3.281 -        ) AS "subquery"
   3.282 -        WHERE "id" = "subquery"."initiative_id";
   3.283 -      -- mark eligible initiatives:
   3.284 -      UPDATE "initiative" SET "eligible" = TRUE
   3.285 -        WHERE "issue_id" = "issue_id_p"
   3.286 -        AND "initiative"."direct_majority"
   3.287 -        AND "initiative"."indirect_majority"
   3.288 -        AND "initiative"."better_than_status_quo"
   3.289 -        AND (
   3.290 -          "policy_row"."no_multistage_majority" = FALSE OR
   3.291 -          "initiative"."multistage_majority" = FALSE )
   3.292 -        AND (
   3.293 -          "policy_row"."no_reverse_beat_path" = FALSE OR
   3.294 -          "initiative"."reverse_beat_path" = FALSE );
   3.295 -      -- mark final winner:
   3.296 -      UPDATE "initiative" SET "winner" = TRUE
   3.297 -        FROM (
   3.298 -          SELECT "id" AS "initiative_id"
   3.299 -          FROM "initiative"
   3.300 -          WHERE "issue_id" = "issue_id_p" AND "eligible"
   3.301 -          ORDER BY
   3.302 -            "schulze_rank",
   3.303 -            "id"
   3.304 -          LIMIT 1
   3.305 -        ) AS "subquery"
   3.306 -        WHERE "id" = "subquery"."initiative_id";
   3.307 -      -- write (final) ranks:
   3.308 -      "rank_v" := 1;
   3.309 -      FOR "initiative_id_v" IN
   3.310 -        SELECT "id"
   3.311 -        FROM "initiative"
   3.312 -        WHERE "issue_id" = "issue_id_p" AND "admitted"
   3.313 -        ORDER BY
   3.314 -          "winner" DESC,
   3.315 -          "eligible" DESC,
   3.316 -          "schulze_rank",
   3.317 -          "id"
   3.318 -      LOOP
   3.319 -        UPDATE "initiative" SET "rank" = "rank_v"
   3.320 -          WHERE "id" = "initiative_id_v";
   3.321 -        "rank_v" := "rank_v" + 1;
   3.322 -      END LOOP;
   3.323 -      -- set schulze rank of status quo and mark issue as finished:
   3.324 -      UPDATE "issue" SET
   3.325 -        "status_quo_schulze_rank" = "rank_ary"[1],
   3.326 -        "state" =
   3.327 -          CASE WHEN EXISTS (
   3.328 -            SELECT NULL FROM "initiative"
   3.329 -            WHERE "issue_id" = "issue_id_p" AND "winner"
   3.330 -          ) THEN
   3.331 -            'finished_with_winner'::"issue_state"
   3.332 -          ELSE
   3.333 -            'finished_without_winner'::"issue_state"
   3.334 -          END,
   3.335 -        "closed" = "phase_finished",
   3.336 -        "phase_finished" = NULL
   3.337 -        WHERE "id" = "issue_id_p";
   3.338 -      RETURN;
   3.339 -    END;
   3.340 -  $$;
   3.341 -
   3.342 -COMMIT;
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/update/core-update.v2.2.5-v3.0.0.sql	Fri Jan 31 12:46:11 2014 +0100
     4.3 @@ -0,0 +1,339 @@
     4.4 +BEGIN;
     4.5 +
     4.6 +CREATE OR REPLACE VIEW "liquid_feedback_version" AS
     4.7 +  SELECT * FROM (VALUES ('3.0.0', 3, 0, 0))
     4.8 +  AS "subquery"("string", "major", "minor", "revision");
     4.9 +
    4.10 +CREATE TABLE "issue_order_in_admission_state" (
    4.11 +        "id"                    INT8            PRIMARY KEY, --REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
    4.12 +        "order_in_area"         INT4,
    4.13 +        "order_in_unit"         INT4 );
    4.14 +
    4.15 +COMMENT ON TABLE "issue_order_in_admission_state" IS 'Ordering information for issues that are not stored in the "issue" table to avoid locking of multiple issues at once; Filled/updated by "lf_update_issue_order"';
    4.16 +
    4.17 +COMMENT ON COLUMN "issue_order_in_admission_state"."id"            IS 'References "issue" ("id") but has no referential integrity trigger associated, due to performance/locking issues';
    4.18 +COMMENT ON COLUMN "issue_order_in_admission_state"."order_in_area" IS 'Order of issues in admission state within a single area; NULL values sort last';
    4.19 +COMMENT ON COLUMN "issue_order_in_admission_state"."order_in_unit" IS 'Order of issues in admission state within all areas of a unit; NULL values sort last';
    4.20 +
    4.21 +CREATE VIEW "issue_supporter_in_admission_state" AS
    4.22 +  SELECT DISTINCT
    4.23 +    "area"."unit_id",
    4.24 +    "issue"."area_id",
    4.25 +    "issue"."id" AS "issue_id",
    4.26 +    "supporter"."member_id",
    4.27 +    "direct_interest_snapshot"."weight"
    4.28 +  FROM "issue"
    4.29 +  JOIN "area" ON "area"."id" = "issue"."area_id"
    4.30 +  JOIN "supporter" ON "supporter"."issue_id" = "issue"."id"
    4.31 +  JOIN "direct_interest_snapshot"
    4.32 +    ON  "direct_interest_snapshot"."issue_id" = "issue"."id"
    4.33 +    AND "direct_interest_snapshot"."event" = "issue"."latest_snapshot_event"
    4.34 +    AND "direct_interest_snapshot"."member_id" = "supporter"."member_id"
    4.35 +  WHERE "issue"."state" = 'admission'::"issue_state";
    4.36 +
    4.37 +COMMENT ON VIEW "issue_supporter_in_admission_state" IS 'Helper view for "lf_update_issue_order" to allow a (proportional) ordering of issues within an area';
    4.38 +
    4.39 +COMMENT ON COLUMN "initiative"."schulze_rank"           IS 'Schulze-Ranking';
    4.40 +COMMENT ON COLUMN "initiative"."better_than_status_quo" IS 'TRUE, if initiative has a schulze-ranking better than the status quo';
    4.41 +COMMENT ON COLUMN "initiative"."worse_than_status_quo"  IS 'TRUE, if initiative has a schulze-ranking worse than the status quo (DEPRECATED, since schulze-ranking is unique per issue; use "better_than_status_quo"=FALSE)';
    4.42 +COMMENT ON COLUMN "initiative"."winner"                 IS 'Winner is the "eligible" initiative with best "schulze_rank"';
    4.43 +
    4.44 +CREATE OR REPLACE FUNCTION "calculate_ranks"("issue_id_p" "issue"."id"%TYPE)
    4.45 +  RETURNS VOID
    4.46 +  LANGUAGE 'plpgsql' VOLATILE AS $$
    4.47 +    DECLARE
    4.48 +      "issue_row"         "issue"%ROWTYPE;
    4.49 +      "policy_row"        "policy"%ROWTYPE;
    4.50 +      "dimension_v"       INTEGER;
    4.51 +      "vote_matrix"       INT4[][];  -- absolute votes
    4.52 +      "matrix"            INT8[][];  -- defeat strength / best paths
    4.53 +      "i"                 INTEGER;
    4.54 +      "j"                 INTEGER;
    4.55 +      "k"                 INTEGER;
    4.56 +      "battle_row"        "battle"%ROWTYPE;
    4.57 +      "rank_ary"          INT4[];
    4.58 +      "rank_v"            INT4;
    4.59 +      "initiative_id_v"   "initiative"."id"%TYPE;
    4.60 +    BEGIN
    4.61 +      PERFORM "require_transaction_isolation"();
    4.62 +      SELECT * INTO "issue_row"
    4.63 +        FROM "issue" WHERE "id" = "issue_id_p";
    4.64 +      SELECT * INTO "policy_row"
    4.65 +        FROM "policy" WHERE "id" = "issue_row"."policy_id";
    4.66 +      SELECT count(1) INTO "dimension_v"
    4.67 +        FROM "battle_participant" WHERE "issue_id" = "issue_id_p";
    4.68 +      -- Create "vote_matrix" with absolute number of votes in pairwise
    4.69 +      -- comparison:
    4.70 +      "vote_matrix" := array_fill(NULL::INT4, ARRAY["dimension_v", "dimension_v"]);
    4.71 +      "i" := 1;
    4.72 +      "j" := 2;
    4.73 +      FOR "battle_row" IN
    4.74 +        SELECT * FROM "battle" WHERE "issue_id" = "issue_id_p"
    4.75 +        ORDER BY
    4.76 +        "winning_initiative_id" NULLS FIRST,
    4.77 +        "losing_initiative_id" NULLS FIRST
    4.78 +      LOOP
    4.79 +        "vote_matrix"["i"]["j"] := "battle_row"."count";
    4.80 +        IF "j" = "dimension_v" THEN
    4.81 +          "i" := "i" + 1;
    4.82 +          "j" := 1;
    4.83 +        ELSE
    4.84 +          "j" := "j" + 1;
    4.85 +          IF "j" = "i" THEN
    4.86 +            "j" := "j" + 1;
    4.87 +          END IF;
    4.88 +        END IF;
    4.89 +      END LOOP;
    4.90 +      IF "i" != "dimension_v" OR "j" != "dimension_v" + 1 THEN
    4.91 +        RAISE EXCEPTION 'Wrong battle count (should not happen)';
    4.92 +      END IF;
    4.93 +      -- Store defeat strengths in "matrix" using "defeat_strength"
    4.94 +      -- function:
    4.95 +      "matrix" := array_fill(NULL::INT8, ARRAY["dimension_v", "dimension_v"]);
    4.96 +      "i" := 1;
    4.97 +      LOOP
    4.98 +        "j" := 1;
    4.99 +        LOOP
   4.100 +          IF "i" != "j" THEN
   4.101 +            "matrix"["i"]["j"] := "defeat_strength"(
   4.102 +              "vote_matrix"["i"]["j"],
   4.103 +              "vote_matrix"["j"]["i"]
   4.104 +            );
   4.105 +          END IF;
   4.106 +          EXIT WHEN "j" = "dimension_v";
   4.107 +          "j" := "j" + 1;
   4.108 +        END LOOP;
   4.109 +        EXIT WHEN "i" = "dimension_v";
   4.110 +        "i" := "i" + 1;
   4.111 +      END LOOP;
   4.112 +      -- Find best paths:
   4.113 +      "i" := 1;
   4.114 +      LOOP
   4.115 +        "j" := 1;
   4.116 +        LOOP
   4.117 +          IF "i" != "j" THEN
   4.118 +            "k" := 1;
   4.119 +            LOOP
   4.120 +              IF "i" != "k" AND "j" != "k" THEN
   4.121 +                IF "matrix"["j"]["i"] < "matrix"["i"]["k"] THEN
   4.122 +                  IF "matrix"["j"]["i"] > "matrix"["j"]["k"] THEN
   4.123 +                    "matrix"["j"]["k"] := "matrix"["j"]["i"];
   4.124 +                  END IF;
   4.125 +                ELSE
   4.126 +                  IF "matrix"["i"]["k"] > "matrix"["j"]["k"] THEN
   4.127 +                    "matrix"["j"]["k"] := "matrix"["i"]["k"];
   4.128 +                  END IF;
   4.129 +                END IF;
   4.130 +              END IF;
   4.131 +              EXIT WHEN "k" = "dimension_v";
   4.132 +              "k" := "k" + 1;
   4.133 +            END LOOP;
   4.134 +          END IF;
   4.135 +          EXIT WHEN "j" = "dimension_v";
   4.136 +          "j" := "j" + 1;
   4.137 +        END LOOP;
   4.138 +        EXIT WHEN "i" = "dimension_v";
   4.139 +        "i" := "i" + 1;
   4.140 +      END LOOP;
   4.141 +      -- Determine order of winners:
   4.142 +      "rank_ary" := array_fill(NULL::INT4, ARRAY["dimension_v"]);
   4.143 +      "rank_v" := 1;
   4.144 +      LOOP
   4.145 +        "i" := 1;
   4.146 +        LOOP
   4.147 +          IF "rank_ary"["i"] ISNULL THEN
   4.148 +            "j" := 1;
   4.149 +            LOOP
   4.150 +              IF
   4.151 +                "i" != "j" AND
   4.152 +                "rank_ary"["j"] ISNULL AND
   4.153 +                ( "matrix"["j"]["i"] > "matrix"["i"]["j"] OR
   4.154 +                  -- tie-breaking by "id"
   4.155 +                  ( "matrix"["j"]["i"] = "matrix"["i"]["j"] AND
   4.156 +                    "j" < "i" ) )
   4.157 +              THEN
   4.158 +                -- someone else is better
   4.159 +                EXIT;
   4.160 +              END IF;
   4.161 +              "j" := "j" + 1;
   4.162 +              IF "j" = "dimension_v" + 1 THEN
   4.163 +                -- noone is better
   4.164 +                "rank_ary"["i"] := "rank_v";
   4.165 +                EXIT;
   4.166 +              END IF;
   4.167 +            END LOOP;
   4.168 +            EXIT WHEN "j" = "dimension_v" + 1;
   4.169 +          END IF;
   4.170 +          "i" := "i" + 1;
   4.171 +          IF "i" > "dimension_v" THEN
   4.172 +            RAISE EXCEPTION 'Schulze ranking does not compute (should not happen)';
   4.173 +          END IF;
   4.174 +        END LOOP;
   4.175 +        EXIT WHEN "rank_v" = "dimension_v";
   4.176 +        "rank_v" := "rank_v" + 1;
   4.177 +      END LOOP;
   4.178 +      -- write preliminary results:
   4.179 +      "i" := 2;  -- omit status quo with "i" = 1
   4.180 +      FOR "initiative_id_v" IN
   4.181 +        SELECT "id" FROM "initiative"
   4.182 +        WHERE "issue_id" = "issue_id_p" AND "admitted"
   4.183 +        ORDER BY "id"
   4.184 +      LOOP
   4.185 +        UPDATE "initiative" SET
   4.186 +          "direct_majority" =
   4.187 +            CASE WHEN "policy_row"."direct_majority_strict" THEN
   4.188 +              "positive_votes" * "policy_row"."direct_majority_den" >
   4.189 +              "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
   4.190 +            ELSE
   4.191 +              "positive_votes" * "policy_row"."direct_majority_den" >=
   4.192 +              "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
   4.193 +            END
   4.194 +            AND "positive_votes" >= "policy_row"."direct_majority_positive"
   4.195 +            AND "issue_row"."voter_count"-"negative_votes" >=
   4.196 +                "policy_row"."direct_majority_non_negative",
   4.197 +            "indirect_majority" =
   4.198 +            CASE WHEN "policy_row"."indirect_majority_strict" THEN
   4.199 +              "positive_votes" * "policy_row"."indirect_majority_den" >
   4.200 +              "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
   4.201 +            ELSE
   4.202 +              "positive_votes" * "policy_row"."indirect_majority_den" >=
   4.203 +              "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
   4.204 +            END
   4.205 +            AND "positive_votes" >= "policy_row"."indirect_majority_positive"
   4.206 +            AND "issue_row"."voter_count"-"negative_votes" >=
   4.207 +                "policy_row"."indirect_majority_non_negative",
   4.208 +          "schulze_rank"           = "rank_ary"["i"],
   4.209 +          "better_than_status_quo" = "rank_ary"["i"] < "rank_ary"[1],
   4.210 +          "worse_than_status_quo"  = "rank_ary"["i"] > "rank_ary"[1],
   4.211 +          "multistage_majority"    = "rank_ary"["i"] >= "rank_ary"[1],
   4.212 +          "reverse_beat_path"      = "matrix"[1]["i"] >= 0,
   4.213 +          "eligible"               = FALSE,
   4.214 +          "winner"                 = FALSE,
   4.215 +          "rank"                   = NULL  -- NOTE: in cases of manual reset of issue state
   4.216 +          WHERE "id" = "initiative_id_v";
   4.217 +        "i" := "i" + 1;
   4.218 +      END LOOP;
   4.219 +      IF "i" != "dimension_v" + 1 THEN
   4.220 +        RAISE EXCEPTION 'Wrong winner count (should not happen)';
   4.221 +      END IF;
   4.222 +      -- take indirect majorities into account:
   4.223 +      LOOP
   4.224 +        UPDATE "initiative" SET "indirect_majority" = TRUE
   4.225 +          FROM (
   4.226 +            SELECT "new_initiative"."id" AS "initiative_id"
   4.227 +            FROM "initiative" "old_initiative"
   4.228 +            JOIN "initiative" "new_initiative"
   4.229 +              ON "new_initiative"."issue_id" = "issue_id_p"
   4.230 +              AND "new_initiative"."indirect_majority" = FALSE
   4.231 +            JOIN "battle" "battle_win"
   4.232 +              ON "battle_win"."issue_id" = "issue_id_p"
   4.233 +              AND "battle_win"."winning_initiative_id" = "new_initiative"."id"
   4.234 +              AND "battle_win"."losing_initiative_id" = "old_initiative"."id"
   4.235 +            JOIN "battle" "battle_lose"
   4.236 +              ON "battle_lose"."issue_id" = "issue_id_p"
   4.237 +              AND "battle_lose"."losing_initiative_id" = "new_initiative"."id"
   4.238 +              AND "battle_lose"."winning_initiative_id" = "old_initiative"."id"
   4.239 +            WHERE "old_initiative"."issue_id" = "issue_id_p"
   4.240 +            AND "old_initiative"."indirect_majority" = TRUE
   4.241 +            AND CASE WHEN "policy_row"."indirect_majority_strict" THEN
   4.242 +              "battle_win"."count" * "policy_row"."indirect_majority_den" >
   4.243 +              "policy_row"."indirect_majority_num" *
   4.244 +              ("battle_win"."count"+"battle_lose"."count")
   4.245 +            ELSE
   4.246 +              "battle_win"."count" * "policy_row"."indirect_majority_den" >=
   4.247 +              "policy_row"."indirect_majority_num" *
   4.248 +              ("battle_win"."count"+"battle_lose"."count")
   4.249 +            END
   4.250 +            AND "battle_win"."count" >= "policy_row"."indirect_majority_positive"
   4.251 +            AND "issue_row"."voter_count"-"battle_lose"."count" >=
   4.252 +                "policy_row"."indirect_majority_non_negative"
   4.253 +          ) AS "subquery"
   4.254 +          WHERE "id" = "subquery"."initiative_id";
   4.255 +        EXIT WHEN NOT FOUND;
   4.256 +      END LOOP;
   4.257 +      -- set "multistage_majority" for remaining matching initiatives:
   4.258 +      UPDATE "initiative" SET "multistage_majority" = TRUE
   4.259 +        FROM (
   4.260 +          SELECT "losing_initiative"."id" AS "initiative_id"
   4.261 +          FROM "initiative" "losing_initiative"
   4.262 +          JOIN "initiative" "winning_initiative"
   4.263 +            ON "winning_initiative"."issue_id" = "issue_id_p"
   4.264 +            AND "winning_initiative"."admitted"
   4.265 +          JOIN "battle" "battle_win"
   4.266 +            ON "battle_win"."issue_id" = "issue_id_p"
   4.267 +            AND "battle_win"."winning_initiative_id" = "winning_initiative"."id"
   4.268 +            AND "battle_win"."losing_initiative_id" = "losing_initiative"."id"
   4.269 +          JOIN "battle" "battle_lose"
   4.270 +            ON "battle_lose"."issue_id" = "issue_id_p"
   4.271 +            AND "battle_lose"."losing_initiative_id" = "winning_initiative"."id"
   4.272 +            AND "battle_lose"."winning_initiative_id" = "losing_initiative"."id"
   4.273 +          WHERE "losing_initiative"."issue_id" = "issue_id_p"
   4.274 +          AND "losing_initiative"."admitted"
   4.275 +          AND "winning_initiative"."schulze_rank" <
   4.276 +              "losing_initiative"."schulze_rank"
   4.277 +          AND "battle_win"."count" > "battle_lose"."count"
   4.278 +          AND (
   4.279 +            "battle_win"."count" > "winning_initiative"."positive_votes" OR
   4.280 +            "battle_lose"."count" < "losing_initiative"."negative_votes" )
   4.281 +        ) AS "subquery"
   4.282 +        WHERE "id" = "subquery"."initiative_id";
   4.283 +      -- mark eligible initiatives:
   4.284 +      UPDATE "initiative" SET "eligible" = TRUE
   4.285 +        WHERE "issue_id" = "issue_id_p"
   4.286 +        AND "initiative"."direct_majority"
   4.287 +        AND "initiative"."indirect_majority"
   4.288 +        AND "initiative"."better_than_status_quo"
   4.289 +        AND (
   4.290 +          "policy_row"."no_multistage_majority" = FALSE OR
   4.291 +          "initiative"."multistage_majority" = FALSE )
   4.292 +        AND (
   4.293 +          "policy_row"."no_reverse_beat_path" = FALSE OR
   4.294 +          "initiative"."reverse_beat_path" = FALSE );
   4.295 +      -- mark final winner:
   4.296 +      UPDATE "initiative" SET "winner" = TRUE
   4.297 +        FROM (
   4.298 +          SELECT "id" AS "initiative_id"
   4.299 +          FROM "initiative"
   4.300 +          WHERE "issue_id" = "issue_id_p" AND "eligible"
   4.301 +          ORDER BY
   4.302 +            "schulze_rank",
   4.303 +            "id"
   4.304 +          LIMIT 1
   4.305 +        ) AS "subquery"
   4.306 +        WHERE "id" = "subquery"."initiative_id";
   4.307 +      -- write (final) ranks:
   4.308 +      "rank_v" := 1;
   4.309 +      FOR "initiative_id_v" IN
   4.310 +        SELECT "id"
   4.311 +        FROM "initiative"
   4.312 +        WHERE "issue_id" = "issue_id_p" AND "admitted"
   4.313 +        ORDER BY
   4.314 +          "winner" DESC,
   4.315 +          "eligible" DESC,
   4.316 +          "schulze_rank",
   4.317 +          "id"
   4.318 +      LOOP
   4.319 +        UPDATE "initiative" SET "rank" = "rank_v"
   4.320 +          WHERE "id" = "initiative_id_v";
   4.321 +        "rank_v" := "rank_v" + 1;
   4.322 +      END LOOP;
   4.323 +      -- set schulze rank of status quo and mark issue as finished:
   4.324 +      UPDATE "issue" SET
   4.325 +        "status_quo_schulze_rank" = "rank_ary"[1],
   4.326 +        "state" =
   4.327 +          CASE WHEN EXISTS (
   4.328 +            SELECT NULL FROM "initiative"
   4.329 +            WHERE "issue_id" = "issue_id_p" AND "winner"
   4.330 +          ) THEN
   4.331 +            'finished_with_winner'::"issue_state"
   4.332 +          ELSE
   4.333 +            'finished_without_winner'::"issue_state"
   4.334 +          END,
   4.335 +        "closed" = "phase_finished",
   4.336 +        "phase_finished" = NULL
   4.337 +        WHERE "id" = "issue_id_p";
   4.338 +      RETURN;
   4.339 +    END;
   4.340 +  $$;
   4.341 +
   4.342 +COMMIT;

Impressum / About Us