liquid_feedback_core
annotate core.sql @ 3:3da35844c874
Version beta4
Introduced half-freeze feature: Policies can cause issues to become half-frozen for a given amount of time before becoming fully frozen for voting. When an issue is half-frozen initiatives may not modify their drafts, but creating a new initiative or removing/adding support and opinions is still possible.
Bugfix: end_of_admission snapshots are now created for accepted issues.
Introduced half-freeze feature: Policies can cause issues to become half-frozen for a given amount of time before becoming fully frozen for voting. When an issue is half-frozen initiatives may not modify their drafts, but creating a new initiative or removing/adding support and opinions is still possible.
Bugfix: end_of_admission snapshots are now created for accepted issues.
author | jbe |
---|---|
date | Sun Nov 15 12:00:00 2009 +0100 (2009-11-15) |
parents | d45919d791ff |
children | 6133c0a62378 |
rev | line source |
---|---|
jbe@0 | 1 |
jbe@0 | 2 CREATE LANGUAGE plpgsql; -- Triggers are implemented in PL/pgSQL |
jbe@0 | 3 |
jbe@0 | 4 -- NOTE: In PostgreSQL every UNIQUE constraint implies creation of an index |
jbe@0 | 5 |
jbe@0 | 6 BEGIN; |
jbe@0 | 7 |
jbe@0 | 8 |
jbe@0 | 9 |
jbe@0 | 10 ------------------------- |
jbe@0 | 11 -- Tables and indicies -- |
jbe@0 | 12 ------------------------- |
jbe@0 | 13 |
jbe@0 | 14 |
jbe@0 | 15 CREATE TABLE "member" ( |
jbe@0 | 16 "id" SERIAL4 PRIMARY KEY, |
jbe@0 | 17 "login" TEXT NOT NULL UNIQUE, |
jbe@0 | 18 "password" TEXT, |
jbe@0 | 19 "active" BOOLEAN NOT NULL DEFAULT TRUE, |
jbe@0 | 20 "admin" BOOLEAN NOT NULL DEFAULT FALSE, |
jbe@0 | 21 "name" TEXT, |
jbe@0 | 22 "ident_number" TEXT UNIQUE ); |
jbe@0 | 23 CREATE INDEX "member_active_idx" ON "member" ("active"); |
jbe@0 | 24 |
jbe@0 | 25 COMMENT ON TABLE "member" IS 'Users of the system, e.g. members of an organization'; |
jbe@0 | 26 |
jbe@0 | 27 COMMENT ON COLUMN "member"."login" IS 'Login name'; |
jbe@0 | 28 COMMENT ON COLUMN "member"."password" IS 'Password (preferably as crypto-hash, depending on the frontend or access layer)'; |
jbe@0 | 29 COMMENT ON COLUMN "member"."active" IS 'Inactive members can not login and their supports/votes are not counted by the system.'; |
jbe@0 | 30 COMMENT ON COLUMN "member"."ident_number" IS 'Additional information about the members idenficication number within the organization'; |
jbe@0 | 31 |
jbe@0 | 32 |
jbe@0 | 33 CREATE TABLE "contact" ( |
jbe@0 | 34 PRIMARY KEY ("member_id", "other_member_id"), |
jbe@0 | 35 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 36 "other_member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 37 "public" BOOLEAN NOT NULL DEFAULT FALSE ); |
jbe@0 | 38 |
jbe@0 | 39 COMMENT ON TABLE "contact" IS 'Contact lists'; |
jbe@0 | 40 |
jbe@0 | 41 COMMENT ON COLUMN "contact"."member_id" IS 'Member having the contact list'; |
jbe@0 | 42 COMMENT ON COLUMN "contact"."other_member_id" IS 'Member referenced in the contact list'; |
jbe@0 | 43 COMMENT ON COLUMN "contact"."public" IS 'TRUE = display contact publically'; |
jbe@0 | 44 |
jbe@0 | 45 |
jbe@0 | 46 CREATE TABLE "session" ( |
jbe@0 | 47 "ident" TEXT PRIMARY KEY, |
jbe@0 | 48 "additional_secret" TEXT, |
jbe@0 | 49 "expiry" TIMESTAMPTZ NOT NULL DEFAULT now() + '24 hours', |
jbe@0 | 50 "member_id" INT8 REFERENCES "member" ("id") ON DELETE SET NULL, |
jbe@0 | 51 "lang" TEXT ); |
jbe@0 | 52 CREATE INDEX "session_expiry_idx" ON "session" ("expiry"); |
jbe@0 | 53 |
jbe@0 | 54 COMMENT ON TABLE "session" IS 'Sessions, i.e. for a web-frontend'; |
jbe@0 | 55 |
jbe@0 | 56 COMMENT ON COLUMN "session"."ident" IS 'Secret session identifier (i.e. random string)'; |
jbe@0 | 57 COMMENT ON COLUMN "session"."additional_secret" IS 'Additional field to store a secret, which can be used against CSRF attacks'; |
jbe@0 | 58 COMMENT ON COLUMN "session"."member_id" IS 'Reference to member, who is logged in'; |
jbe@0 | 59 COMMENT ON COLUMN "session"."lang" IS 'Language code of the selected language'; |
jbe@0 | 60 |
jbe@0 | 61 |
jbe@0 | 62 CREATE TABLE "policy" ( |
jbe@0 | 63 "id" SERIAL4 PRIMARY KEY, |
jbe@0 | 64 "active" BOOLEAN NOT NULL DEFAULT TRUE, |
jbe@0 | 65 "name" TEXT NOT NULL UNIQUE, |
jbe@0 | 66 "description" TEXT NOT NULL DEFAULT '', |
jbe@0 | 67 "admission_time" INTERVAL NOT NULL, |
jbe@0 | 68 "discussion_time" INTERVAL NOT NULL, |
jbe@3 | 69 "verification_time" INTERVAL NOT NULL, |
jbe@0 | 70 "voting_time" INTERVAL NOT NULL, |
jbe@0 | 71 "issue_quorum_num" INT4 NOT NULL, |
jbe@0 | 72 "issue_quorum_den" INT4 NOT NULL, |
jbe@0 | 73 "initiative_quorum_num" INT4 NOT NULL, |
jbe@0 | 74 "initiative_quorum_den" INT4 NOT NULL ); |
jbe@0 | 75 CREATE INDEX "policy_active_idx" ON "policy" ("active"); |
jbe@0 | 76 |
jbe@0 | 77 COMMENT ON TABLE "policy" IS 'Policies for a particular proceeding type (timelimits, quorum)'; |
jbe@0 | 78 |
jbe@0 | 79 COMMENT ON COLUMN "policy"."active" IS 'TRUE = policy can be used for new issues'; |
jbe@0 | 80 COMMENT ON COLUMN "policy"."admission_time" IS 'Maximum time an issue stays open without being "accepted"'; |
jbe@3 | 81 COMMENT ON COLUMN "policy"."discussion_time" IS 'Regular time until an issue is "half_frozen" after being "accepted"'; |
jbe@3 | 82 COMMENT ON COLUMN "policy"."verification_time" IS 'Regular time until an issue is "fully_frozen" after being "half_frozen"'; |
jbe@3 | 83 COMMENT ON COLUMN "policy"."voting_time" IS 'Time after an issue is "fully_frozen" but not "closed"'; |
jbe@0 | 84 COMMENT ON COLUMN "policy"."issue_quorum_num" IS 'Numerator of quorum to be reached by one initiative of an issue to be "accepted"'; |
jbe@0 | 85 COMMENT ON COLUMN "policy"."issue_quorum_den" IS 'Denominator of quorum to be reached by one initiative of an issue to be "accepted"'; |
jbe@0 | 86 COMMENT ON COLUMN "policy"."initiative_quorum_num" IS 'Numerator of quorum to be reached by an initiative to be "admitted" for voting'; |
jbe@0 | 87 COMMENT ON COLUMN "policy"."initiative_quorum_den" IS 'Denominator of quorum to be reached by an initiative to be "admitted" for voting'; |
jbe@0 | 88 |
jbe@0 | 89 |
jbe@0 | 90 CREATE TABLE "area" ( |
jbe@0 | 91 "id" SERIAL4 PRIMARY KEY, |
jbe@0 | 92 "active" BOOLEAN NOT NULL DEFAULT TRUE, |
jbe@0 | 93 "name" TEXT NOT NULL, |
jbe@0 | 94 "description" TEXT NOT NULL DEFAULT '' ); |
jbe@0 | 95 CREATE INDEX "area_active_idx" ON "area" ("active"); |
jbe@0 | 96 |
jbe@0 | 97 COMMENT ON TABLE "area" IS 'Subject areas'; |
jbe@0 | 98 |
jbe@0 | 99 COMMENT ON COLUMN "area"."active" IS 'TRUE means new issues can be created in this area'; |
jbe@0 | 100 |
jbe@0 | 101 |
jbe@0 | 102 CREATE TABLE "issue" ( |
jbe@0 | 103 "id" SERIAL4 PRIMARY KEY, |
jbe@0 | 104 "area_id" INT4 NOT NULL REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 105 "policy_id" INT4 NOT NULL REFERENCES "policy" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 106 "created" TIMESTAMPTZ NOT NULL DEFAULT now(), |
jbe@0 | 107 "accepted" TIMESTAMPTZ, |
jbe@3 | 108 "half_frozen" TIMESTAMPTZ, |
jbe@3 | 109 "fully_frozen" TIMESTAMPTZ, |
jbe@0 | 110 "closed" TIMESTAMPTZ, |
jbe@0 | 111 "ranks_available" BOOLEAN NOT NULL DEFAULT FALSE, |
jbe@0 | 112 "snapshot" TIMESTAMPTZ, |
jbe@0 | 113 "population" INT4, |
jbe@0 | 114 "vote_now" INT4, |
jbe@0 | 115 "vote_later" INT4, |
jbe@0 | 116 CONSTRAINT "valid_state" CHECK ( |
jbe@3 | 117 ("accepted" ISNULL AND "half_frozen" ISNULL AND "fully_frozen" ISNULL AND "closed" ISNULL AND "ranks_available" = FALSE) OR |
jbe@3 | 118 ("accepted" ISNULL AND "half_frozen" ISNULL AND "fully_frozen" ISNULL AND "closed" NOTNULL AND "ranks_available" = FALSE) OR |
jbe@3 | 119 ("accepted" NOTNULL AND "half_frozen" ISNULL AND "fully_frozen" ISNULL AND "closed" ISNULL AND "ranks_available" = FALSE) OR |
jbe@3 | 120 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" ISNULL AND "closed" ISNULL AND "ranks_available" = FALSE) OR |
jbe@3 | 121 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" NOTNULL AND "closed" ISNULL AND "ranks_available" = FALSE) OR |
jbe@3 | 122 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" NOTNULL AND "closed" NOTNULL AND "ranks_available" = FALSE) OR |
jbe@3 | 123 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" NOTNULL AND "closed" NOTNULL AND "ranks_available" = TRUE) ), |
jbe@3 | 124 CONSTRAINT "state_change_order" CHECK ( |
jbe@3 | 125 "created" <= "accepted" AND |
jbe@3 | 126 "accepted" <= "half_frozen" AND |
jbe@3 | 127 "half_frozen" <= "fully_frozen" AND |
jbe@3 | 128 "fully_frozen" <= "closed" ), |
jbe@3 | 129 CONSTRAINT "last_snapshot_on_full_freeze" CHECK ("snapshot" = "fully_frozen"), -- NOTE: snapshot can be set, while frozen is NULL yet |
jbe@3 | 130 CONSTRAINT "freeze_requires_snapshot" CHECK ("fully_frozen" ISNULL OR "snapshot" NOTNULL) ); |
jbe@0 | 131 CREATE INDEX "issue_area_id_idx" ON "issue" ("area_id"); |
jbe@0 | 132 CREATE INDEX "issue_policy_id_idx" ON "issue" ("policy_id"); |
jbe@0 | 133 CREATE INDEX "issue_created_idx_open" ON "issue" ("created") WHERE "closed" ISNULL; |
jbe@0 | 134 |
jbe@0 | 135 COMMENT ON TABLE "issue" IS 'Groups of initiatives'; |
jbe@0 | 136 |
jbe@0 | 137 COMMENT ON COLUMN "issue"."accepted" IS 'Point in time, when one initiative of issue reached the "issue_quorum"'; |
jbe@3 | 138 COMMENT ON COLUMN "issue"."half_frozen" IS 'Point in time, when "discussion_time" has elapsed, or members voted for voting'; |
jbe@3 | 139 COMMENT ON COLUMN "issue"."fully_frozen" IS 'Point in time, when "verification_time" has elapsed'; |
jbe@3 | 140 COMMENT ON COLUMN "issue"."closed" IS 'Point in time, when "admission_time" or "voting_time" have elapsed, and issue is no longer active'; |
jbe@0 | 141 COMMENT ON COLUMN "issue"."ranks_available" IS 'TRUE = ranks have been calculated'; |
jbe@0 | 142 COMMENT ON COLUMN "issue"."snapshot" IS 'Point in time, when snapshot tables have been updated and "population", "vote_now", "vote_later" and *_count values were precalculated'; |
jbe@0 | 143 COMMENT ON COLUMN "issue"."population" IS 'Calculated from table "direct_population_snapshot"'; |
jbe@0 | 144 COMMENT ON COLUMN "issue"."vote_now" IS 'Calculated from table "direct_interest_snapshot"'; |
jbe@0 | 145 COMMENT ON COLUMN "issue"."vote_later" IS 'Calculated from table "direct_interest_snapshot"'; |
jbe@0 | 146 |
jbe@0 | 147 |
jbe@0 | 148 CREATE TABLE "initiative" ( |
jbe@0 | 149 UNIQUE ("issue_id", "id"), -- index needed for foreign-key on table "vote" |
jbe@0 | 150 "issue_id" INT4 NOT NULL REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 151 "id" SERIAL4 PRIMARY KEY, |
jbe@0 | 152 "name" TEXT NOT NULL, |
jbe@0 | 153 "created" TIMESTAMPTZ NOT NULL DEFAULT now(), |
jbe@0 | 154 "revoked" TIMESTAMPTZ, |
jbe@0 | 155 "admitted" BOOLEAN, |
jbe@0 | 156 "supporter_count" INT4, |
jbe@0 | 157 "informed_supporter_count" INT4, |
jbe@0 | 158 "satisfied_supporter_count" INT4, |
jbe@0 | 159 "satisfied_informed_supporter_count" INT4, |
jbe@0 | 160 "positive_votes" INT4, |
jbe@0 | 161 "negative_votes" INT4, |
jbe@0 | 162 "rank" INT4, |
jbe@0 | 163 CONSTRAINT "revoked_initiatives_cant_be_admitted" |
jbe@0 | 164 CHECK ("revoked" ISNULL OR "admitted" ISNULL), |
jbe@0 | 165 CONSTRAINT "non_admitted_initiatives_cant_contain_voting_results" |
jbe@0 | 166 CHECK ("admitted" = TRUE OR ("positive_votes" ISNULL AND "negative_votes" ISNULL AND "rank" ISNULL)) ); |
jbe@0 | 167 |
jbe@0 | 168 COMMENT ON TABLE "initiative" IS 'Group of members publishing drafts for resolutions to be passed'; |
jbe@0 | 169 |
jbe@0 | 170 COMMENT ON COLUMN "initiative"."revoked" IS 'Point in time, when one initiator decided to revoke the initiative'; |
jbe@0 | 171 COMMENT ON COLUMN "initiative"."admitted" IS 'True, if initiative reaches the "initiative_quorum" when freezing the issue'; |
jbe@0 | 172 COMMENT ON COLUMN "initiative"."supporter_count" IS 'Calculated from table "direct_supporter_snapshot"'; |
jbe@0 | 173 COMMENT ON COLUMN "initiative"."informed_supporter_count" IS 'Calculated from table "direct_supporter_snapshot"'; |
jbe@0 | 174 COMMENT ON COLUMN "initiative"."satisfied_supporter_count" IS 'Calculated from table "direct_supporter_snapshot"'; |
jbe@0 | 175 COMMENT ON COLUMN "initiative"."satisfied_informed_supporter_count" IS 'Calculated from table "direct_supporter_snapshot"'; |
jbe@0 | 176 COMMENT ON COLUMN "initiative"."positive_votes" IS 'Calculated from table "direct_voter"'; |
jbe@0 | 177 COMMENT ON COLUMN "initiative"."negative_votes" IS 'Calculated from table "direct_voter"'; |
jbe@0 | 178 COMMENT ON COLUMN "initiative"."rank" IS 'Rank of approved initiatives (winner is 1), calculated from table "direct_voter"'; |
jbe@0 | 179 |
jbe@0 | 180 |
jbe@0 | 181 CREATE TABLE "draft" ( |
jbe@0 | 182 UNIQUE ("initiative_id", "id"), -- index needed for foreign-key on table "supporter" |
jbe@0 | 183 "initiative_id" INT4 NOT NULL REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 184 "id" SERIAL8 PRIMARY KEY, |
jbe@0 | 185 "created" TIMESTAMPTZ NOT NULL DEFAULT now(), |
jbe@0 | 186 "author_id" INT4 NOT NULL REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 187 "content" TEXT NOT NULL ); |
jbe@0 | 188 |
jbe@0 | 189 COMMENT ON TABLE "draft" IS 'Drafts of initiatives to solve issues'; |
jbe@0 | 190 |
jbe@0 | 191 |
jbe@0 | 192 CREATE TABLE "suggestion" ( |
jbe@0 | 193 UNIQUE ("initiative_id", "id"), -- index needed for foreign-key on table "opinion" |
jbe@0 | 194 "initiative_id" INT4 NOT NULL REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 195 "id" SERIAL8 PRIMARY KEY, |
jbe@0 | 196 "created" TIMESTAMPTZ NOT NULL DEFAULT now(), |
jbe@0 | 197 "author_id" INT4 NOT NULL REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 198 "name" TEXT NOT NULL, |
jbe@0 | 199 "description" TEXT NOT NULL DEFAULT '', |
jbe@0 | 200 "minus2_unfulfilled_count" INT4, |
jbe@0 | 201 "minus2_fulfilled_count" INT4, |
jbe@0 | 202 "minus1_unfulfilled_count" INT4, |
jbe@0 | 203 "minus1_fulfilled_count" INT4, |
jbe@0 | 204 "plus1_unfulfilled_count" INT4, |
jbe@0 | 205 "plus1_fulfilled_count" INT4, |
jbe@0 | 206 "plus2_unfulfilled_count" INT4, |
jbe@0 | 207 "plus2_fulfilled_count" INT4 ); |
jbe@0 | 208 |
jbe@0 | 209 COMMENT ON TABLE "suggestion" IS 'Suggestions to initiators, to change the current draft'; |
jbe@0 | 210 |
jbe@0 | 211 COMMENT ON COLUMN "suggestion"."minus2_unfulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters'; |
jbe@0 | 212 COMMENT ON COLUMN "suggestion"."minus2_fulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters'; |
jbe@0 | 213 COMMENT ON COLUMN "suggestion"."minus1_unfulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters'; |
jbe@0 | 214 COMMENT ON COLUMN "suggestion"."minus1_fulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters'; |
jbe@0 | 215 COMMENT ON COLUMN "suggestion"."plus1_unfulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters'; |
jbe@0 | 216 COMMENT ON COLUMN "suggestion"."plus1_fulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters'; |
jbe@0 | 217 COMMENT ON COLUMN "suggestion"."plus2_unfulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters'; |
jbe@0 | 218 COMMENT ON COLUMN "suggestion"."plus2_fulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters'; |
jbe@0 | 219 |
jbe@0 | 220 |
jbe@0 | 221 CREATE TABLE "membership" ( |
jbe@0 | 222 PRIMARY KEY ("area_id", "member_id"), |
jbe@0 | 223 "area_id" INT4 REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 224 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 225 "autoreject" BOOLEAN NOT NULL DEFAULT FALSE ); |
jbe@0 | 226 CREATE INDEX "membership_member_id_idx" ON "membership" ("member_id"); |
jbe@0 | 227 |
jbe@0 | 228 COMMENT ON TABLE "membership" IS 'Interest of members in topic areas'; |
jbe@0 | 229 |
jbe@0 | 230 COMMENT ON COLUMN "membership"."autoreject" IS 'TRUE = member votes against all initiatives in case of not explicitly taking part in the voting procedure; If there exists an "interest" entry, the interest entry has precedence'; |
jbe@0 | 231 |
jbe@0 | 232 |
jbe@0 | 233 CREATE TABLE "interest" ( |
jbe@0 | 234 PRIMARY KEY ("issue_id", "member_id"), |
jbe@0 | 235 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 236 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 237 "autoreject" BOOLEAN NOT NULL, |
jbe@0 | 238 "voting_requested" BOOLEAN ); |
jbe@0 | 239 CREATE INDEX "interest_member_id_idx" ON "interest" ("member_id"); |
jbe@0 | 240 |
jbe@0 | 241 COMMENT ON TABLE "interest" IS 'Interest of members in a particular issue'; |
jbe@0 | 242 |
jbe@0 | 243 COMMENT ON COLUMN "interest"."autoreject" IS 'TRUE = member votes against all initiatives in case of not explicitly taking part in the voting procedure'; |
jbe@0 | 244 COMMENT ON COLUMN "interest"."voting_requested" IS 'TRUE = member wants to vote now, FALSE = member wants to vote later, NULL = policy rules should apply'; |
jbe@0 | 245 |
jbe@0 | 246 |
jbe@0 | 247 CREATE TABLE "initiator" ( |
jbe@0 | 248 PRIMARY KEY ("initiative_id", "member_id"), |
jbe@0 | 249 "initiative_id" INT4 REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 250 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 251 "accepted" BOOLEAN NOT NULL DEFAULT TRUE ); |
jbe@0 | 252 CREATE INDEX "initiator_member_id_idx" ON "initiator" ("member_id"); |
jbe@0 | 253 |
jbe@0 | 254 COMMENT ON TABLE "initiator" IS 'Members who are allowed to post new drafts'; |
jbe@0 | 255 |
jbe@0 | 256 COMMENT ON COLUMN "initiator"."accepted" IS 'If "accepted" = FALSE, then the member was invited to be a co-initiator, but has not answered yet.'; |
jbe@0 | 257 |
jbe@0 | 258 |
jbe@0 | 259 CREATE TABLE "supporter" ( |
jbe@0 | 260 "issue_id" INT4 NOT NULL, |
jbe@0 | 261 PRIMARY KEY ("initiative_id", "member_id"), |
jbe@0 | 262 "initiative_id" INT4, |
jbe@0 | 263 "member_id" INT4, |
jbe@0 | 264 "draft_id" INT8 NOT NULL, |
jbe@0 | 265 FOREIGN KEY ("issue_id", "member_id") REFERENCES "interest" ("issue_id", "member_id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 266 FOREIGN KEY ("initiative_id", "draft_id") REFERENCES "draft" ("initiative_id", "id") ON DELETE CASCADE ON UPDATE CASCADE ); |
jbe@0 | 267 CREATE INDEX "supporter_member_id_idx" ON "supporter" ("member_id"); |
jbe@0 | 268 |
jbe@0 | 269 COMMENT ON TABLE "supporter" IS 'Members who support an initiative (conditionally)'; |
jbe@0 | 270 |
jbe@2 | 271 COMMENT ON COLUMN "supporter"."draft_id" IS 'Latest seen draft, defaults to current draft of the initiative (implemented by trigger "default_for_draft_id")'; |
jbe@0 | 272 |
jbe@0 | 273 |
jbe@0 | 274 CREATE TABLE "opinion" ( |
jbe@0 | 275 "initiative_id" INT4 NOT NULL, |
jbe@0 | 276 PRIMARY KEY ("suggestion_id", "member_id"), |
jbe@0 | 277 "suggestion_id" INT8, |
jbe@0 | 278 "member_id" INT4, |
jbe@0 | 279 "degree" INT2 NOT NULL CHECK ("degree" >= -2 AND "degree" <= 2 AND "degree" != 0), |
jbe@0 | 280 "fulfilled" BOOLEAN NOT NULL DEFAULT FALSE, |
jbe@0 | 281 FOREIGN KEY ("initiative_id", "suggestion_id") REFERENCES "suggestion" ("initiative_id", "id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 282 FOREIGN KEY ("initiative_id", "member_id") REFERENCES "supporter" ("initiative_id", "member_id") ON DELETE CASCADE ON UPDATE CASCADE ); |
jbe@0 | 283 CREATE INDEX "opinion_member_id_idx" ON "opinion" ("member_id"); |
jbe@0 | 284 |
jbe@0 | 285 COMMENT ON TABLE "opinion" IS 'Opinion on suggestions (criticism related to initiatives)'; |
jbe@0 | 286 |
jbe@0 | 287 COMMENT ON COLUMN "opinion"."degree" IS '2 = fulfillment required for support; 1 = fulfillment desired; -1 = fulfillment unwanted; -2 = fulfillment cancels support'; |
jbe@0 | 288 |
jbe@0 | 289 |
jbe@0 | 290 CREATE TABLE "delegation" ( |
jbe@0 | 291 "id" SERIAL8 PRIMARY KEY, |
jbe@0 | 292 "truster_id" INT4 NOT NULL REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 293 "trustee_id" INT4 NOT NULL REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 294 "area_id" INT4 REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 295 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 296 CONSTRAINT "cant_delegate_to_yourself" CHECK ("truster_id" != "trustee_id"), |
jbe@0 | 297 CONSTRAINT "area_id_or_issue_id_can_be_set_but_not_both" CHECK ("area_id" ISNULL OR "issue_id" ISNULL), |
jbe@0 | 298 UNIQUE ("area_id", "truster_id", "trustee_id"), |
jbe@0 | 299 UNIQUE ("issue_id", "truster_id", "trustee_id") ); |
jbe@0 | 300 CREATE UNIQUE INDEX "delegation_default_truster_id_trustee_id_unique_idx" |
jbe@0 | 301 ON "delegation" ("truster_id", "trustee_id") |
jbe@0 | 302 WHERE "area_id" ISNULL AND "issue_id" ISNULL; |
jbe@0 | 303 CREATE INDEX "delegation_truster_id_idx" ON "delegation" ("truster_id"); |
jbe@0 | 304 CREATE INDEX "delegation_trustee_id_idx" ON "delegation" ("trustee_id"); |
jbe@0 | 305 |
jbe@0 | 306 COMMENT ON TABLE "delegation" IS 'Delegation of vote-weight to other members'; |
jbe@0 | 307 |
jbe@0 | 308 COMMENT ON COLUMN "delegation"."area_id" IS 'Reference to area, if delegation is area-wide, otherwise NULL'; |
jbe@0 | 309 COMMENT ON COLUMN "delegation"."issue_id" IS 'Reference to issue, if delegation is issue-wide, otherwise NULL'; |
jbe@0 | 310 |
jbe@0 | 311 |
jbe@3 | 312 CREATE TYPE "snapshot_event" AS ENUM ('periodic', 'end_of_admission', 'start_of_voting'); |
jbe@0 | 313 |
jbe@3 | 314 COMMENT ON TYPE "snapshot_event" IS 'Reason for snapshots: ''periodic'' = due to periodic recalculation, ''end_of_admission'' = saved state at end of admission period, ''start_of_voting'' = saved state at end of verification period'; |
jbe@0 | 315 |
jbe@0 | 316 |
jbe@0 | 317 CREATE TABLE "direct_population_snapshot" ( |
jbe@0 | 318 PRIMARY KEY ("issue_id", "event", "member_id"), |
jbe@0 | 319 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 320 "event" "snapshot_event", |
jbe@0 | 321 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 322 "weight" INT4, |
jbe@0 | 323 "interest_exists" BOOLEAN NOT NULL ); |
jbe@0 | 324 CREATE INDEX "direct_population_snapshot_member_id_idx" ON "direct_population_snapshot" ("member_id"); |
jbe@0 | 325 |
jbe@0 | 326 COMMENT ON TABLE "direct_population_snapshot" IS 'Snapshot of active members having either a "membership" in the "area" or an "interest" in the "issue"'; |
jbe@0 | 327 |
jbe@0 | 328 COMMENT ON COLUMN "direct_population_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details'; |
jbe@0 | 329 COMMENT ON COLUMN "direct_population_snapshot"."weight" IS 'Weight of member (1 or higher) according to "delegating_population_snapshot"'; |
jbe@0 | 330 COMMENT ON COLUMN "direct_population_snapshot"."interest_exists" IS 'TRUE if entry is due to interest in issue, FALSE if entry is only due to membership in area'; |
jbe@0 | 331 |
jbe@0 | 332 |
jbe@0 | 333 CREATE TABLE "delegating_population_snapshot" ( |
jbe@0 | 334 PRIMARY KEY ("issue_id", "event", "member_id"), |
jbe@0 | 335 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 336 "event" "snapshot_event", |
jbe@0 | 337 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 338 "delegate_member_ids" INT4[] NOT NULL ); |
jbe@0 | 339 CREATE INDEX "delegating_population_snapshot_member_id_idx" ON "delegating_population_snapshot" ("member_id"); |
jbe@0 | 340 |
jbe@0 | 341 COMMENT ON TABLE "direct_population_snapshot" IS 'Delegations increasing the weight of entries in the "direct_population_snapshot" table'; |
jbe@0 | 342 |
jbe@0 | 343 COMMENT ON COLUMN "delegating_population_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details'; |
jbe@0 | 344 COMMENT ON COLUMN "delegating_population_snapshot"."member_id" IS 'Delegating member'; |
jbe@0 | 345 COMMENT ON COLUMN "delegating_population_snapshot"."delegate_member_ids" IS 'Chain of members who act as delegates; last entry referes to "member_id" column of table "direct_population_snapshot"'; |
jbe@0 | 346 |
jbe@0 | 347 |
jbe@0 | 348 CREATE TABLE "direct_interest_snapshot" ( |
jbe@0 | 349 PRIMARY KEY ("issue_id", "event", "member_id"), |
jbe@0 | 350 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 351 "event" "snapshot_event", |
jbe@0 | 352 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 353 "weight" INT4, |
jbe@0 | 354 "voting_requested" BOOLEAN ); |
jbe@0 | 355 CREATE INDEX "direct_interest_snapshot_member_id_idx" ON "direct_interest_snapshot" ("member_id"); |
jbe@0 | 356 |
jbe@0 | 357 COMMENT ON TABLE "direct_interest_snapshot" IS 'Snapshot of active members having an "interest" in the "issue"'; |
jbe@0 | 358 |
jbe@0 | 359 COMMENT ON COLUMN "direct_interest_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details'; |
jbe@0 | 360 COMMENT ON COLUMN "direct_interest_snapshot"."weight" IS 'Weight of member (1 or higher) according to "delegating_interest_snapshot"'; |
jbe@0 | 361 COMMENT ON COLUMN "direct_interest_snapshot"."voting_requested" IS 'Copied from column "voting_requested" of table "interest"'; |
jbe@0 | 362 |
jbe@0 | 363 |
jbe@0 | 364 CREATE TABLE "delegating_interest_snapshot" ( |
jbe@0 | 365 PRIMARY KEY ("issue_id", "event", "member_id"), |
jbe@0 | 366 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 367 "event" "snapshot_event", |
jbe@0 | 368 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 369 "delegate_member_ids" INT4[] NOT NULL ); |
jbe@0 | 370 CREATE INDEX "delegating_interest_snapshot_member_id_idx" ON "delegating_interest_snapshot" ("member_id"); |
jbe@0 | 371 |
jbe@0 | 372 COMMENT ON TABLE "delegating_interest_snapshot" IS 'Delegations increasing the weight of entries in the "direct_interest_snapshot" table'; |
jbe@0 | 373 |
jbe@0 | 374 COMMENT ON COLUMN "delegating_interest_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details'; |
jbe@0 | 375 COMMENT ON COLUMN "delegating_interest_snapshot"."member_id" IS 'Delegating member'; |
jbe@0 | 376 COMMENT ON COLUMN "delegating_interest_snapshot"."delegate_member_ids" IS 'Chain of members who act as delegates; last entry referes to "member_id" column of table "direct_interest_snapshot"'; |
jbe@0 | 377 |
jbe@0 | 378 |
jbe@0 | 379 CREATE TABLE "direct_supporter_snapshot" ( |
jbe@0 | 380 "issue_id" INT4 NOT NULL, |
jbe@0 | 381 PRIMARY KEY ("initiative_id", "event", "member_id"), |
jbe@0 | 382 "initiative_id" INT4, |
jbe@0 | 383 "event" "snapshot_event", |
jbe@0 | 384 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 385 "informed" BOOLEAN NOT NULL, |
jbe@0 | 386 "satisfied" BOOLEAN NOT NULL, |
jbe@0 | 387 FOREIGN KEY ("issue_id", "initiative_id") REFERENCES "initiative" ("issue_id", "id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 388 FOREIGN KEY ("issue_id", "event", "member_id") REFERENCES "direct_interest_snapshot" ("issue_id", "event", "member_id") ON DELETE CASCADE ON UPDATE CASCADE ); |
jbe@0 | 389 CREATE INDEX "direct_supporter_snapshot_member_id_idx" ON "direct_supporter_snapshot" ("member_id"); |
jbe@0 | 390 |
jbe@0 | 391 COMMENT ON TABLE "direct_supporter_snapshot" IS 'Snapshot of supporters of initiatives (weight is stored in "direct_interest_snapshot)'; |
jbe@0 | 392 |
jbe@0 | 393 COMMENT ON COLUMN "direct_supporter_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details'; |
jbe@0 | 394 COMMENT ON COLUMN "direct_supporter_snapshot"."informed" IS 'Supporter has seen the latest draft of the initiative'; |
jbe@0 | 395 COMMENT ON COLUMN "direct_supporter_snapshot"."satisfied" IS 'Supporter has no "critical_opinion"s'; |
jbe@0 | 396 |
jbe@0 | 397 |
jbe@0 | 398 CREATE TABLE "direct_voter" ( |
jbe@0 | 399 PRIMARY KEY ("issue_id", "member_id"), |
jbe@0 | 400 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 401 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 402 "weight" INT4, |
jbe@0 | 403 "autoreject" BOOLEAN NOT NULL DEFAULT FALSE ); |
jbe@0 | 404 CREATE INDEX "direct_voter_member_id_idx" ON "direct_voter" ("member_id"); |
jbe@0 | 405 |
jbe@0 | 406 COMMENT ON TABLE "direct_voter" IS 'Members having directly voted for/against initiatives of an issue'; |
jbe@0 | 407 |
jbe@0 | 408 COMMENT ON COLUMN "direct_voter"."weight" IS 'Weight of member (1 or higher) according to "delegating_voter" table'; |
jbe@0 | 409 COMMENT ON COLUMN "direct_voter"."autoreject" IS 'Votes were inserted due to "autoreject" feature'; |
jbe@0 | 410 |
jbe@0 | 411 |
jbe@0 | 412 CREATE TABLE "delegating_voter" ( |
jbe@0 | 413 PRIMARY KEY ("issue_id", "member_id"), |
jbe@0 | 414 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 415 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, |
jbe@0 | 416 "delegate_member_ids" INT4[] NOT NULL ); |
jbe@0 | 417 CREATE INDEX "delegating_voter_member_id_idx" ON "direct_voter" ("member_id"); |
jbe@0 | 418 |
jbe@0 | 419 COMMENT ON TABLE "delegating_voter" IS 'Delegations increasing the weight of entries in the "direct_voter" table'; |
jbe@0 | 420 |
jbe@0 | 421 COMMENT ON COLUMN "delegating_voter"."member_id" IS 'Delegating member'; |
jbe@0 | 422 COMMENT ON COLUMN "delegating_voter"."delegate_member_ids" IS 'Chain of members who act as delegates; last entry referes to "member_id" column of table "direct_voter"'; |
jbe@0 | 423 |
jbe@0 | 424 |
jbe@0 | 425 CREATE TABLE "vote" ( |
jbe@0 | 426 "issue_id" INT4 NOT NULL, |
jbe@0 | 427 PRIMARY KEY ("initiative_id", "member_id"), |
jbe@0 | 428 "initiative_id" INT4, |
jbe@0 | 429 "member_id" INT4, |
jbe@0 | 430 "grade" INT4, |
jbe@0 | 431 FOREIGN KEY ("issue_id", "initiative_id") REFERENCES "initiative" ("issue_id", "id") ON DELETE CASCADE ON UPDATE CASCADE, |
jbe@0 | 432 FOREIGN KEY ("issue_id", "member_id") REFERENCES "direct_voter" ("issue_id", "member_id") ON DELETE CASCADE ON UPDATE CASCADE ); |
jbe@0 | 433 CREATE INDEX "vote_member_id_idx" ON "vote" ("member_id"); |
jbe@0 | 434 |
jbe@0 | 435 COMMENT ON TABLE "vote" IS 'Manual and delegated votes without abstentions'; |
jbe@0 | 436 |
jbe@0 | 437 COMMENT ON COLUMN "vote"."grade" IS 'Values smaller than zero mean reject, values greater than zero mean acceptance, zero or missing row means abstention. Preferences are expressed by different positive or negative numbers.'; |
jbe@0 | 438 |
jbe@0 | 439 |
jbe@0 | 440 |
jbe@0 | 441 ---------------------------- |
jbe@0 | 442 -- Additional constraints -- |
jbe@0 | 443 ---------------------------- |
jbe@0 | 444 |
jbe@0 | 445 |
jbe@0 | 446 CREATE FUNCTION "issue_requires_first_initiative_trigger"() |
jbe@0 | 447 RETURNS TRIGGER |
jbe@0 | 448 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 449 BEGIN |
jbe@0 | 450 IF NOT EXISTS ( |
jbe@0 | 451 SELECT NULL FROM "initiative" WHERE "issue_id" = NEW."id" |
jbe@0 | 452 ) THEN |
jbe@0 | 453 --RAISE 'Cannot create issue without an initial initiative.' USING |
jbe@0 | 454 -- ERRCODE = 'integrity_constraint_violation', |
jbe@0 | 455 -- HINT = 'Create issue, initiative, and draft within the same transaction.'; |
jbe@0 | 456 RAISE EXCEPTION 'Cannot create issue without an initial initiative.'; |
jbe@0 | 457 END IF; |
jbe@0 | 458 RETURN NULL; |
jbe@0 | 459 END; |
jbe@0 | 460 $$; |
jbe@0 | 461 |
jbe@0 | 462 CREATE CONSTRAINT TRIGGER "issue_requires_first_initiative" |
jbe@0 | 463 AFTER INSERT OR UPDATE ON "issue" DEFERRABLE INITIALLY DEFERRED |
jbe@0 | 464 FOR EACH ROW EXECUTE PROCEDURE |
jbe@0 | 465 "issue_requires_first_initiative_trigger"(); |
jbe@0 | 466 |
jbe@0 | 467 COMMENT ON FUNCTION "issue_requires_first_initiative_trigger"() IS 'Implementation of trigger "issue_requires_first_initiative" on table "issue"'; |
jbe@0 | 468 COMMENT ON TRIGGER "issue_requires_first_initiative" ON "issue" IS 'Ensure that new issues have at least one initiative'; |
jbe@0 | 469 |
jbe@0 | 470 |
jbe@0 | 471 CREATE FUNCTION "last_initiative_deletes_issue_trigger"() |
jbe@0 | 472 RETURNS TRIGGER |
jbe@0 | 473 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 474 DECLARE |
jbe@0 | 475 "reference_lost" BOOLEAN; |
jbe@0 | 476 BEGIN |
jbe@0 | 477 IF TG_OP = 'DELETE' THEN |
jbe@0 | 478 "reference_lost" := TRUE; |
jbe@0 | 479 ELSE |
jbe@0 | 480 "reference_lost" := NEW."issue_id" != OLD."issue_id"; |
jbe@0 | 481 END IF; |
jbe@0 | 482 IF |
jbe@0 | 483 "reference_lost" AND NOT EXISTS ( |
jbe@0 | 484 SELECT NULL FROM "initiative" WHERE "issue_id" = OLD."issue_id" |
jbe@0 | 485 ) |
jbe@0 | 486 THEN |
jbe@0 | 487 DELETE FROM "issue" WHERE "id" = OLD."issue_id"; |
jbe@0 | 488 END IF; |
jbe@0 | 489 RETURN NULL; |
jbe@0 | 490 END; |
jbe@0 | 491 $$; |
jbe@0 | 492 |
jbe@0 | 493 CREATE CONSTRAINT TRIGGER "last_initiative_deletes_issue" |
jbe@0 | 494 AFTER UPDATE OR DELETE ON "initiative" DEFERRABLE INITIALLY DEFERRED |
jbe@0 | 495 FOR EACH ROW EXECUTE PROCEDURE |
jbe@0 | 496 "last_initiative_deletes_issue_trigger"(); |
jbe@0 | 497 |
jbe@0 | 498 COMMENT ON FUNCTION "last_initiative_deletes_issue_trigger"() IS 'Implementation of trigger "last_initiative_deletes_issue" on table "initiative"'; |
jbe@0 | 499 COMMENT ON TRIGGER "last_initiative_deletes_issue" ON "initiative" IS 'Removing the last initiative of an issue deletes the issue'; |
jbe@0 | 500 |
jbe@0 | 501 |
jbe@0 | 502 CREATE FUNCTION "initiative_requires_first_draft_trigger"() |
jbe@0 | 503 RETURNS TRIGGER |
jbe@0 | 504 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 505 BEGIN |
jbe@0 | 506 IF NOT EXISTS ( |
jbe@0 | 507 SELECT NULL FROM "draft" WHERE "initiative_id" = NEW."id" |
jbe@0 | 508 ) THEN |
jbe@0 | 509 --RAISE 'Cannot create initiative without an initial draft.' USING |
jbe@0 | 510 -- ERRCODE = 'integrity_constraint_violation', |
jbe@0 | 511 -- HINT = 'Create issue, initiative and draft within the same transaction.'; |
jbe@0 | 512 RAISE EXCEPTION 'Cannot create initiative without an initial draft.'; |
jbe@0 | 513 END IF; |
jbe@0 | 514 RETURN NULL; |
jbe@0 | 515 END; |
jbe@0 | 516 $$; |
jbe@0 | 517 |
jbe@0 | 518 CREATE CONSTRAINT TRIGGER "initiative_requires_first_draft" |
jbe@0 | 519 AFTER INSERT OR UPDATE ON "initiative" DEFERRABLE INITIALLY DEFERRED |
jbe@0 | 520 FOR EACH ROW EXECUTE PROCEDURE |
jbe@0 | 521 "initiative_requires_first_draft_trigger"(); |
jbe@0 | 522 |
jbe@0 | 523 COMMENT ON FUNCTION "initiative_requires_first_draft_trigger"() IS 'Implementation of trigger "initiative_requires_first_draft" on table "initiative"'; |
jbe@0 | 524 COMMENT ON TRIGGER "initiative_requires_first_draft" ON "initiative" IS 'Ensure that new initiatives have at least one draft'; |
jbe@0 | 525 |
jbe@0 | 526 |
jbe@0 | 527 CREATE FUNCTION "last_draft_deletes_initiative_trigger"() |
jbe@0 | 528 RETURNS TRIGGER |
jbe@0 | 529 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 530 DECLARE |
jbe@0 | 531 "reference_lost" BOOLEAN; |
jbe@0 | 532 BEGIN |
jbe@0 | 533 IF TG_OP = 'DELETE' THEN |
jbe@0 | 534 "reference_lost" := TRUE; |
jbe@0 | 535 ELSE |
jbe@0 | 536 "reference_lost" := NEW."initiative_id" != OLD."initiative_id"; |
jbe@0 | 537 END IF; |
jbe@0 | 538 IF |
jbe@0 | 539 "reference_lost" AND NOT EXISTS ( |
jbe@0 | 540 SELECT NULL FROM "draft" WHERE "initiative_id" = OLD."initiative_id" |
jbe@0 | 541 ) |
jbe@0 | 542 THEN |
jbe@0 | 543 DELETE FROM "initiative" WHERE "id" = OLD."initiative_id"; |
jbe@0 | 544 END IF; |
jbe@0 | 545 RETURN NULL; |
jbe@0 | 546 END; |
jbe@0 | 547 $$; |
jbe@0 | 548 |
jbe@0 | 549 CREATE CONSTRAINT TRIGGER "last_draft_deletes_initiative" |
jbe@0 | 550 AFTER UPDATE OR DELETE ON "draft" DEFERRABLE INITIALLY DEFERRED |
jbe@0 | 551 FOR EACH ROW EXECUTE PROCEDURE |
jbe@0 | 552 "last_draft_deletes_initiative_trigger"(); |
jbe@0 | 553 |
jbe@0 | 554 COMMENT ON FUNCTION "last_draft_deletes_initiative_trigger"() IS 'Implementation of trigger "last_draft_deletes_initiative" on table "draft"'; |
jbe@0 | 555 COMMENT ON TRIGGER "last_draft_deletes_initiative" ON "draft" IS 'Removing the last draft of an initiative deletes the initiative'; |
jbe@0 | 556 |
jbe@0 | 557 |
jbe@0 | 558 CREATE FUNCTION "suggestion_requires_first_opinion_trigger"() |
jbe@0 | 559 RETURNS TRIGGER |
jbe@0 | 560 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 561 BEGIN |
jbe@0 | 562 IF NOT EXISTS ( |
jbe@0 | 563 SELECT NULL FROM "opinion" WHERE "suggestion_id" = NEW."id" |
jbe@0 | 564 ) THEN |
jbe@0 | 565 RAISE EXCEPTION 'Cannot create a suggestion without an opinion.'; |
jbe@0 | 566 END IF; |
jbe@0 | 567 RETURN NULL; |
jbe@0 | 568 END; |
jbe@0 | 569 $$; |
jbe@0 | 570 |
jbe@0 | 571 CREATE CONSTRAINT TRIGGER "suggestion_requires_first_opinion" |
jbe@0 | 572 AFTER INSERT OR UPDATE ON "suggestion" DEFERRABLE INITIALLY DEFERRED |
jbe@0 | 573 FOR EACH ROW EXECUTE PROCEDURE |
jbe@0 | 574 "suggestion_requires_first_opinion_trigger"(); |
jbe@0 | 575 |
jbe@0 | 576 COMMENT ON FUNCTION "suggestion_requires_first_opinion_trigger"() IS 'Implementation of trigger "suggestion_requires_first_opinion" on table "suggestion"'; |
jbe@0 | 577 COMMENT ON TRIGGER "suggestion_requires_first_opinion" ON "suggestion" IS 'Ensure that new suggestions have at least one opinion'; |
jbe@0 | 578 |
jbe@0 | 579 |
jbe@0 | 580 CREATE FUNCTION "last_opinion_deletes_suggestion_trigger"() |
jbe@0 | 581 RETURNS TRIGGER |
jbe@0 | 582 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 583 DECLARE |
jbe@0 | 584 "reference_lost" BOOLEAN; |
jbe@0 | 585 BEGIN |
jbe@0 | 586 IF TG_OP = 'DELETE' THEN |
jbe@0 | 587 "reference_lost" := TRUE; |
jbe@0 | 588 ELSE |
jbe@0 | 589 "reference_lost" := NEW."suggestion_id" != OLD."suggestion_id"; |
jbe@0 | 590 END IF; |
jbe@0 | 591 IF |
jbe@0 | 592 "reference_lost" AND NOT EXISTS ( |
jbe@0 | 593 SELECT NULL FROM "opinion" WHERE "suggestion_id" = OLD."suggestion_id" |
jbe@0 | 594 ) |
jbe@0 | 595 THEN |
jbe@0 | 596 DELETE FROM "suggestion" WHERE "id" = OLD."suggestion_id"; |
jbe@0 | 597 END IF; |
jbe@0 | 598 RETURN NULL; |
jbe@0 | 599 END; |
jbe@0 | 600 $$; |
jbe@0 | 601 |
jbe@0 | 602 CREATE CONSTRAINT TRIGGER "last_opinion_deletes_suggestion" |
jbe@0 | 603 AFTER UPDATE OR DELETE ON "opinion" DEFERRABLE INITIALLY DEFERRED |
jbe@0 | 604 FOR EACH ROW EXECUTE PROCEDURE |
jbe@0 | 605 "last_opinion_deletes_suggestion_trigger"(); |
jbe@0 | 606 |
jbe@0 | 607 COMMENT ON FUNCTION "last_opinion_deletes_suggestion_trigger"() IS 'Implementation of trigger "last_opinion_deletes_suggestion" on table "opinion"'; |
jbe@0 | 608 COMMENT ON TRIGGER "last_opinion_deletes_suggestion" ON "opinion" IS 'Removing the last opinion of a suggestion deletes the suggestion'; |
jbe@0 | 609 |
jbe@0 | 610 |
jbe@0 | 611 |
jbe@0 | 612 -------------------------------------------------------------------- |
jbe@0 | 613 -- Auto-retrieval of fields only needed for referential integrity -- |
jbe@0 | 614 -------------------------------------------------------------------- |
jbe@0 | 615 |
jbe@0 | 616 CREATE FUNCTION "autofill_issue_id_trigger"() |
jbe@0 | 617 RETURNS TRIGGER |
jbe@0 | 618 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 619 BEGIN |
jbe@0 | 620 IF NEW."issue_id" ISNULL THEN |
jbe@0 | 621 SELECT "issue_id" INTO NEW."issue_id" |
jbe@0 | 622 FROM "initiative" WHERE "id" = NEW."initiative_id"; |
jbe@0 | 623 END IF; |
jbe@0 | 624 RETURN NEW; |
jbe@0 | 625 END; |
jbe@0 | 626 $$; |
jbe@0 | 627 |
jbe@0 | 628 CREATE TRIGGER "autofill_issue_id" BEFORE INSERT ON "supporter" |
jbe@0 | 629 FOR EACH ROW EXECUTE PROCEDURE "autofill_issue_id_trigger"(); |
jbe@0 | 630 |
jbe@0 | 631 CREATE TRIGGER "autofill_issue_id" BEFORE INSERT ON "vote" |
jbe@0 | 632 FOR EACH ROW EXECUTE PROCEDURE "autofill_issue_id_trigger"(); |
jbe@0 | 633 |
jbe@0 | 634 COMMENT ON FUNCTION "autofill_issue_id_trigger"() IS 'Implementation of triggers "autofill_issue_id" on tables "supporter" and "vote"'; |
jbe@0 | 635 COMMENT ON TRIGGER "autofill_issue_id" ON "supporter" IS 'Set "issue_id" field automatically, if NULL'; |
jbe@0 | 636 COMMENT ON TRIGGER "autofill_issue_id" ON "vote" IS 'Set "issue_id" field automatically, if NULL'; |
jbe@0 | 637 |
jbe@0 | 638 |
jbe@0 | 639 CREATE FUNCTION "autofill_initiative_id_trigger"() |
jbe@0 | 640 RETURNS TRIGGER |
jbe@0 | 641 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 642 BEGIN |
jbe@0 | 643 IF NEW."initiative_id" ISNULL THEN |
jbe@0 | 644 SELECT "initiative_id" INTO NEW."initiative_id" |
jbe@0 | 645 FROM "suggestion" WHERE "id" = NEW."suggestion_id"; |
jbe@0 | 646 END IF; |
jbe@0 | 647 RETURN NEW; |
jbe@0 | 648 END; |
jbe@0 | 649 $$; |
jbe@0 | 650 |
jbe@0 | 651 CREATE TRIGGER "autofill_initiative_id" BEFORE INSERT ON "opinion" |
jbe@0 | 652 FOR EACH ROW EXECUTE PROCEDURE "autofill_initiative_id_trigger"(); |
jbe@0 | 653 |
jbe@0 | 654 COMMENT ON FUNCTION "autofill_initiative_id_trigger"() IS 'Implementation of trigger "autofill_initiative_id" on table "opinion"'; |
jbe@0 | 655 COMMENT ON TRIGGER "autofill_initiative_id" ON "opinion" IS 'Set "initiative_id" field automatically, if NULL'; |
jbe@0 | 656 |
jbe@0 | 657 |
jbe@0 | 658 |
jbe@0 | 659 ----------------------------------------------------------------------- |
jbe@0 | 660 -- Automatic copy of autoreject settings from membership to interest -- |
jbe@0 | 661 ----------------------------------------------------------------------- |
jbe@0 | 662 |
jbe@0 | 663 CREATE FUNCTION "copy_autoreject_trigger"() |
jbe@0 | 664 RETURNS TRIGGER |
jbe@0 | 665 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 666 BEGIN |
jbe@0 | 667 IF NEW."autoreject" ISNULL THEN |
jbe@0 | 668 SELECT "membership"."autoreject" INTO NEW."autoreject" |
jbe@0 | 669 FROM "issue" JOIN "membership" |
jbe@0 | 670 ON "issue"."area_id" = "membership"."area_id" |
jbe@0 | 671 WHERE "issue"."id" = NEW."issue_id" |
jbe@0 | 672 AND "membership"."member_id" = NEW."member_id"; |
jbe@0 | 673 END IF; |
jbe@0 | 674 IF NEW."autoreject" ISNULL THEN |
jbe@0 | 675 NEW."autoreject" := FALSE; |
jbe@0 | 676 END IF; |
jbe@0 | 677 RETURN NEW; |
jbe@0 | 678 END; |
jbe@0 | 679 $$; |
jbe@0 | 680 |
jbe@0 | 681 CREATE TRIGGER "copy_autoreject" BEFORE INSERT OR UPDATE ON "interest" |
jbe@0 | 682 FOR EACH ROW EXECUTE PROCEDURE "copy_autoreject_trigger"(); |
jbe@0 | 683 |
jbe@0 | 684 COMMENT ON FUNCTION "copy_autoreject_trigger"() IS 'Implementation of trigger "copy_autoreject" on table "interest"'; |
jbe@0 | 685 COMMENT ON TRIGGER "copy_autoreject" ON "interest" IS 'If "autoreject" is NULL, then copy it from the area setting, or set to FALSE, if no membership existent'; |
jbe@0 | 686 |
jbe@0 | 687 |
jbe@2 | 688 CREATE FUNCTION "supporter_default_for_draft_id_trigger"() |
jbe@2 | 689 RETURNS TRIGGER |
jbe@2 | 690 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@2 | 691 BEGIN |
jbe@2 | 692 IF NEW."draft_id" ISNULL THEN |
jbe@2 | 693 SELECT "id" INTO NEW."draft_id" FROM "current_draft" |
jbe@2 | 694 WHERE "initiative_id" = NEW."initiative_id"; |
jbe@2 | 695 END IF; |
jbe@2 | 696 RETURN NEW; |
jbe@2 | 697 END; |
jbe@2 | 698 $$; |
jbe@2 | 699 |
jbe@2 | 700 CREATE TRIGGER "default_for_draft_id" BEFORE INSERT OR UPDATE ON "supporter" |
jbe@2 | 701 FOR EACH ROW EXECUTE PROCEDURE "supporter_default_for_draft_id_trigger"(); |
jbe@2 | 702 |
jbe@2 | 703 COMMENT ON FUNCTION "supporter_default_for_draft_id_trigger"() IS 'Implementation of trigger "default_for_draft" on table "supporter"'; |
jbe@2 | 704 COMMENT ON TRIGGER "default_for_draft_id" ON "supporter" IS 'If "draft_id" is NULL, then use the current draft of the initiative as default'; |
jbe@2 | 705 |
jbe@2 | 706 |
jbe@0 | 707 |
jbe@0 | 708 ---------------------------------------- |
jbe@0 | 709 -- Automatic creation of dependencies -- |
jbe@0 | 710 ---------------------------------------- |
jbe@0 | 711 |
jbe@0 | 712 CREATE FUNCTION "autocreate_interest_trigger"() |
jbe@0 | 713 RETURNS TRIGGER |
jbe@0 | 714 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 715 BEGIN |
jbe@0 | 716 IF NOT EXISTS ( |
jbe@0 | 717 SELECT NULL FROM "initiative" JOIN "interest" |
jbe@0 | 718 ON "initiative"."issue_id" = "interest"."issue_id" |
jbe@0 | 719 WHERE "initiative"."id" = NEW."initiative_id" |
jbe@0 | 720 AND "interest"."member_id" = NEW."member_id" |
jbe@0 | 721 ) THEN |
jbe@0 | 722 BEGIN |
jbe@0 | 723 INSERT INTO "interest" ("issue_id", "member_id") |
jbe@0 | 724 SELECT "issue_id", NEW."member_id" |
jbe@0 | 725 FROM "initiative" WHERE "id" = NEW."initiative_id"; |
jbe@0 | 726 EXCEPTION WHEN unique_violation THEN END; |
jbe@0 | 727 END IF; |
jbe@0 | 728 RETURN NEW; |
jbe@0 | 729 END; |
jbe@0 | 730 $$; |
jbe@0 | 731 |
jbe@0 | 732 CREATE TRIGGER "autocreate_interest" BEFORE INSERT ON "supporter" |
jbe@0 | 733 FOR EACH ROW EXECUTE PROCEDURE "autocreate_interest_trigger"(); |
jbe@0 | 734 |
jbe@0 | 735 COMMENT ON FUNCTION "autocreate_interest_trigger"() IS 'Implementation of trigger "autocreate_interest" on table "supporter"'; |
jbe@0 | 736 COMMENT ON TRIGGER "autocreate_interest" ON "supporter" IS 'Supporting an initiative implies interest in the issue, thus automatically creates an entry in the "interest" table'; |
jbe@0 | 737 |
jbe@0 | 738 |
jbe@0 | 739 CREATE FUNCTION "autocreate_supporter_trigger"() |
jbe@0 | 740 RETURNS TRIGGER |
jbe@0 | 741 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 742 BEGIN |
jbe@0 | 743 IF NOT EXISTS ( |
jbe@0 | 744 SELECT NULL FROM "suggestion" JOIN "supporter" |
jbe@0 | 745 ON "suggestion"."initiative_id" = "supporter"."initiative_id" |
jbe@0 | 746 WHERE "suggestion"."id" = NEW."suggestion_id" |
jbe@0 | 747 AND "supporter"."member_id" = NEW."member_id" |
jbe@0 | 748 ) THEN |
jbe@0 | 749 BEGIN |
jbe@0 | 750 INSERT INTO "supporter" ("initiative_id", "member_id") |
jbe@0 | 751 SELECT "initiative_id", NEW."member_id" |
jbe@0 | 752 FROM "suggestion" WHERE "id" = NEW."suggestion_id"; |
jbe@0 | 753 EXCEPTION WHEN unique_violation THEN END; |
jbe@0 | 754 END IF; |
jbe@0 | 755 RETURN NEW; |
jbe@0 | 756 END; |
jbe@0 | 757 $$; |
jbe@0 | 758 |
jbe@0 | 759 CREATE TRIGGER "autocreate_supporter" BEFORE INSERT ON "opinion" |
jbe@0 | 760 FOR EACH ROW EXECUTE PROCEDURE "autocreate_supporter_trigger"(); |
jbe@0 | 761 |
jbe@0 | 762 COMMENT ON FUNCTION "autocreate_supporter_trigger"() IS 'Implementation of trigger "autocreate_supporter" on table "opinion"'; |
jbe@0 | 763 COMMENT ON TRIGGER "autocreate_supporter" ON "opinion" IS 'Opinions can only be added for supported initiatives. This trigger automatrically creates an entry in the "supporter" table, if not existent yet.'; |
jbe@0 | 764 |
jbe@0 | 765 |
jbe@0 | 766 |
jbe@0 | 767 ------------------------------------------ |
jbe@0 | 768 -- Views and helper functions for views -- |
jbe@0 | 769 ------------------------------------------ |
jbe@0 | 770 |
jbe@0 | 771 CREATE VIEW "issue_delegation_with_overridden_and_inactive" AS |
jbe@0 | 772 SELECT "delegation".*, "issue"."id" AS "resulting_issue_id" |
jbe@0 | 773 FROM "delegation" |
jbe@0 | 774 JOIN "issue" ON |
jbe@0 | 775 ("delegation"."area_id" ISNULL AND "delegation"."issue_id" ISNULL) OR |
jbe@0 | 776 "delegation"."area_id" = "issue"."area_id" OR |
jbe@0 | 777 "delegation"."issue_id" = "issue"."id"; |
jbe@0 | 778 |
jbe@0 | 779 COMMENT ON VIEW "issue_delegation_with_overridden_and_inactive" IS 'Helper view for "issue_delegation"'; |
jbe@0 | 780 |
jbe@0 | 781 |
jbe@0 | 782 CREATE VIEW "issue_delegation" AS |
jbe@0 | 783 SELECT |
jbe@0 | 784 "entry"."id" AS "id", |
jbe@0 | 785 "entry"."truster_id" AS "truster_id", |
jbe@0 | 786 "entry"."trustee_id" AS "trustee_id", |
jbe@0 | 787 "entry"."resulting_issue_id" AS "issue_id" |
jbe@0 | 788 FROM "issue_delegation_with_overridden_and_inactive" AS "entry" |
jbe@0 | 789 JOIN "member" AS "truster" ON "entry"."truster_id" = "truster"."id" |
jbe@0 | 790 JOIN "member" AS "trustee" ON "entry"."trustee_id" = "trustee"."id" |
jbe@0 | 791 LEFT JOIN "issue_delegation_with_overridden_and_inactive" AS "override" |
jbe@0 | 792 ON "entry"."truster_id" = "override"."truster_id" |
jbe@0 | 793 AND "entry"."id" != "override"."id" |
jbe@0 | 794 AND ( |
jbe@0 | 795 ("entry"."area_id" ISNULL AND "entry"."issue_id" ISNULL) OR |
jbe@0 | 796 "override"."issue_id" NOTNULL |
jbe@0 | 797 ) |
jbe@0 | 798 WHERE "truster"."active" AND "trustee"."active" |
jbe@0 | 799 AND "override"."truster_id" ISNULL; |
jbe@0 | 800 |
jbe@0 | 801 COMMENT ON VIEW "issue_delegation" IS 'Resulting delegations for issues, without those involving inactive members'; |
jbe@0 | 802 |
jbe@0 | 803 |
jbe@0 | 804 CREATE VIEW "current_draft" AS |
jbe@0 | 805 SELECT "draft".* FROM ( |
jbe@0 | 806 SELECT |
jbe@0 | 807 "initiative"."id" AS "initiative_id", |
jbe@0 | 808 max("draft"."id") AS "draft_id" |
jbe@0 | 809 FROM "initiative" JOIN "draft" |
jbe@0 | 810 ON "initiative"."id" = "draft"."initiative_id" |
jbe@0 | 811 GROUP BY "initiative"."id" |
jbe@0 | 812 ) AS "subquery" |
jbe@0 | 813 JOIN "draft" ON "subquery"."draft_id" = "draft"."id"; |
jbe@0 | 814 |
jbe@0 | 815 COMMENT ON VIEW "current_draft" IS 'All latest drafts for each initiative'; |
jbe@0 | 816 |
jbe@0 | 817 |
jbe@0 | 818 CREATE VIEW "critical_opinion" AS |
jbe@0 | 819 SELECT * FROM "opinion" |
jbe@0 | 820 WHERE ("degree" = 2 AND "fulfilled" = FALSE) |
jbe@0 | 821 OR ("degree" = -2 AND "fulfilled" = TRUE); |
jbe@0 | 822 |
jbe@0 | 823 COMMENT ON VIEW "critical_opinion" IS 'Opinions currently causing dissatisfaction'; |
jbe@0 | 824 |
jbe@0 | 825 |
jbe@0 | 826 CREATE VIEW "battle_participant" AS |
jbe@0 | 827 SELECT "issue_id", "id" AS "initiative_id" FROM "initiative" |
jbe@0 | 828 WHERE "admitted" |
jbe@0 | 829 AND "positive_votes" > "negative_votes"; |
jbe@0 | 830 |
jbe@0 | 831 COMMENT ON VIEW "battle_participant" IS 'Helper view for "battle" view'; |
jbe@0 | 832 |
jbe@0 | 833 |
jbe@0 | 834 CREATE VIEW "battle" AS |
jbe@0 | 835 SELECT |
jbe@0 | 836 "issue"."id" AS "issue_id", |
jbe@0 | 837 "winning_initiative"."initiative_id" AS "winning_initiative_id", |
jbe@0 | 838 "losing_initiative"."initiative_id" AS "losing_initiative_id", |
jbe@0 | 839 sum( |
jbe@0 | 840 CASE WHEN |
jbe@0 | 841 coalesce("better_vote"."grade", 0) > |
jbe@0 | 842 coalesce("worse_vote"."grade", 0) |
jbe@0 | 843 THEN "direct_voter"."weight" ELSE 0 END |
jbe@0 | 844 ) AS "count" |
jbe@0 | 845 FROM "issue" |
jbe@0 | 846 LEFT JOIN "direct_voter" |
jbe@0 | 847 ON "issue"."id" = "direct_voter"."issue_id" |
jbe@0 | 848 JOIN "battle_participant" AS "winning_initiative" |
jbe@0 | 849 ON "issue"."id" = "winning_initiative"."issue_id" |
jbe@0 | 850 JOIN "battle_participant" AS "losing_initiative" |
jbe@0 | 851 ON "issue"."id" = "losing_initiative"."issue_id" |
jbe@0 | 852 LEFT JOIN "vote" AS "better_vote" |
jbe@0 | 853 ON "direct_voter"."member_id" = "better_vote"."member_id" |
jbe@0 | 854 AND "winning_initiative"."initiative_id" = "better_vote"."initiative_id" |
jbe@0 | 855 LEFT JOIN "vote" AS "worse_vote" |
jbe@0 | 856 ON "direct_voter"."member_id" = "worse_vote"."member_id" |
jbe@0 | 857 AND "losing_initiative"."initiative_id" = "worse_vote"."initiative_id" |
jbe@0 | 858 WHERE |
jbe@0 | 859 "winning_initiative"."initiative_id" != |
jbe@0 | 860 "losing_initiative"."initiative_id" |
jbe@0 | 861 GROUP BY |
jbe@0 | 862 "issue"."id", |
jbe@0 | 863 "winning_initiative"."initiative_id", |
jbe@0 | 864 "losing_initiative"."initiative_id"; |
jbe@0 | 865 |
jbe@0 | 866 COMMENT ON VIEW "battle" IS 'Number of members preferring one initiative over another'; |
jbe@0 | 867 |
jbe@0 | 868 |
jbe@1 | 869 CREATE VIEW "expired_session" AS |
jbe@1 | 870 SELECT * FROM "session" WHERE now() > "expiry"; |
jbe@1 | 871 |
jbe@1 | 872 CREATE RULE "delete" AS ON DELETE TO "expired_session" DO INSTEAD |
jbe@1 | 873 DELETE FROM "session" WHERE "ident" = OLD."ident"; |
jbe@1 | 874 |
jbe@1 | 875 COMMENT ON VIEW "expired_session" IS 'View containing all expired sessions where DELETE is possible'; |
jbe@1 | 876 COMMENT ON RULE "delete" ON "expired_session" IS 'Rule allowing DELETE on rows in "expired_session" view, i.e. DELETE FROM "expired_session"'; |
jbe@1 | 877 |
jbe@1 | 878 |
jbe@0 | 879 CREATE VIEW "open_issue" AS |
jbe@0 | 880 SELECT * FROM "issue" WHERE "closed" ISNULL; |
jbe@0 | 881 |
jbe@0 | 882 COMMENT ON VIEW "open_issue" IS 'All open issues'; |
jbe@0 | 883 |
jbe@0 | 884 |
jbe@0 | 885 CREATE VIEW "issue_with_ranks_missing" AS |
jbe@0 | 886 SELECT * FROM "issue" |
jbe@3 | 887 WHERE "fully_frozen" NOTNULL |
jbe@0 | 888 AND "closed" NOTNULL |
jbe@0 | 889 AND "ranks_available" = FALSE; |
jbe@0 | 890 |
jbe@0 | 891 COMMENT ON VIEW "issue_with_ranks_missing" IS 'Issues where voting was finished, but no ranks have been calculated yet'; |
jbe@0 | 892 |
jbe@0 | 893 |
jbe@0 | 894 |
jbe@0 | 895 ------------------------------ |
jbe@0 | 896 -- Comparison by vote count -- |
jbe@0 | 897 ------------------------------ |
jbe@0 | 898 |
jbe@0 | 899 CREATE FUNCTION "vote_ratio" |
jbe@0 | 900 ( "positive_votes_p" "initiative"."positive_votes"%TYPE, |
jbe@0 | 901 "negative_votes_p" "initiative"."negative_votes"%TYPE ) |
jbe@0 | 902 RETURNS FLOAT8 |
jbe@0 | 903 LANGUAGE 'plpgsql' STABLE AS $$ |
jbe@0 | 904 DECLARE |
jbe@0 | 905 "total_v" INT4; |
jbe@0 | 906 BEGIN |
jbe@0 | 907 "total_v" := "positive_votes_p" + "negative_votes_p"; |
jbe@0 | 908 IF "total_v" > 0 THEN |
jbe@0 | 909 RETURN "positive_votes_p"::FLOAT8 / "total_v"::FLOAT8; |
jbe@0 | 910 ELSE |
jbe@0 | 911 RETURN 0.5; |
jbe@0 | 912 END IF; |
jbe@0 | 913 END; |
jbe@0 | 914 $$; |
jbe@0 | 915 |
jbe@0 | 916 COMMENT ON FUNCTION "vote_ratio" |
jbe@0 | 917 ( "initiative"."positive_votes"%TYPE, |
jbe@0 | 918 "initiative"."negative_votes"%TYPE ) |
jbe@0 | 919 IS 'Ratio of positive votes to sum of positive and negative votes; 0.5, if there are neither positive nor negative votes'; |
jbe@0 | 920 |
jbe@0 | 921 |
jbe@0 | 922 |
jbe@0 | 923 ------------------------------------------------ |
jbe@0 | 924 -- Locking for snapshots and voting procedure -- |
jbe@0 | 925 ------------------------------------------------ |
jbe@0 | 926 |
jbe@0 | 927 CREATE FUNCTION "global_lock"() RETURNS VOID |
jbe@0 | 928 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 929 BEGIN |
jbe@0 | 930 -- NOTE: PostgreSQL allows reading, while tables are locked in |
jbe@0 | 931 -- exclusive move. Transactions should be kept short anyway! |
jbe@0 | 932 LOCK TABLE "member" IN EXCLUSIVE MODE; |
jbe@0 | 933 LOCK TABLE "policy" IN EXCLUSIVE MODE; |
jbe@0 | 934 LOCK TABLE "area" IN EXCLUSIVE MODE; |
jbe@0 | 935 LOCK TABLE "issue" IN EXCLUSIVE MODE; |
jbe@0 | 936 LOCK TABLE "initiative" IN EXCLUSIVE MODE; |
jbe@0 | 937 LOCK TABLE "draft" IN EXCLUSIVE MODE; |
jbe@0 | 938 LOCK TABLE "suggestion" IN EXCLUSIVE MODE; |
jbe@0 | 939 LOCK TABLE "membership" IN EXCLUSIVE MODE; |
jbe@0 | 940 LOCK TABLE "interest" IN EXCLUSIVE MODE; |
jbe@0 | 941 LOCK TABLE "initiator" IN EXCLUSIVE MODE; |
jbe@0 | 942 LOCK TABLE "supporter" IN EXCLUSIVE MODE; |
jbe@0 | 943 LOCK TABLE "opinion" IN EXCLUSIVE MODE; |
jbe@0 | 944 LOCK TABLE "delegation" IN EXCLUSIVE MODE; |
jbe@0 | 945 LOCK TABLE "direct_population_snapshot" IN EXCLUSIVE MODE; |
jbe@0 | 946 LOCK TABLE "delegating_population_snapshot" IN EXCLUSIVE MODE; |
jbe@0 | 947 LOCK TABLE "direct_interest_snapshot" IN EXCLUSIVE MODE; |
jbe@0 | 948 LOCK TABLE "delegating_interest_snapshot" IN EXCLUSIVE MODE; |
jbe@0 | 949 LOCK TABLE "direct_supporter_snapshot" IN EXCLUSIVE MODE; |
jbe@0 | 950 LOCK TABLE "direct_voter" IN EXCLUSIVE MODE; |
jbe@0 | 951 LOCK TABLE "delegating_voter" IN EXCLUSIVE MODE; |
jbe@0 | 952 LOCK TABLE "vote" IN EXCLUSIVE MODE; |
jbe@0 | 953 RETURN; |
jbe@0 | 954 END; |
jbe@0 | 955 $$; |
jbe@0 | 956 |
jbe@0 | 957 COMMENT ON FUNCTION "global_lock"() IS 'Locks all tables related to support/voting until end of transaction; read access is still possible though'; |
jbe@0 | 958 |
jbe@0 | 959 |
jbe@0 | 960 |
jbe@0 | 961 ------------------------------ |
jbe@0 | 962 -- Calculation of snapshots -- |
jbe@0 | 963 ------------------------------ |
jbe@0 | 964 |
jbe@0 | 965 CREATE FUNCTION "weight_of_added_delegations_for_population_snapshot" |
jbe@0 | 966 ( "issue_id_p" "issue"."id"%TYPE, |
jbe@0 | 967 "member_id_p" "member"."id"%TYPE, |
jbe@0 | 968 "delegate_member_ids_p" "delegating_population_snapshot"."delegate_member_ids"%TYPE ) |
jbe@0 | 969 RETURNS "direct_population_snapshot"."weight"%TYPE |
jbe@0 | 970 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 971 DECLARE |
jbe@0 | 972 "issue_delegation_row" "issue_delegation"%ROWTYPE; |
jbe@0 | 973 "delegate_member_ids_v" "delegating_population_snapshot"."delegate_member_ids"%TYPE; |
jbe@0 | 974 "weight_v" INT4; |
jbe@0 | 975 BEGIN |
jbe@0 | 976 "weight_v" := 0; |
jbe@0 | 977 FOR "issue_delegation_row" IN |
jbe@0 | 978 SELECT * FROM "issue_delegation" |
jbe@0 | 979 WHERE "trustee_id" = "member_id_p" |
jbe@0 | 980 AND "issue_id" = "issue_id_p" |
jbe@0 | 981 LOOP |
jbe@0 | 982 IF NOT EXISTS ( |
jbe@0 | 983 SELECT NULL FROM "direct_population_snapshot" |
jbe@0 | 984 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 985 AND "event" = 'periodic' |
jbe@0 | 986 AND "member_id" = "issue_delegation_row"."truster_id" |
jbe@0 | 987 ) AND NOT EXISTS ( |
jbe@0 | 988 SELECT NULL FROM "delegating_population_snapshot" |
jbe@0 | 989 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 990 AND "event" = 'periodic' |
jbe@0 | 991 AND "member_id" = "issue_delegation_row"."truster_id" |
jbe@0 | 992 ) THEN |
jbe@0 | 993 "delegate_member_ids_v" := |
jbe@0 | 994 "member_id_p" || "delegate_member_ids_p"; |
jbe@0 | 995 INSERT INTO "delegating_population_snapshot" |
jbe@0 | 996 ("issue_id", "event", "member_id", "delegate_member_ids") |
jbe@0 | 997 VALUES ( |
jbe@0 | 998 "issue_id_p", |
jbe@0 | 999 'periodic', |
jbe@0 | 1000 "issue_delegation_row"."truster_id", |
jbe@0 | 1001 "delegate_member_ids_v" |
jbe@0 | 1002 ); |
jbe@0 | 1003 "weight_v" := "weight_v" + 1 + |
jbe@0 | 1004 "weight_of_added_delegations_for_population_snapshot"( |
jbe@0 | 1005 "issue_id_p", |
jbe@0 | 1006 "issue_delegation_row"."truster_id", |
jbe@0 | 1007 "delegate_member_ids_v" |
jbe@0 | 1008 ); |
jbe@0 | 1009 END IF; |
jbe@0 | 1010 END LOOP; |
jbe@0 | 1011 RETURN "weight_v"; |
jbe@0 | 1012 END; |
jbe@0 | 1013 $$; |
jbe@0 | 1014 |
jbe@0 | 1015 COMMENT ON FUNCTION "weight_of_added_delegations_for_population_snapshot" |
jbe@0 | 1016 ( "issue"."id"%TYPE, |
jbe@0 | 1017 "member"."id"%TYPE, |
jbe@0 | 1018 "delegating_population_snapshot"."delegate_member_ids"%TYPE ) |
jbe@0 | 1019 IS 'Helper function for "create_population_snapshot" function'; |
jbe@0 | 1020 |
jbe@0 | 1021 |
jbe@0 | 1022 CREATE FUNCTION "create_population_snapshot" |
jbe@0 | 1023 ( "issue_id_p" "issue"."id"%TYPE ) |
jbe@0 | 1024 RETURNS VOID |
jbe@0 | 1025 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1026 DECLARE |
jbe@0 | 1027 "member_id_v" "member"."id"%TYPE; |
jbe@0 | 1028 BEGIN |
jbe@0 | 1029 DELETE FROM "direct_population_snapshot" |
jbe@0 | 1030 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1031 AND "event" = 'periodic'; |
jbe@0 | 1032 DELETE FROM "delegating_population_snapshot" |
jbe@0 | 1033 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1034 AND "event" = 'periodic'; |
jbe@0 | 1035 INSERT INTO "direct_population_snapshot" |
jbe@0 | 1036 ("issue_id", "event", "member_id", "interest_exists") |
jbe@0 | 1037 SELECT DISTINCT ON ("issue_id", "member_id") |
jbe@0 | 1038 "issue_id_p" AS "issue_id", |
jbe@0 | 1039 'periodic' AS "event", |
jbe@0 | 1040 "subquery"."member_id", |
jbe@0 | 1041 "subquery"."interest_exists" |
jbe@0 | 1042 FROM ( |
jbe@0 | 1043 SELECT |
jbe@0 | 1044 "member"."id" AS "member_id", |
jbe@0 | 1045 FALSE AS "interest_exists" |
jbe@0 | 1046 FROM "issue" |
jbe@0 | 1047 JOIN "area" ON "issue"."area_id" = "area"."id" |
jbe@0 | 1048 JOIN "membership" ON "area"."id" = "membership"."area_id" |
jbe@0 | 1049 JOIN "member" ON "membership"."member_id" = "member"."id" |
jbe@0 | 1050 WHERE "issue"."id" = "issue_id_p" |
jbe@0 | 1051 AND "member"."active" |
jbe@0 | 1052 UNION |
jbe@0 | 1053 SELECT |
jbe@0 | 1054 "member"."id" AS "member_id", |
jbe@0 | 1055 TRUE AS "interest_exists" |
jbe@0 | 1056 FROM "interest" JOIN "member" |
jbe@0 | 1057 ON "interest"."member_id" = "member"."id" |
jbe@0 | 1058 WHERE "interest"."issue_id" = "issue_id_p" |
jbe@0 | 1059 AND "member"."active" |
jbe@0 | 1060 ) AS "subquery" |
jbe@0 | 1061 ORDER BY |
jbe@0 | 1062 "issue_id_p", |
jbe@0 | 1063 "subquery"."member_id", |
jbe@0 | 1064 "subquery"."interest_exists" DESC; |
jbe@0 | 1065 FOR "member_id_v" IN |
jbe@0 | 1066 SELECT "member_id" FROM "direct_population_snapshot" |
jbe@0 | 1067 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1068 AND "event" = 'periodic' |
jbe@0 | 1069 LOOP |
jbe@0 | 1070 UPDATE "direct_population_snapshot" SET |
jbe@0 | 1071 "weight" = 1 + |
jbe@0 | 1072 "weight_of_added_delegations_for_population_snapshot"( |
jbe@0 | 1073 "issue_id_p", |
jbe@0 | 1074 "member_id_v", |
jbe@0 | 1075 '{}' |
jbe@0 | 1076 ) |
jbe@0 | 1077 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1078 AND "event" = 'periodic' |
jbe@0 | 1079 AND "member_id" = "member_id_v"; |
jbe@0 | 1080 END LOOP; |
jbe@0 | 1081 RETURN; |
jbe@0 | 1082 END; |
jbe@0 | 1083 $$; |
jbe@0 | 1084 |
jbe@0 | 1085 COMMENT ON FUNCTION "create_population_snapshot" |
jbe@0 | 1086 ( "issue_id_p" "issue"."id"%TYPE ) |
jbe@0 | 1087 IS 'This function creates a new ''periodic'' population snapshot for the given issue. It does neither lock any tables, nor updates precalculated values in other tables.'; |
jbe@0 | 1088 |
jbe@0 | 1089 |
jbe@0 | 1090 CREATE FUNCTION "weight_of_added_delegations_for_interest_snapshot" |
jbe@0 | 1091 ( "issue_id_p" "issue"."id"%TYPE, |
jbe@0 | 1092 "member_id_p" "member"."id"%TYPE, |
jbe@0 | 1093 "delegate_member_ids_p" "delegating_interest_snapshot"."delegate_member_ids"%TYPE ) |
jbe@0 | 1094 RETURNS "direct_interest_snapshot"."weight"%TYPE |
jbe@0 | 1095 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1096 DECLARE |
jbe@0 | 1097 "issue_delegation_row" "issue_delegation"%ROWTYPE; |
jbe@0 | 1098 "delegate_member_ids_v" "delegating_interest_snapshot"."delegate_member_ids"%TYPE; |
jbe@0 | 1099 "weight_v" INT4; |
jbe@0 | 1100 BEGIN |
jbe@0 | 1101 "weight_v" := 0; |
jbe@0 | 1102 FOR "issue_delegation_row" IN |
jbe@0 | 1103 SELECT * FROM "issue_delegation" |
jbe@0 | 1104 WHERE "trustee_id" = "member_id_p" |
jbe@0 | 1105 AND "issue_id" = "issue_id_p" |
jbe@0 | 1106 LOOP |
jbe@0 | 1107 IF NOT EXISTS ( |
jbe@0 | 1108 SELECT NULL FROM "direct_interest_snapshot" |
jbe@0 | 1109 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1110 AND "event" = 'periodic' |
jbe@0 | 1111 AND "member_id" = "issue_delegation_row"."truster_id" |
jbe@0 | 1112 ) AND NOT EXISTS ( |
jbe@0 | 1113 SELECT NULL FROM "delegating_interest_snapshot" |
jbe@0 | 1114 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1115 AND "event" = 'periodic' |
jbe@0 | 1116 AND "member_id" = "issue_delegation_row"."truster_id" |
jbe@0 | 1117 ) THEN |
jbe@0 | 1118 "delegate_member_ids_v" := |
jbe@0 | 1119 "member_id_p" || "delegate_member_ids_p"; |
jbe@0 | 1120 INSERT INTO "delegating_interest_snapshot" |
jbe@0 | 1121 ("issue_id", "event", "member_id", "delegate_member_ids") |
jbe@0 | 1122 VALUES ( |
jbe@0 | 1123 "issue_id_p", |
jbe@0 | 1124 'periodic', |
jbe@0 | 1125 "issue_delegation_row"."truster_id", |
jbe@0 | 1126 "delegate_member_ids_v" |
jbe@0 | 1127 ); |
jbe@0 | 1128 "weight_v" := "weight_v" + 1 + |
jbe@0 | 1129 "weight_of_added_delegations_for_interest_snapshot"( |
jbe@0 | 1130 "issue_id_p", |
jbe@0 | 1131 "issue_delegation_row"."truster_id", |
jbe@0 | 1132 "delegate_member_ids_v" |
jbe@0 | 1133 ); |
jbe@0 | 1134 END IF; |
jbe@0 | 1135 END LOOP; |
jbe@0 | 1136 RETURN "weight_v"; |
jbe@0 | 1137 END; |
jbe@0 | 1138 $$; |
jbe@0 | 1139 |
jbe@0 | 1140 COMMENT ON FUNCTION "weight_of_added_delegations_for_interest_snapshot" |
jbe@0 | 1141 ( "issue"."id"%TYPE, |
jbe@0 | 1142 "member"."id"%TYPE, |
jbe@0 | 1143 "delegating_interest_snapshot"."delegate_member_ids"%TYPE ) |
jbe@0 | 1144 IS 'Helper function for "create_interest_snapshot" function'; |
jbe@0 | 1145 |
jbe@0 | 1146 |
jbe@0 | 1147 CREATE FUNCTION "create_interest_snapshot" |
jbe@0 | 1148 ( "issue_id_p" "issue"."id"%TYPE ) |
jbe@0 | 1149 RETURNS VOID |
jbe@0 | 1150 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1151 DECLARE |
jbe@0 | 1152 "member_id_v" "member"."id"%TYPE; |
jbe@0 | 1153 BEGIN |
jbe@0 | 1154 DELETE FROM "direct_interest_snapshot" |
jbe@0 | 1155 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1156 AND "event" = 'periodic'; |
jbe@0 | 1157 DELETE FROM "delegating_interest_snapshot" |
jbe@0 | 1158 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1159 AND "event" = 'periodic'; |
jbe@0 | 1160 DELETE FROM "direct_supporter_snapshot" |
jbe@0 | 1161 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1162 AND "event" = 'periodic'; |
jbe@0 | 1163 INSERT INTO "direct_interest_snapshot" |
jbe@0 | 1164 ("issue_id", "event", "member_id", "voting_requested") |
jbe@0 | 1165 SELECT |
jbe@0 | 1166 "issue_id_p" AS "issue_id", |
jbe@0 | 1167 'periodic' AS "event", |
jbe@0 | 1168 "member"."id" AS "member_id", |
jbe@0 | 1169 "interest"."voting_requested" |
jbe@0 | 1170 FROM "interest" JOIN "member" |
jbe@0 | 1171 ON "interest"."member_id" = "member"."id" |
jbe@0 | 1172 WHERE "interest"."issue_id" = "issue_id_p" |
jbe@0 | 1173 AND "member"."active"; |
jbe@0 | 1174 FOR "member_id_v" IN |
jbe@0 | 1175 SELECT "member_id" FROM "direct_interest_snapshot" |
jbe@0 | 1176 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1177 AND "event" = 'periodic' |
jbe@0 | 1178 LOOP |
jbe@0 | 1179 UPDATE "direct_interest_snapshot" SET |
jbe@0 | 1180 "weight" = 1 + |
jbe@0 | 1181 "weight_of_added_delegations_for_interest_snapshot"( |
jbe@0 | 1182 "issue_id_p", |
jbe@0 | 1183 "member_id_v", |
jbe@0 | 1184 '{}' |
jbe@0 | 1185 ) |
jbe@0 | 1186 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1187 AND "event" = 'periodic' |
jbe@0 | 1188 AND "member_id" = "member_id_v"; |
jbe@0 | 1189 END LOOP; |
jbe@0 | 1190 INSERT INTO "direct_supporter_snapshot" |
jbe@0 | 1191 ( "issue_id", "initiative_id", "event", "member_id", |
jbe@0 | 1192 "informed", "satisfied" ) |
jbe@0 | 1193 SELECT |
jbe@0 | 1194 "issue_id_p" AS "issue_id", |
jbe@0 | 1195 "initiative"."id" AS "initiative_id", |
jbe@0 | 1196 'periodic' AS "event", |
jbe@0 | 1197 "member"."id" AS "member_id", |
jbe@0 | 1198 "supporter"."draft_id" = "current_draft"."id" AS "informed", |
jbe@0 | 1199 NOT EXISTS ( |
jbe@0 | 1200 SELECT NULL FROM "critical_opinion" |
jbe@0 | 1201 WHERE "initiative_id" = "initiative"."id" |
jbe@0 | 1202 AND "member_id" = "member"."id" |
jbe@0 | 1203 ) AS "satisfied" |
jbe@0 | 1204 FROM "supporter" |
jbe@0 | 1205 JOIN "member" |
jbe@0 | 1206 ON "supporter"."member_id" = "member"."id" |
jbe@0 | 1207 JOIN "initiative" |
jbe@0 | 1208 ON "supporter"."initiative_id" = "initiative"."id" |
jbe@0 | 1209 JOIN "current_draft" |
jbe@0 | 1210 ON "initiative"."id" = "current_draft"."initiative_id" |
jbe@0 | 1211 JOIN "direct_interest_snapshot" |
jbe@0 | 1212 ON "member"."id" = "direct_interest_snapshot"."member_id" |
jbe@0 | 1213 AND "initiative"."issue_id" = "direct_interest_snapshot"."issue_id" |
jbe@3 | 1214 AND "event" = 'periodic' |
jbe@0 | 1215 WHERE "member"."active" |
jbe@0 | 1216 AND "initiative"."issue_id" = "issue_id_p"; |
jbe@0 | 1217 RETURN; |
jbe@0 | 1218 END; |
jbe@0 | 1219 $$; |
jbe@0 | 1220 |
jbe@0 | 1221 COMMENT ON FUNCTION "create_interest_snapshot" |
jbe@0 | 1222 ( "issue"."id"%TYPE ) |
jbe@0 | 1223 IS 'This function creates a new ''periodic'' interest/supporter snapshot for the given issue. It does neither lock any tables, nor updates precalculated values in other tables.'; |
jbe@0 | 1224 |
jbe@0 | 1225 |
jbe@0 | 1226 CREATE FUNCTION "create_snapshot" |
jbe@0 | 1227 ( "issue_id_p" "issue"."id"%TYPE ) |
jbe@0 | 1228 RETURNS VOID |
jbe@0 | 1229 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1230 DECLARE |
jbe@0 | 1231 "initiative_id_v" "initiative"."id"%TYPE; |
jbe@0 | 1232 "suggestion_id_v" "suggestion"."id"%TYPE; |
jbe@0 | 1233 BEGIN |
jbe@0 | 1234 PERFORM "global_lock"(); |
jbe@0 | 1235 PERFORM "create_population_snapshot"("issue_id_p"); |
jbe@0 | 1236 PERFORM "create_interest_snapshot"("issue_id_p"); |
jbe@0 | 1237 UPDATE "issue" SET |
jbe@0 | 1238 "snapshot" = now(), |
jbe@0 | 1239 "population" = ( |
jbe@0 | 1240 SELECT coalesce(sum("weight"), 0) |
jbe@0 | 1241 FROM "direct_population_snapshot" |
jbe@0 | 1242 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1243 AND "event" = 'periodic' |
jbe@0 | 1244 ), |
jbe@0 | 1245 "vote_now" = ( |
jbe@0 | 1246 SELECT coalesce(sum("weight"), 0) |
jbe@0 | 1247 FROM "direct_interest_snapshot" |
jbe@0 | 1248 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1249 AND "event" = 'periodic' |
jbe@0 | 1250 AND "voting_requested" = TRUE |
jbe@0 | 1251 ), |
jbe@0 | 1252 "vote_later" = ( |
jbe@0 | 1253 SELECT coalesce(sum("weight"), 0) |
jbe@0 | 1254 FROM "direct_interest_snapshot" |
jbe@0 | 1255 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1256 AND "event" = 'periodic' |
jbe@0 | 1257 AND "voting_requested" = FALSE |
jbe@0 | 1258 ) |
jbe@0 | 1259 WHERE "id" = "issue_id_p"; |
jbe@0 | 1260 FOR "initiative_id_v" IN |
jbe@0 | 1261 SELECT "id" FROM "initiative" WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1262 LOOP |
jbe@0 | 1263 UPDATE "initiative" SET |
jbe@0 | 1264 "supporter_count" = ( |
jbe@0 | 1265 SELECT coalesce(sum("di"."weight"), 0) |
jbe@0 | 1266 FROM "direct_interest_snapshot" AS "di" |
jbe@0 | 1267 JOIN "direct_supporter_snapshot" AS "ds" |
jbe@0 | 1268 ON "di"."member_id" = "ds"."member_id" |
jbe@0 | 1269 WHERE "di"."issue_id" = "issue_id_p" |
jbe@0 | 1270 AND "di"."event" = 'periodic' |
jbe@0 | 1271 AND "ds"."initiative_id" = "initiative_id_v" |
jbe@0 | 1272 AND "ds"."event" = 'periodic' |
jbe@0 | 1273 ), |
jbe@0 | 1274 "informed_supporter_count" = ( |
jbe@0 | 1275 SELECT coalesce(sum("di"."weight"), 0) |
jbe@0 | 1276 FROM "direct_interest_snapshot" AS "di" |
jbe@0 | 1277 JOIN "direct_supporter_snapshot" AS "ds" |
jbe@0 | 1278 ON "di"."member_id" = "ds"."member_id" |
jbe@0 | 1279 WHERE "di"."issue_id" = "issue_id_p" |
jbe@0 | 1280 AND "di"."event" = 'periodic' |
jbe@0 | 1281 AND "ds"."initiative_id" = "initiative_id_v" |
jbe@0 | 1282 AND "ds"."event" = 'periodic' |
jbe@0 | 1283 AND "ds"."informed" |
jbe@0 | 1284 ), |
jbe@0 | 1285 "satisfied_supporter_count" = ( |
jbe@0 | 1286 SELECT coalesce(sum("di"."weight"), 0) |
jbe@0 | 1287 FROM "direct_interest_snapshot" AS "di" |
jbe@0 | 1288 JOIN "direct_supporter_snapshot" AS "ds" |
jbe@0 | 1289 ON "di"."member_id" = "ds"."member_id" |
jbe@0 | 1290 WHERE "di"."issue_id" = "issue_id_p" |
jbe@0 | 1291 AND "di"."event" = 'periodic' |
jbe@0 | 1292 AND "ds"."initiative_id" = "initiative_id_v" |
jbe@0 | 1293 AND "ds"."event" = 'periodic' |
jbe@0 | 1294 AND "ds"."satisfied" |
jbe@0 | 1295 ), |
jbe@0 | 1296 "satisfied_informed_supporter_count" = ( |
jbe@0 | 1297 SELECT coalesce(sum("di"."weight"), 0) |
jbe@0 | 1298 FROM "direct_interest_snapshot" AS "di" |
jbe@0 | 1299 JOIN "direct_supporter_snapshot" AS "ds" |
jbe@0 | 1300 ON "di"."member_id" = "ds"."member_id" |
jbe@0 | 1301 WHERE "di"."issue_id" = "issue_id_p" |
jbe@0 | 1302 AND "di"."event" = 'periodic' |
jbe@0 | 1303 AND "ds"."initiative_id" = "initiative_id_v" |
jbe@0 | 1304 AND "ds"."event" = 'periodic' |
jbe@0 | 1305 AND "ds"."informed" |
jbe@0 | 1306 AND "ds"."satisfied" |
jbe@0 | 1307 ) |
jbe@0 | 1308 WHERE "id" = "initiative_id_v"; |
jbe@0 | 1309 FOR "suggestion_id_v" IN |
jbe@0 | 1310 SELECT "id" FROM "suggestion" |
jbe@0 | 1311 WHERE "initiative_id" = "initiative_id_v" |
jbe@0 | 1312 LOOP |
jbe@0 | 1313 UPDATE "suggestion" SET |
jbe@0 | 1314 "minus2_unfulfilled_count" = ( |
jbe@0 | 1315 SELECT coalesce(sum("snapshot"."weight"), 0) |
jbe@0 | 1316 FROM "opinion" JOIN "direct_interest_snapshot" AS "snapshot" |
jbe@0 | 1317 ON "opinion"."member_id" = "snapshot"."member_id" |
jbe@1 | 1318 WHERE "opinion"."suggestion_id" = "suggestion_id_v" |
jbe@0 | 1319 AND "snapshot"."issue_id" = "issue_id_p" |
jbe@0 | 1320 AND "opinion"."degree" = -2 |
jbe@0 | 1321 AND "opinion"."fulfilled" = FALSE |
jbe@0 | 1322 ), |
jbe@0 | 1323 "minus2_fulfilled_count" = ( |
jbe@0 | 1324 SELECT coalesce(sum("snapshot"."weight"), 0) |
jbe@0 | 1325 FROM "opinion" JOIN "direct_interest_snapshot" AS "snapshot" |
jbe@0 | 1326 ON "opinion"."member_id" = "snapshot"."member_id" |
jbe@1 | 1327 WHERE "opinion"."suggestion_id" = "suggestion_id_v" |
jbe@0 | 1328 AND "snapshot"."issue_id" = "issue_id_p" |
jbe@0 | 1329 AND "opinion"."degree" = -2 |
jbe@0 | 1330 AND "opinion"."fulfilled" = TRUE |
jbe@0 | 1331 ), |
jbe@0 | 1332 "minus1_unfulfilled_count" = ( |
jbe@0 | 1333 SELECT coalesce(sum("snapshot"."weight"), 0) |
jbe@0 | 1334 FROM "opinion" JOIN "direct_interest_snapshot" AS "snapshot" |
jbe@0 | 1335 ON "opinion"."member_id" = "snapshot"."member_id" |
jbe@1 | 1336 WHERE "opinion"."suggestion_id" = "suggestion_id_v" |
jbe@0 | 1337 AND "snapshot"."issue_id" = "issue_id_p" |
jbe@0 | 1338 AND "opinion"."degree" = -1 |
jbe@0 | 1339 AND "opinion"."fulfilled" = FALSE |
jbe@0 | 1340 ), |
jbe@0 | 1341 "minus1_fulfilled_count" = ( |
jbe@0 | 1342 SELECT coalesce(sum("snapshot"."weight"), 0) |
jbe@0 | 1343 FROM "opinion" JOIN "direct_interest_snapshot" AS "snapshot" |
jbe@0 | 1344 ON "opinion"."member_id" = "snapshot"."member_id" |
jbe@1 | 1345 WHERE "opinion"."suggestion_id" = "suggestion_id_v" |
jbe@0 | 1346 AND "snapshot"."issue_id" = "issue_id_p" |
jbe@0 | 1347 AND "opinion"."degree" = -1 |
jbe@0 | 1348 AND "opinion"."fulfilled" = TRUE |
jbe@0 | 1349 ), |
jbe@0 | 1350 "plus1_unfulfilled_count" = ( |
jbe@0 | 1351 SELECT coalesce(sum("snapshot"."weight"), 0) |
jbe@0 | 1352 FROM "opinion" JOIN "direct_interest_snapshot" AS "snapshot" |
jbe@0 | 1353 ON "opinion"."member_id" = "snapshot"."member_id" |
jbe@1 | 1354 WHERE "opinion"."suggestion_id" = "suggestion_id_v" |
jbe@0 | 1355 AND "snapshot"."issue_id" = "issue_id_p" |
jbe@0 | 1356 AND "opinion"."degree" = 1 |
jbe@0 | 1357 AND "opinion"."fulfilled" = FALSE |
jbe@0 | 1358 ), |
jbe@0 | 1359 "plus1_fulfilled_count" = ( |
jbe@0 | 1360 SELECT coalesce(sum("snapshot"."weight"), 0) |
jbe@0 | 1361 FROM "opinion" JOIN "direct_interest_snapshot" AS "snapshot" |
jbe@0 | 1362 ON "opinion"."member_id" = "snapshot"."member_id" |
jbe@1 | 1363 WHERE "opinion"."suggestion_id" = "suggestion_id_v" |
jbe@0 | 1364 AND "snapshot"."issue_id" = "issue_id_p" |
jbe@0 | 1365 AND "opinion"."degree" = 1 |
jbe@0 | 1366 AND "opinion"."fulfilled" = TRUE |
jbe@0 | 1367 ), |
jbe@0 | 1368 "plus2_unfulfilled_count" = ( |
jbe@0 | 1369 SELECT coalesce(sum("snapshot"."weight"), 0) |
jbe@0 | 1370 FROM "opinion" JOIN "direct_interest_snapshot" AS "snapshot" |
jbe@0 | 1371 ON "opinion"."member_id" = "snapshot"."member_id" |
jbe@1 | 1372 WHERE "opinion"."suggestion_id" = "suggestion_id_v" |
jbe@0 | 1373 AND "snapshot"."issue_id" = "issue_id_p" |
jbe@0 | 1374 AND "opinion"."degree" = 2 |
jbe@0 | 1375 AND "opinion"."fulfilled" = FALSE |
jbe@0 | 1376 ), |
jbe@0 | 1377 "plus2_fulfilled_count" = ( |
jbe@0 | 1378 SELECT coalesce(sum("snapshot"."weight"), 0) |
jbe@0 | 1379 FROM "opinion" JOIN "direct_interest_snapshot" AS "snapshot" |
jbe@0 | 1380 ON "opinion"."member_id" = "snapshot"."member_id" |
jbe@1 | 1381 WHERE "opinion"."suggestion_id" = "suggestion_id_v" |
jbe@0 | 1382 AND "snapshot"."issue_id" = "issue_id_p" |
jbe@0 | 1383 AND "opinion"."degree" = 2 |
jbe@0 | 1384 AND "opinion"."fulfilled" = TRUE |
jbe@0 | 1385 ) |
jbe@0 | 1386 WHERE "suggestion"."id" = "suggestion_id_v"; |
jbe@0 | 1387 END LOOP; |
jbe@0 | 1388 END LOOP; |
jbe@0 | 1389 RETURN; |
jbe@0 | 1390 END; |
jbe@0 | 1391 $$; |
jbe@0 | 1392 |
jbe@0 | 1393 COMMENT ON FUNCTION "create_snapshot" |
jbe@0 | 1394 ( "issue"."id"%TYPE ) |
jbe@0 | 1395 IS 'This function creates a complete new ''periodic'' snapshot of population, interest and support for the given issue. All involved tables are locked, and after completion precalculated values in the source tables are updated.'; |
jbe@0 | 1396 |
jbe@0 | 1397 |
jbe@0 | 1398 CREATE FUNCTION "set_snapshot_event" |
jbe@0 | 1399 ( "issue_id_p" "issue"."id"%TYPE, |
jbe@0 | 1400 "event_p" "snapshot_event" ) |
jbe@0 | 1401 RETURNS VOID |
jbe@0 | 1402 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1403 BEGIN |
jbe@3 | 1404 UPDATE "direct_population_snapshot" SET "event" = "event_p" |
jbe@0 | 1405 WHERE "issue_id" = "issue_id_p" AND "event" = 'periodic'; |
jbe@3 | 1406 UPDATE "delegating_population_snapshot" SET "event" = "event_p" |
jbe@0 | 1407 WHERE "issue_id" = "issue_id_p" AND "event" = 'periodic'; |
jbe@3 | 1408 UPDATE "direct_interest_snapshot" SET "event" = "event_p" |
jbe@0 | 1409 WHERE "issue_id" = "issue_id_p" AND "event" = 'periodic'; |
jbe@3 | 1410 UPDATE "delegating_interest_snapshot" SET "event" = "event_p" |
jbe@0 | 1411 WHERE "issue_id" = "issue_id_p" AND "event" = 'periodic'; |
jbe@3 | 1412 UPDATE "direct_supporter_snapshot" SET "event" = "event_p" |
jbe@0 | 1413 WHERE "issue_id" = "issue_id_p" AND "event" = 'periodic'; |
jbe@0 | 1414 RETURN; |
jbe@0 | 1415 END; |
jbe@0 | 1416 $$; |
jbe@0 | 1417 |
jbe@0 | 1418 COMMENT ON FUNCTION "set_snapshot_event" |
jbe@0 | 1419 ( "issue"."id"%TYPE, |
jbe@0 | 1420 "snapshot_event" ) |
jbe@0 | 1421 IS 'Change "event" attribute of the previous ''periodic'' snapshot'; |
jbe@0 | 1422 |
jbe@0 | 1423 |
jbe@0 | 1424 |
jbe@0 | 1425 --------------------- |
jbe@0 | 1426 -- Freezing issues -- |
jbe@0 | 1427 --------------------- |
jbe@0 | 1428 |
jbe@0 | 1429 CREATE FUNCTION "freeze_after_snapshot" |
jbe@0 | 1430 ( "issue_id_p" "issue"."id"%TYPE ) |
jbe@0 | 1431 RETURNS VOID |
jbe@0 | 1432 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1433 DECLARE |
jbe@0 | 1434 "issue_row" "issue"%ROWTYPE; |
jbe@0 | 1435 "policy_row" "policy"%ROWTYPE; |
jbe@0 | 1436 "initiative_row" "initiative"%ROWTYPE; |
jbe@0 | 1437 BEGIN |
jbe@0 | 1438 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p"; |
jbe@0 | 1439 SELECT * INTO "policy_row" |
jbe@0 | 1440 FROM "policy" WHERE "id" = "issue_row"."policy_id"; |
jbe@3 | 1441 PERFORM "set_snapshot_event"("issue_id_p", 'start_of_voting'); |
jbe@3 | 1442 UPDATE "issue" SET |
jbe@3 | 1443 "accepted" = COALESCE("accepted", now()), |
jbe@3 | 1444 "half_frozen" = COALESCE("half_frozen", now()), |
jbe@3 | 1445 "fully_frozen" = now() |
jbe@3 | 1446 WHERE "id" = "issue_id_p"; |
jbe@0 | 1447 FOR "initiative_row" IN |
jbe@0 | 1448 SELECT * FROM "initiative" WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1449 LOOP |
jbe@0 | 1450 IF |
jbe@0 | 1451 "initiative_row"."satisfied_supporter_count" > 0 AND |
jbe@0 | 1452 "initiative_row"."satisfied_supporter_count" * |
jbe@0 | 1453 "policy_row"."initiative_quorum_den" >= |
jbe@0 | 1454 "issue_row"."population" * "policy_row"."initiative_quorum_num" |
jbe@0 | 1455 THEN |
jbe@0 | 1456 UPDATE "initiative" SET "admitted" = TRUE |
jbe@0 | 1457 WHERE "id" = "initiative_row"."id"; |
jbe@0 | 1458 ELSE |
jbe@0 | 1459 UPDATE "initiative" SET "admitted" = FALSE |
jbe@0 | 1460 WHERE "id" = "initiative_row"."id"; |
jbe@0 | 1461 END IF; |
jbe@0 | 1462 END LOOP; |
jbe@0 | 1463 RETURN; |
jbe@0 | 1464 END; |
jbe@0 | 1465 $$; |
jbe@0 | 1466 |
jbe@0 | 1467 COMMENT ON FUNCTION "freeze_after_snapshot" |
jbe@0 | 1468 ( "issue"."id"%TYPE ) |
jbe@3 | 1469 IS 'This function freezes an issue (fully) and starts voting, but must only be called when "create_snapshot" was called in the same transaction'; |
jbe@0 | 1470 |
jbe@0 | 1471 |
jbe@0 | 1472 CREATE FUNCTION "manual_freeze"("issue_id_p" "issue"."id"%TYPE) |
jbe@0 | 1473 RETURNS VOID |
jbe@0 | 1474 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1475 DECLARE |
jbe@0 | 1476 "issue_row" "issue"%ROWTYPE; |
jbe@0 | 1477 BEGIN |
jbe@0 | 1478 PERFORM "create_snapshot"("issue_id_p"); |
jbe@0 | 1479 PERFORM "freeze_after_snapshot"("issue_id_p"); |
jbe@0 | 1480 RETURN; |
jbe@0 | 1481 END; |
jbe@0 | 1482 $$; |
jbe@0 | 1483 |
jbe@0 | 1484 COMMENT ON FUNCTION "freeze_after_snapshot" |
jbe@0 | 1485 ( "issue"."id"%TYPE ) |
jbe@3 | 1486 IS 'Freeze an issue manually (fully) and start voting'; |
jbe@0 | 1487 |
jbe@0 | 1488 |
jbe@0 | 1489 |
jbe@0 | 1490 ----------------------- |
jbe@0 | 1491 -- Counting of votes -- |
jbe@0 | 1492 ----------------------- |
jbe@0 | 1493 |
jbe@0 | 1494 |
jbe@0 | 1495 CREATE FUNCTION "weight_of_added_delegations" |
jbe@0 | 1496 ( "issue_id_p" "issue"."id"%TYPE, |
jbe@0 | 1497 "member_id_p" "member"."id"%TYPE, |
jbe@0 | 1498 "delegate_member_ids_p" "delegating_voter"."delegate_member_ids"%TYPE ) |
jbe@0 | 1499 RETURNS "direct_voter"."weight"%TYPE |
jbe@0 | 1500 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1501 DECLARE |
jbe@0 | 1502 "issue_delegation_row" "issue_delegation"%ROWTYPE; |
jbe@0 | 1503 "delegate_member_ids_v" "delegating_voter"."delegate_member_ids"%TYPE; |
jbe@0 | 1504 "weight_v" INT4; |
jbe@0 | 1505 BEGIN |
jbe@0 | 1506 "weight_v" := 0; |
jbe@0 | 1507 FOR "issue_delegation_row" IN |
jbe@0 | 1508 SELECT * FROM "issue_delegation" |
jbe@0 | 1509 WHERE "trustee_id" = "member_id_p" |
jbe@0 | 1510 AND "issue_id" = "issue_id_p" |
jbe@0 | 1511 LOOP |
jbe@0 | 1512 IF NOT EXISTS ( |
jbe@0 | 1513 SELECT NULL FROM "direct_voter" |
jbe@0 | 1514 WHERE "member_id" = "issue_delegation_row"."truster_id" |
jbe@0 | 1515 AND "issue_id" = "issue_id_p" |
jbe@0 | 1516 ) AND NOT EXISTS ( |
jbe@0 | 1517 SELECT NULL FROM "delegating_voter" |
jbe@0 | 1518 WHERE "member_id" = "issue_delegation_row"."truster_id" |
jbe@0 | 1519 AND "issue_id" = "issue_id_p" |
jbe@0 | 1520 ) THEN |
jbe@0 | 1521 "delegate_member_ids_v" := |
jbe@0 | 1522 "member_id_p" || "delegate_member_ids_p"; |
jbe@0 | 1523 INSERT INTO "delegating_voter" |
jbe@0 | 1524 ("member_id", "issue_id", "delegate_member_ids") |
jbe@0 | 1525 VALUES ( |
jbe@0 | 1526 "issue_delegation_row"."truster_id", |
jbe@0 | 1527 "issue_id_p", |
jbe@0 | 1528 "delegate_member_ids_v" |
jbe@0 | 1529 ); |
jbe@0 | 1530 "weight_v" := "weight_v" + 1 + "weight_of_added_delegations"( |
jbe@0 | 1531 "issue_id_p", |
jbe@0 | 1532 "issue_delegation_row"."truster_id", |
jbe@0 | 1533 "delegate_member_ids_v" |
jbe@0 | 1534 ); |
jbe@0 | 1535 END IF; |
jbe@0 | 1536 END LOOP; |
jbe@0 | 1537 RETURN "weight_v"; |
jbe@0 | 1538 END; |
jbe@0 | 1539 $$; |
jbe@0 | 1540 |
jbe@0 | 1541 COMMENT ON FUNCTION "weight_of_added_delegations" |
jbe@0 | 1542 ( "issue"."id"%TYPE, |
jbe@0 | 1543 "member"."id"%TYPE, |
jbe@0 | 1544 "delegating_voter"."delegate_member_ids"%TYPE ) |
jbe@0 | 1545 IS 'Helper function for "add_vote_delegations" function'; |
jbe@0 | 1546 |
jbe@0 | 1547 |
jbe@0 | 1548 CREATE FUNCTION "add_vote_delegations" |
jbe@0 | 1549 ( "issue_id_p" "issue"."id"%TYPE ) |
jbe@0 | 1550 RETURNS VOID |
jbe@0 | 1551 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1552 DECLARE |
jbe@0 | 1553 "member_id_v" "member"."id"%TYPE; |
jbe@0 | 1554 BEGIN |
jbe@0 | 1555 FOR "member_id_v" IN |
jbe@0 | 1556 SELECT "member_id" FROM "direct_voter" |
jbe@0 | 1557 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1558 LOOP |
jbe@0 | 1559 UPDATE "direct_voter" SET |
jbe@0 | 1560 "weight" = "weight" + "weight_of_added_delegations"( |
jbe@0 | 1561 "issue_id_p", |
jbe@0 | 1562 "member_id_v", |
jbe@0 | 1563 '{}' |
jbe@0 | 1564 ) |
jbe@0 | 1565 WHERE "member_id" = "member_id_v" |
jbe@0 | 1566 AND "issue_id" = "issue_id_p"; |
jbe@0 | 1567 END LOOP; |
jbe@0 | 1568 RETURN; |
jbe@0 | 1569 END; |
jbe@0 | 1570 $$; |
jbe@0 | 1571 |
jbe@0 | 1572 COMMENT ON FUNCTION "add_vote_delegations" |
jbe@0 | 1573 ( "issue_id_p" "issue"."id"%TYPE ) |
jbe@0 | 1574 IS 'Helper function for "close_voting" function'; |
jbe@0 | 1575 |
jbe@0 | 1576 |
jbe@0 | 1577 CREATE FUNCTION "close_voting"("issue_id_p" "issue"."id"%TYPE) |
jbe@0 | 1578 RETURNS VOID |
jbe@0 | 1579 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1580 DECLARE |
jbe@0 | 1581 "issue_row" "issue"%ROWTYPE; |
jbe@0 | 1582 "member_id_v" "member"."id"%TYPE; |
jbe@0 | 1583 BEGIN |
jbe@0 | 1584 PERFORM "global_lock"(); |
jbe@0 | 1585 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p"; |
jbe@0 | 1586 DELETE FROM "delegating_voter" |
jbe@0 | 1587 WHERE "issue_id" = "issue_id_p"; |
jbe@0 | 1588 DELETE FROM "direct_voter" |
jbe@0 | 1589 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1590 AND "autoreject" = TRUE; |
jbe@0 | 1591 DELETE FROM "direct_voter" USING "member" |
jbe@0 | 1592 WHERE "direct_voter"."member_id" = "member"."id" |
jbe@0 | 1593 AND "direct_voter"."issue_id" = "issue_id_p" |
jbe@0 | 1594 AND "member"."active" = FALSE; |
jbe@0 | 1595 UPDATE "direct_voter" SET "weight" = 1 |
jbe@0 | 1596 WHERE "issue_id" = "issue_id_p"; |
jbe@0 | 1597 PERFORM "add_vote_delegations"("issue_id_p"); |
jbe@0 | 1598 FOR "member_id_v" IN |
jbe@0 | 1599 SELECT "interest"."member_id" |
jbe@0 | 1600 FROM "interest" |
jbe@0 | 1601 LEFT JOIN "direct_voter" |
jbe@0 | 1602 ON "interest"."member_id" = "direct_voter"."member_id" |
jbe@0 | 1603 AND "interest"."issue_id" = "direct_voter"."issue_id" |
jbe@0 | 1604 LEFT JOIN "delegating_voter" |
jbe@0 | 1605 ON "interest"."member_id" = "delegating_voter"."member_id" |
jbe@0 | 1606 AND "interest"."issue_id" = "delegating_voter"."issue_id" |
jbe@0 | 1607 WHERE "interest"."issue_id" = "issue_id_p" |
jbe@0 | 1608 AND "interest"."autoreject" = TRUE |
jbe@0 | 1609 AND "direct_voter"."member_id" ISNULL |
jbe@0 | 1610 AND "delegating_voter"."member_id" ISNULL |
jbe@0 | 1611 UNION SELECT "membership"."member_id" |
jbe@0 | 1612 FROM "membership" |
jbe@0 | 1613 LEFT JOIN "interest" |
jbe@0 | 1614 ON "membership"."member_id" = "interest"."member_id" |
jbe@0 | 1615 AND "interest"."issue_id" = "issue_id_p" |
jbe@0 | 1616 LEFT JOIN "direct_voter" |
jbe@0 | 1617 ON "membership"."member_id" = "direct_voter"."member_id" |
jbe@0 | 1618 AND "direct_voter"."issue_id" = "issue_id_p" |
jbe@0 | 1619 LEFT JOIN "delegating_voter" |
jbe@0 | 1620 ON "membership"."member_id" = "delegating_voter"."member_id" |
jbe@0 | 1621 AND "delegating_voter"."issue_id" = "issue_id_p" |
jbe@0 | 1622 WHERE "membership"."area_id" = "issue_row"."area_id" |
jbe@0 | 1623 AND "membership"."autoreject" = TRUE |
jbe@0 | 1624 AND "interest"."autoreject" ISNULL |
jbe@0 | 1625 AND "direct_voter"."member_id" ISNULL |
jbe@0 | 1626 AND "delegating_voter"."member_id" ISNULL |
jbe@0 | 1627 LOOP |
jbe@0 | 1628 INSERT INTO "direct_voter" ("member_id", "issue_id", "autoreject") |
jbe@0 | 1629 VALUES ("member_id_v", "issue_id_p", TRUE); |
jbe@0 | 1630 INSERT INTO "vote" ( |
jbe@0 | 1631 "member_id", |
jbe@0 | 1632 "issue_id", |
jbe@0 | 1633 "initiative_id", |
jbe@0 | 1634 "grade" |
jbe@0 | 1635 ) SELECT |
jbe@0 | 1636 "member_id_v" AS "member_id", |
jbe@0 | 1637 "issue_id_p" AS "issue_id", |
jbe@0 | 1638 "id" AS "initiative_id", |
jbe@0 | 1639 -1 AS "grade" |
jbe@0 | 1640 FROM "initiative" WHERE "issue_id" = "issue_id_p"; |
jbe@0 | 1641 END LOOP; |
jbe@0 | 1642 PERFORM "add_vote_delegations"("issue_id_p"); |
jbe@0 | 1643 UPDATE "initiative" SET |
jbe@0 | 1644 "positive_votes" = "subquery"."positive_votes", |
jbe@0 | 1645 "negative_votes" = "subquery"."negative_votes" |
jbe@0 | 1646 FROM ( |
jbe@0 | 1647 SELECT |
jbe@0 | 1648 "initiative_id", |
jbe@0 | 1649 sum( |
jbe@0 | 1650 CASE WHEN "grade" > 0 THEN "direct_voter"."weight" ELSE 0 END |
jbe@0 | 1651 ) AS "positive_votes", |
jbe@0 | 1652 sum ( |
jbe@0 | 1653 CASE WHEN "grade" < 0 THEN "direct_voter"."weight" ELSE 0 END |
jbe@0 | 1654 ) AS "negative_votes" |
jbe@0 | 1655 FROM "vote" JOIN "direct_voter" |
jbe@0 | 1656 ON "vote"."member_id" = "direct_voter"."member_id" |
jbe@0 | 1657 AND "vote"."issue_id" = "direct_voter"."issue_id" |
jbe@0 | 1658 WHERE "vote"."issue_id" = "issue_id_p" |
jbe@0 | 1659 GROUP BY "initiative_id" |
jbe@0 | 1660 ) AS "subquery" |
jbe@0 | 1661 WHERE "initiative"."admitted" |
jbe@0 | 1662 AND "initiative"."id" = "subquery"."initiative_id"; |
jbe@0 | 1663 UPDATE "issue" SET "closed" = now() WHERE "id" = "issue_id_p"; |
jbe@0 | 1664 END; |
jbe@0 | 1665 $$; |
jbe@0 | 1666 |
jbe@0 | 1667 COMMENT ON FUNCTION "close_voting" |
jbe@0 | 1668 ( "issue"."id"%TYPE ) |
jbe@0 | 1669 IS 'Closes the voting on an issue, and calculates positive and negative votes for each initiative; The ranking is not calculated yet, to keep the (locking) transaction short.'; |
jbe@0 | 1670 |
jbe@0 | 1671 |
jbe@0 | 1672 CREATE FUNCTION "init_array"("dim_p" INTEGER) |
jbe@0 | 1673 RETURNS INT4[] |
jbe@0 | 1674 LANGUAGE 'plpgsql' IMMUTABLE AS $$ |
jbe@0 | 1675 DECLARE |
jbe@0 | 1676 "i" INTEGER; |
jbe@0 | 1677 "ary_text_v" TEXT; |
jbe@0 | 1678 BEGIN |
jbe@0 | 1679 IF "dim_p" >= 1 THEN |
jbe@0 | 1680 "ary_text_v" := '{NULL'; |
jbe@0 | 1681 "i" := "dim_p"; |
jbe@0 | 1682 LOOP |
jbe@0 | 1683 "i" := "i" - 1; |
jbe@0 | 1684 EXIT WHEN "i" = 0; |
jbe@0 | 1685 "ary_text_v" := "ary_text_v" || ',NULL'; |
jbe@0 | 1686 END LOOP; |
jbe@0 | 1687 "ary_text_v" := "ary_text_v" || '}'; |
jbe@0 | 1688 RETURN "ary_text_v"::INT4[][]; |
jbe@0 | 1689 ELSE |
jbe@0 | 1690 RAISE EXCEPTION 'Dimension needs to be at least 1.'; |
jbe@0 | 1691 END IF; |
jbe@0 | 1692 END; |
jbe@0 | 1693 $$; |
jbe@0 | 1694 |
jbe@0 | 1695 COMMENT ON FUNCTION "init_array"(INTEGER) IS 'Needed for PostgreSQL < 8.4, due to missing "array_fill" function'; |
jbe@0 | 1696 |
jbe@0 | 1697 |
jbe@0 | 1698 CREATE FUNCTION "init_square_matrix"("dim_p" INTEGER) |
jbe@0 | 1699 RETURNS INT4[][] |
jbe@0 | 1700 LANGUAGE 'plpgsql' IMMUTABLE AS $$ |
jbe@0 | 1701 DECLARE |
jbe@0 | 1702 "i" INTEGER; |
jbe@0 | 1703 "row_text_v" TEXT; |
jbe@0 | 1704 "ary_text_v" TEXT; |
jbe@0 | 1705 BEGIN |
jbe@0 | 1706 IF "dim_p" >= 1 THEN |
jbe@0 | 1707 "row_text_v" := '{NULL'; |
jbe@0 | 1708 "i" := "dim_p"; |
jbe@0 | 1709 LOOP |
jbe@0 | 1710 "i" := "i" - 1; |
jbe@0 | 1711 EXIT WHEN "i" = 0; |
jbe@0 | 1712 "row_text_v" := "row_text_v" || ',NULL'; |
jbe@0 | 1713 END LOOP; |
jbe@0 | 1714 "row_text_v" := "row_text_v" || '}'; |
jbe@0 | 1715 "ary_text_v" := '{' || "row_text_v"; |
jbe@0 | 1716 "i" := "dim_p"; |
jbe@0 | 1717 LOOP |
jbe@0 | 1718 "i" := "i" - 1; |
jbe@0 | 1719 EXIT WHEN "i" = 0; |
jbe@0 | 1720 "ary_text_v" := "ary_text_v" || ',' || "row_text_v"; |
jbe@0 | 1721 END LOOP; |
jbe@0 | 1722 "ary_text_v" := "ary_text_v" || '}'; |
jbe@0 | 1723 RETURN "ary_text_v"::INT4[][]; |
jbe@0 | 1724 ELSE |
jbe@0 | 1725 RAISE EXCEPTION 'Dimension needs to be at least 1.'; |
jbe@0 | 1726 END IF; |
jbe@0 | 1727 END; |
jbe@0 | 1728 $$; |
jbe@0 | 1729 |
jbe@0 | 1730 COMMENT ON FUNCTION "init_square_matrix"(INTEGER) IS 'Needed for PostgreSQL < 8.4, due to missing "array_fill" function'; |
jbe@0 | 1731 |
jbe@0 | 1732 |
jbe@0 | 1733 CREATE FUNCTION "calculate_ranks"("issue_id_p" "issue"."id"%TYPE) |
jbe@0 | 1734 RETURNS VOID |
jbe@0 | 1735 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1736 DECLARE |
jbe@0 | 1737 "dimension_v" INTEGER; |
jbe@0 | 1738 "matrix" INT4[][]; |
jbe@0 | 1739 "i" INTEGER; |
jbe@0 | 1740 "j" INTEGER; |
jbe@0 | 1741 "k" INTEGER; |
jbe@0 | 1742 "battle_row" "battle"%ROWTYPE; |
jbe@0 | 1743 "rank_ary" INT4[]; |
jbe@0 | 1744 "rank_v" INT4; |
jbe@0 | 1745 "done_v" INTEGER; |
jbe@0 | 1746 "winners_ary" INTEGER[]; |
jbe@0 | 1747 "initiative_id_v" "initiative"."id"%TYPE; |
jbe@0 | 1748 BEGIN |
jbe@0 | 1749 PERFORM NULL FROM "issue" WHERE "id" = "issue_id_p" FOR UPDATE; |
jbe@0 | 1750 -- Prepare matrix for Schulze-Method: |
jbe@0 | 1751 SELECT count(1) INTO "dimension_v" |
jbe@0 | 1752 FROM "battle_participant" WHERE "issue_id" = "issue_id_p"; |
jbe@0 | 1753 IF "dimension_v" = 1 THEN |
jbe@0 | 1754 UPDATE "initiative" SET |
jbe@0 | 1755 "rank" = 1 |
jbe@0 | 1756 FROM "battle_participant" |
jbe@0 | 1757 WHERE "initiative"."issue_id" = "issue_id_p" |
jbe@0 | 1758 AND "initiative"."id" = "battle_participant"."initiative_id"; |
jbe@0 | 1759 ELSIF "dimension_v" > 1 THEN |
jbe@0 | 1760 "matrix" := "init_square_matrix"("dimension_v"); -- TODO: replace by "array_fill" function (PostgreSQL 8.4) |
jbe@0 | 1761 "i" := 1; |
jbe@0 | 1762 "j" := 2; |
jbe@0 | 1763 -- Fill matrix with data from "battle" view |
jbe@0 | 1764 FOR "battle_row" IN |
jbe@0 | 1765 SELECT * FROM "battle" WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1766 ORDER BY "winning_initiative_id", "losing_initiative_id" |
jbe@0 | 1767 LOOP |
jbe@0 | 1768 "matrix"["i"]["j"] := "battle_row"."count"; |
jbe@0 | 1769 IF "j" = "dimension_v" THEN |
jbe@0 | 1770 "i" := "i" + 1; |
jbe@0 | 1771 "j" := 1; |
jbe@0 | 1772 ELSE |
jbe@0 | 1773 "j" := "j" + 1; |
jbe@0 | 1774 IF "j" = "i" THEN |
jbe@0 | 1775 "j" := "j" + 1; |
jbe@0 | 1776 END IF; |
jbe@0 | 1777 END IF; |
jbe@0 | 1778 END LOOP; |
jbe@0 | 1779 IF "i" != "dimension_v" OR "j" != "dimension_v" + 1 THEN |
jbe@0 | 1780 RAISE EXCEPTION 'Wrong battle count (should not happen)'; |
jbe@0 | 1781 END IF; |
jbe@0 | 1782 -- Delete losers from matrix: |
jbe@0 | 1783 "i" := 1; |
jbe@0 | 1784 LOOP |
jbe@0 | 1785 "j" := "i" + 1; |
jbe@0 | 1786 LOOP |
jbe@0 | 1787 IF "i" != "j" THEN |
jbe@0 | 1788 IF "matrix"["i"]["j"] < "matrix"["j"]["i"] THEN |
jbe@0 | 1789 "matrix"["i"]["j"] := 0; |
jbe@0 | 1790 ELSIF matrix[j][i] < matrix[i][j] THEN |
jbe@0 | 1791 "matrix"["j"]["i"] := 0; |
jbe@0 | 1792 ELSE |
jbe@0 | 1793 "matrix"["i"]["j"] := 0; |
jbe@0 | 1794 "matrix"["j"]["i"] := 0; |
jbe@0 | 1795 END IF; |
jbe@0 | 1796 END IF; |
jbe@0 | 1797 EXIT WHEN "j" = "dimension_v"; |
jbe@0 | 1798 "j" := "j" + 1; |
jbe@0 | 1799 END LOOP; |
jbe@0 | 1800 EXIT WHEN "i" = "dimension_v" - 1; |
jbe@0 | 1801 "i" := "i" + 1; |
jbe@0 | 1802 END LOOP; |
jbe@0 | 1803 -- Find best paths: |
jbe@0 | 1804 "i" := 1; |
jbe@0 | 1805 LOOP |
jbe@0 | 1806 "j" := 1; |
jbe@0 | 1807 LOOP |
jbe@0 | 1808 IF "i" != "j" THEN |
jbe@0 | 1809 "k" := 1; |
jbe@0 | 1810 LOOP |
jbe@0 | 1811 IF "i" != "k" AND "j" != "k" THEN |
jbe@0 | 1812 IF "matrix"["j"]["i"] < "matrix"["i"]["k"] THEN |
jbe@0 | 1813 IF "matrix"["j"]["i"] > "matrix"["j"]["k"] THEN |
jbe@0 | 1814 "matrix"["j"]["k"] := "matrix"["j"]["i"]; |
jbe@0 | 1815 END IF; |
jbe@0 | 1816 ELSE |
jbe@0 | 1817 IF "matrix"["i"]["k"] > "matrix"["j"]["k"] THEN |
jbe@0 | 1818 "matrix"["j"]["k"] := "matrix"["i"]["k"]; |
jbe@0 | 1819 END IF; |
jbe@0 | 1820 END IF; |
jbe@0 | 1821 END IF; |
jbe@0 | 1822 EXIT WHEN "k" = "dimension_v"; |
jbe@0 | 1823 "k" := "k" + 1; |
jbe@0 | 1824 END LOOP; |
jbe@0 | 1825 END IF; |
jbe@0 | 1826 EXIT WHEN "j" = "dimension_v"; |
jbe@0 | 1827 "j" := "j" + 1; |
jbe@0 | 1828 END LOOP; |
jbe@0 | 1829 EXIT WHEN "i" = "dimension_v"; |
jbe@0 | 1830 "i" := "i" + 1; |
jbe@0 | 1831 END LOOP; |
jbe@0 | 1832 -- Determine order of winners: |
jbe@0 | 1833 "rank_ary" := "init_array"("dimension_v"); -- TODO: replace by "array_fill" function (PostgreSQL 8.4) |
jbe@0 | 1834 "rank_v" := 1; |
jbe@0 | 1835 "done_v" := 0; |
jbe@0 | 1836 LOOP |
jbe@0 | 1837 "winners_ary" := '{}'; |
jbe@0 | 1838 "i" := 1; |
jbe@0 | 1839 LOOP |
jbe@0 | 1840 IF "rank_ary"["i"] ISNULL THEN |
jbe@0 | 1841 "j" := 1; |
jbe@0 | 1842 LOOP |
jbe@0 | 1843 IF |
jbe@0 | 1844 "i" != "j" AND |
jbe@0 | 1845 "rank_ary"["j"] ISNULL AND |
jbe@0 | 1846 "matrix"["j"]["i"] > "matrix"["i"]["j"] |
jbe@0 | 1847 THEN |
jbe@0 | 1848 -- someone else is better |
jbe@0 | 1849 EXIT; |
jbe@0 | 1850 END IF; |
jbe@0 | 1851 IF "j" = "dimension_v" THEN |
jbe@0 | 1852 -- noone is better |
jbe@0 | 1853 "winners_ary" := "winners_ary" || "i"; |
jbe@0 | 1854 EXIT; |
jbe@0 | 1855 END IF; |
jbe@0 | 1856 "j" := "j" + 1; |
jbe@0 | 1857 END LOOP; |
jbe@0 | 1858 END IF; |
jbe@0 | 1859 EXIT WHEN "i" = "dimension_v"; |
jbe@0 | 1860 "i" := "i" + 1; |
jbe@0 | 1861 END LOOP; |
jbe@0 | 1862 "i" := 1; |
jbe@0 | 1863 LOOP |
jbe@0 | 1864 "rank_ary"["winners_ary"["i"]] := "rank_v"; |
jbe@0 | 1865 "done_v" := "done_v" + 1; |
jbe@0 | 1866 EXIT WHEN "i" = array_upper("winners_ary", 1); |
jbe@0 | 1867 "i" := "i" + 1; |
jbe@0 | 1868 END LOOP; |
jbe@0 | 1869 EXIT WHEN "done_v" = "dimension_v"; |
jbe@0 | 1870 "rank_v" := "rank_v" + 1; |
jbe@0 | 1871 END LOOP; |
jbe@0 | 1872 -- write preliminary ranks: |
jbe@0 | 1873 "i" := 1; |
jbe@0 | 1874 FOR "initiative_id_v" IN |
jbe@0 | 1875 SELECT "initiative"."id" |
jbe@0 | 1876 FROM "initiative" JOIN "battle_participant" |
jbe@0 | 1877 ON "initiative"."id" = "battle_participant"."initiative_id" |
jbe@0 | 1878 WHERE "initiative"."issue_id" = "issue_id_p" |
jbe@0 | 1879 ORDER BY "initiative"."id" |
jbe@0 | 1880 LOOP |
jbe@0 | 1881 UPDATE "initiative" SET "rank" = "rank_ary"["i"] |
jbe@0 | 1882 WHERE "id" = "initiative_id_v"; |
jbe@0 | 1883 "i" := "i" + 1; |
jbe@0 | 1884 END LOOP; |
jbe@0 | 1885 IF "i" != "dimension_v" + 1 THEN |
jbe@0 | 1886 RAISE EXCEPTION 'Wrong winner count (should not happen)'; |
jbe@0 | 1887 END IF; |
jbe@0 | 1888 -- straighten ranks (start counting with 1, no equal ranks): |
jbe@0 | 1889 "rank_v" := 1; |
jbe@0 | 1890 FOR "initiative_id_v" IN |
jbe@0 | 1891 SELECT "id" FROM "initiative" |
jbe@0 | 1892 WHERE "issue_id" = "issue_id_p" AND "rank" NOTNULL |
jbe@0 | 1893 ORDER BY |
jbe@0 | 1894 "rank", |
jbe@0 | 1895 "vote_ratio"("positive_votes", "negative_votes") DESC, |
jbe@0 | 1896 "id" |
jbe@0 | 1897 LOOP |
jbe@0 | 1898 UPDATE "initiative" SET "rank" = "rank_v" |
jbe@0 | 1899 WHERE "id" = "initiative_id_v"; |
jbe@0 | 1900 "rank_v" := "rank_v" + 1; |
jbe@0 | 1901 END LOOP; |
jbe@0 | 1902 END IF; |
jbe@0 | 1903 -- mark issue as finished |
jbe@0 | 1904 UPDATE "issue" SET "ranks_available" = TRUE |
jbe@0 | 1905 WHERE "id" = "issue_id_p"; |
jbe@0 | 1906 RETURN; |
jbe@0 | 1907 END; |
jbe@0 | 1908 $$; |
jbe@0 | 1909 |
jbe@0 | 1910 COMMENT ON FUNCTION "calculate_ranks" |
jbe@0 | 1911 ( "issue"."id"%TYPE ) |
jbe@0 | 1912 IS 'Determine ranking (Votes have to be counted first)'; |
jbe@0 | 1913 |
jbe@0 | 1914 |
jbe@0 | 1915 |
jbe@0 | 1916 ----------------------------- |
jbe@0 | 1917 -- Automatic state changes -- |
jbe@0 | 1918 ----------------------------- |
jbe@0 | 1919 |
jbe@0 | 1920 |
jbe@0 | 1921 CREATE FUNCTION "check_issue" |
jbe@0 | 1922 ( "issue_id_p" "issue"."id"%TYPE ) |
jbe@0 | 1923 RETURNS VOID |
jbe@0 | 1924 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 1925 DECLARE |
jbe@0 | 1926 "issue_row" "issue"%ROWTYPE; |
jbe@0 | 1927 "policy_row" "policy"%ROWTYPE; |
jbe@0 | 1928 "voting_requested_v" BOOLEAN; |
jbe@0 | 1929 BEGIN |
jbe@0 | 1930 PERFORM "global_lock"(); |
jbe@0 | 1931 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p"; |
jbe@0 | 1932 IF "issue_row"."closed" ISNULL THEN |
jbe@0 | 1933 SELECT * INTO "policy_row" FROM "policy" |
jbe@0 | 1934 WHERE "id" = "issue_row"."policy_id"; |
jbe@3 | 1935 IF "issue_row"."fully_frozen" ISNULL THEN |
jbe@0 | 1936 PERFORM "create_snapshot"("issue_id_p"); |
jbe@0 | 1937 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p"; |
jbe@0 | 1938 END IF; |
jbe@0 | 1939 IF "issue_row"."accepted" ISNULL THEN |
jbe@0 | 1940 IF EXISTS ( |
jbe@0 | 1941 SELECT NULL FROM "initiative" |
jbe@0 | 1942 WHERE "issue_id" = "issue_id_p" |
jbe@0 | 1943 AND "supporter_count" > 0 |
jbe@0 | 1944 AND "supporter_count" * "policy_row"."issue_quorum_den" |
jbe@0 | 1945 >= "issue_row"."population" * "policy_row"."issue_quorum_num" |
jbe@0 | 1946 ) THEN |
jbe@3 | 1947 PERFORM "set_snapshot_event"("issue_id_p", 'end_of_admission'); |
jbe@0 | 1948 "issue_row"."accepted" = now(); -- NOTE: "issue_row" used later |
jbe@0 | 1949 UPDATE "issue" SET "accepted" = "issue_row"."accepted" |
jbe@0 | 1950 WHERE "id" = "issue_row"."id"; |
jbe@0 | 1951 ELSIF |
jbe@3 | 1952 now() >= "issue_row"."created" + "policy_row"."admission_time" |
jbe@0 | 1953 THEN |
jbe@0 | 1954 PERFORM "set_snapshot_event"("issue_id_p", 'end_of_admission'); |
jbe@0 | 1955 UPDATE "issue" SET "closed" = now() |
jbe@0 | 1956 WHERE "id" = "issue_row"."id"; |
jbe@0 | 1957 END IF; |
jbe@0 | 1958 END IF; |
jbe@0 | 1959 IF |
jbe@0 | 1960 "issue_row"."accepted" NOTNULL AND |
jbe@3 | 1961 "issue_row"."half_frozen" ISNULL |
jbe@0 | 1962 THEN |
jbe@0 | 1963 SELECT |
jbe@0 | 1964 CASE |
jbe@0 | 1965 WHEN "vote_now" * 2 > "issue_row"."population" THEN |
jbe@0 | 1966 TRUE |
jbe@0 | 1967 WHEN "vote_later" * 2 > "issue_row"."population" THEN |
jbe@0 | 1968 FALSE |
jbe@0 | 1969 ELSE NULL |
jbe@0 | 1970 END |
jbe@0 | 1971 INTO "voting_requested_v" |
jbe@0 | 1972 FROM "issue" WHERE "id" = "issue_id_p"; |
jbe@0 | 1973 IF |
jbe@0 | 1974 "voting_requested_v" OR ( |
jbe@3 | 1975 "voting_requested_v" ISNULL AND |
jbe@3 | 1976 now() >= "issue_row"."accepted" + "policy_row"."discussion_time" |
jbe@0 | 1977 ) |
jbe@0 | 1978 THEN |
jbe@3 | 1979 "issue_row"."half_frozen" = now(); -- NOTE: "issue_row" used later |
jbe@3 | 1980 UPDATE "issue" SET "half_frozen" = "issue_row"."half_frozen" |
jbe@3 | 1981 WHERE "id" = "issue_row"."id"; |
jbe@0 | 1982 END IF; |
jbe@0 | 1983 END IF; |
jbe@0 | 1984 IF |
jbe@3 | 1985 "issue_row"."half_frozen" NOTNULL AND |
jbe@3 | 1986 "issue_row"."fully_frozen" ISNULL AND |
jbe@3 | 1987 now() >= "issue_row"."half_frozen" + "policy_row"."verification_time" |
jbe@3 | 1988 THEN |
jbe@3 | 1989 "issue_row"."fully_frozen" = now(); -- NOTE: "issue_row" used later |
jbe@3 | 1990 PERFORM "freeze_after_snapshot"("issue_id_p"); |
jbe@3 | 1991 END IF; |
jbe@3 | 1992 IF |
jbe@3 | 1993 "issue_row"."fully_frozen" NOTNULL AND |
jbe@3 | 1994 now() >= "issue_row"."fully_frozen" + "policy_row"."voting_time" |
jbe@0 | 1995 THEN |
jbe@0 | 1996 PERFORM "close_voting"("issue_id_p"); |
jbe@0 | 1997 END IF; |
jbe@0 | 1998 END IF; |
jbe@0 | 1999 RETURN; |
jbe@0 | 2000 END; |
jbe@0 | 2001 $$; |
jbe@0 | 2002 |
jbe@0 | 2003 COMMENT ON FUNCTION "check_issue" |
jbe@0 | 2004 ( "issue"."id"%TYPE ) |
jbe@0 | 2005 IS 'Precalculate supporter counts etc. for a given issue, and check, if status change is required; At end of voting the ranking is not calculated by this function, but must be calculated in a seperate transaction using the "calculate_ranks" function.'; |
jbe@0 | 2006 |
jbe@0 | 2007 |
jbe@0 | 2008 CREATE FUNCTION "check_everything"() |
jbe@0 | 2009 RETURNS VOID |
jbe@0 | 2010 LANGUAGE 'plpgsql' VOLATILE AS $$ |
jbe@0 | 2011 DECLARE |
jbe@0 | 2012 "issue_id_v" "issue"."id"%TYPE; |
jbe@0 | 2013 BEGIN |
jbe@1 | 2014 DELETE FROM "expired_session"; |
jbe@0 | 2015 FOR "issue_id_v" IN |
jbe@1 | 2016 SELECT "id" FROM "open_issue" |
jbe@0 | 2017 LOOP |
jbe@0 | 2018 PERFORM "check_issue"("issue_id_v"); |
jbe@0 | 2019 END LOOP; |
jbe@0 | 2020 FOR "issue_id_v" IN |
jbe@1 | 2021 SELECT "id" FROM "issue_with_ranks_missing" |
jbe@0 | 2022 LOOP |
jbe@0 | 2023 PERFORM "calculate_ranks"("issue_id_v"); |
jbe@0 | 2024 END LOOP; |
jbe@0 | 2025 RETURN; |
jbe@0 | 2026 END; |
jbe@0 | 2027 $$; |
jbe@0 | 2028 |
jbe@0 | 2029 COMMENT ON FUNCTION "check_everything"() IS 'Perform "check_issue" for every open issue, and if possible, automatically calculate ranks. Use this function only for development and debugging purposes, as long transactions with exclusive locking may result.'; |
jbe@0 | 2030 |
jbe@0 | 2031 |
jbe@0 | 2032 |
jbe@0 | 2033 COMMIT; |