jbe@478: BEGIN; jbe@478: jbe@478: CREATE OR REPLACE VIEW "liquid_feedback_version" AS jbe@478: SELECT * FROM (VALUES ('3.2.0', 3, 2, 0)) jbe@478: AS "subquery"("string", "major", "minor", "revision"); jbe@478: jbe@478: ALTER TABLE "member" ADD COLUMN "disable_notifications" BOOLEAN NOT NULL DEFAULT FALSE; jbe@492: ALTER TABLE "member" ADD COLUMN "notification_counter" INT4 NOT NULL DEFAULT 1; jbe@492: ALTER TABLE "member" ADD COLUMN "notification_sample_size" INT4 NOT NULL DEFAULT 3; jbe@492: ALTER TABLE "member" ADD COLUMN "notification_dow" INT4 CHECK ("notification_dow" BETWEEN 0 AND 6); jbe@492: ALTER TABLE "member" ADD COLUMN "notification_hour" INT4 CHECK ("notification_hour" BETWEEN 0 AND 23); jbe@478: jbe@478: UPDATE "member" SET "disable_notifications" = TRUE WHERE "notify_level" = 'none'; jbe@478: jbe@478: CREATE TABLE "subscription" ( jbe@478: PRIMARY KEY ("member_id", "unit_id"), jbe@478: "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, jbe@478: "unit_id" INT4 REFERENCES "unit" ("id") ON DELETE CASCADE ON UPDATE CASCADE ); jbe@478: CREATE INDEX "subscription_unit_id_idx" ON "subscription" ("unit_id"); jbe@492: jbe@492: COMMENT ON TABLE "subscription" IS 'An entry in this table denotes that the member wishes to receive notifications regardless of his/her privileges in the given unit'; jbe@492: jbe@492: CREATE TABLE "ignored_area" ( jbe@492: PRIMARY KEY ("member_id", "area_id"), jbe@492: "member_id" INT4 REFERENCES "unit" ("id") ON DELETE CASCADE ON UPDATE CASCADE, jbe@492: "area_id" INT4 REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE ); jbe@492: CREATE INDEX "ignored_area_area_id_idx" ON "ignored_area" ("area_id"); jbe@492: jbe@492: COMMENT ON TABLE "ignored_area" IS 'An entry in this table denotes that the member does not wish to receive notifications for the given subject area unless he/she declared interested in a particular issue'; jbe@492: jbe@492: CREATE TABLE "initiative_notification_sent" ( jbe@492: PRIMARY KEY ("member_id", "initiative_id"), jbe@492: "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE, jbe@492: "initiative_id" INT4 REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE, jbe@492: "last_draft_id" INT8 NOT NULL, jbe@495: "last_suggestion_id" INT8 ); jbe@492: CREATE INDEX "initiative_notification_sent_initiative_idx" ON "initiative_notification_sent" ("initiative_id"); jbe@478: jbe@478: CREATE VIEW "updated_initiative" AS jbe@478: SELECT jbe@492: "supporter"."member_id" AS "seen_by_member_id", jbe@478: TRUE AS "supported", jbe@478: EXISTS ( jbe@478: SELECT NULL FROM "draft" jbe@478: WHERE "draft"."initiative_id" = "initiative"."id" jbe@478: AND "draft"."id" > "supporter"."draft_id" jbe@478: ) AS "new_draft", jbe@478: ( SELECT count(1) FROM "suggestion" jbe@492: LEFT JOIN "opinion" ON jbe@492: "opinion"."member_id" = "supporter"."member_id" AND jbe@492: "opinion"."suggestion_id" = "suggestion"."id" jbe@478: WHERE "suggestion"."initiative_id" = "initiative"."id" jbe@492: AND "opinion"."member_id" ISNULL jbe@478: AND COALESCE( jbe@492: "suggestion"."id" > "sent"."last_suggestion_id", jbe@478: TRUE jbe@478: ) jbe@478: ) AS "new_suggestion_count", jbe@478: FALSE AS "featured", jbe@478: NOT EXISTS ( jbe@478: SELECT NULL FROM "initiative" AS "better_initiative" jbe@478: WHERE jbe@484: "better_initiative"."issue_id" = "initiative"."issue_id" jbe@484: AND jbe@484: ( COALESCE("better_initiative"."harmonic_weight", -1), jbe@484: -"better_initiative"."id" ) > jbe@484: ( COALESCE("initiative"."harmonic_weight", -1), jbe@485: -"initiative"."id" ) jbe@478: ) AS "leading", jbe@478: "initiative".* jbe@492: FROM "supporter" JOIN "initiative" jbe@492: ON "supporter"."initiative_id" = "initiative"."id" jbe@492: LEFT JOIN "initiative_notification_sent" AS "sent" jbe@492: ON "sent"."member_id" = "supporter"."member_id" jbe@492: AND "sent"."initiative_id" = "initiative"."id" jbe@478: JOIN "issue" ON "issue"."id" = "initiative"."issue_id" jbe@480: WHERE "issue"."state" IN ('admission', 'discussion') jbe@478: AND ( jbe@478: EXISTS ( jbe@478: SELECT NULL FROM "draft" jbe@478: WHERE "draft"."initiative_id" = "initiative"."id" jbe@478: AND "draft"."id" > "supporter"."draft_id" jbe@478: ) OR EXISTS ( jbe@478: SELECT NULL FROM "suggestion" jbe@492: LEFT JOIN "opinion" ON jbe@492: "opinion"."member_id" = "supporter"."member_id" AND jbe@492: "opinion"."suggestion_id" = "suggestion"."id" jbe@478: WHERE "suggestion"."initiative_id" = "initiative"."id" jbe@492: AND "opinion"."member_id" ISNULL jbe@478: AND COALESCE( jbe@492: "suggestion"."id" > "sent"."last_suggestion_id", jbe@478: TRUE jbe@478: ) jbe@478: ) jbe@478: ); jbe@478: jbe@478: CREATE FUNCTION "featured_initiative" jbe@478: ( "member_id_p" "member"."id"%TYPE, jbe@478: "area_id_p" "area"."id"%TYPE ) jbe@478: RETURNS SETOF "initiative" jbe@478: LANGUAGE 'plpgsql' STABLE AS $$ jbe@478: DECLARE jbe@482: "member_row" "member"%ROWTYPE; jbe@478: "member_id_v" "member"."id"%TYPE; jbe@478: "seed_v" TEXT; jbe@478: "result_row" "initiative"%ROWTYPE; jbe@478: "match_v" BOOLEAN; jbe@478: "initiative_id_ary" INT4[]; --"initiative"."id"%TYPE[] jbe@478: BEGIN jbe@482: SELECT INTO "member_row" * FROM "member" WHERE "id" = "member_id_p"; jbe@478: "initiative_id_ary" := '{}'; jbe@478: LOOP jbe@478: "match_v" := FALSE; jbe@478: FOR "member_id_v", "seed_v" IN jbe@478: SELECT * FROM ( jbe@478: SELECT DISTINCT jbe@478: "supporter"."member_id", jbe@482: md5("member_id_p" || '-' || "member_row"."notification_counter" || '-' || "area_id_p" || '-' || "supporter"."member_id") AS "seed" jbe@478: FROM "supporter" jbe@478: JOIN "member" ON "member"."id" = "supporter"."member_id" jbe@478: JOIN "initiative" ON "initiative"."id" = "supporter"."initiative_id" jbe@478: JOIN "issue" ON "issue"."id" = "initiative"."issue_id" jbe@478: WHERE "supporter"."member_id" != "member_id_p" jbe@478: AND "issue"."area_id" = "area_id_p" jbe@478: AND "issue"."state" IN ('admission', 'discussion', 'verification') jbe@478: ) AS "subquery" jbe@478: ORDER BY "seed" jbe@478: LOOP jbe@478: SELECT "initiative".* INTO "result_row" jbe@478: FROM "initiative" jbe@478: JOIN "issue" ON "issue"."id" = "initiative"."issue_id" jbe@478: JOIN "supporter" ON "supporter"."initiative_id" = "initiative"."id" jbe@478: LEFT JOIN "supporter" AS "self_support" ON jbe@478: "self_support"."initiative_id" = "initiative"."id" AND jbe@478: "self_support"."member_id" = "member_id_p" jbe@478: WHERE "supporter"."member_id" = "member_id_v" jbe@478: AND "issue"."area_id" = "area_id_p" jbe@478: AND "issue"."state" IN ('admission', 'discussion', 'verification') jbe@478: AND "self_support"."member_id" ISNULL jbe@478: AND NOT "initiative_id_ary" @> ARRAY["initiative"."id"] jbe@478: ORDER BY md5("seed_v" || '-' || "initiative"."id") jbe@478: LIMIT 1; jbe@478: IF FOUND THEN jbe@478: "match_v" := TRUE; jbe@478: "initiative_id_ary" := "initiative_id_ary" || "result_row"."id"; jbe@478: RETURN NEXT "result_row"; jbe@492: IF array_length("initiative_id_ary", 1) >= "member_row"."notification_sample_size" THEN jbe@478: RETURN; jbe@478: END IF; jbe@478: END IF; jbe@478: END LOOP; jbe@478: EXIT WHEN NOT "match_v"; jbe@478: END LOOP; jbe@478: RETURN; jbe@478: END; jbe@478: $$; jbe@478: jbe@478: CREATE VIEW "updated_or_featured_initiative" AS jbe@478: SELECT * FROM "updated_initiative" jbe@478: UNION ALL jbe@478: SELECT jbe@478: "member"."id" AS "seen_by_member_id", jbe@478: FALSE AS "supported", jbe@492: EXISTS ( jbe@492: SELECT NULL FROM "draft" jbe@492: WHERE "draft"."initiative_id" = "initiative"."id" jbe@492: AND COALESCE( jbe@492: "draft"."id" > "sent"."last_draft_id", jbe@492: TRUE jbe@492: ) jbe@492: ) AS "new_draft", jbe@492: ( SELECT count(1) FROM "suggestion" jbe@492: WHERE "suggestion"."initiative_id" = "initiative"."id" jbe@492: AND COALESCE( jbe@492: "suggestion"."id" > "sent"."last_suggestion_id", jbe@492: TRUE jbe@492: ) jbe@492: ) AS "new_suggestion_count", jbe@478: TRUE AS "featured", jbe@478: NOT EXISTS ( jbe@478: SELECT NULL FROM "initiative" AS "better_initiative" jbe@478: WHERE jbe@484: "better_initiative"."issue_id" = "initiative"."issue_id" jbe@484: AND jbe@484: ( COALESCE("better_initiative"."harmonic_weight", -1), jbe@484: -"better_initiative"."id" ) > jbe@484: ( COALESCE("initiative"."harmonic_weight", -1), jbe@485: -"initiative"."id" ) jbe@478: ) AS "leading", jbe@478: "initiative".* jbe@478: FROM "member" CROSS JOIN "area" jbe@478: CROSS JOIN LATERAL jbe@492: "featured_initiative"("member"."id", "area"."id") AS "initiative" jbe@492: LEFT JOIN "initiative_notification_sent" AS "sent" jbe@492: ON "sent"."member_id" = "member"."id" jbe@492: AND "sent"."initiative_id" = "initiative"."id"; jbe@478: jbe@478: CREATE VIEW "leading_complement_initiative" AS jbe@478: SELECT * FROM ( jbe@478: SELECT DISTINCT ON ("seen_by_member_id", "initiative"."issue_id") jbe@492: "uf_initiative"."seen_by_member_id", jbe@492: "supporter"."member_id" NOTNULL AS "supported", jbe@492: CASE WHEN "supporter"."member_id" NOTNULL THEN FALSE ELSE jbe@492: EXISTS ( jbe@492: SELECT NULL FROM "draft" jbe@492: WHERE "draft"."initiative_id" = "initiative"."id" jbe@492: AND COALESCE( jbe@492: "draft"."id" > "sent"."last_draft_id", jbe@492: TRUE jbe@492: ) jbe@492: ) jbe@492: END AS "new_draft", jbe@492: CASE WHEN "supporter"."member_id" NOTNULL THEN 0 ELSE jbe@492: ( SELECT count(1) FROM "suggestion" jbe@492: WHERE "suggestion"."initiative_id" = "initiative"."id" jbe@492: AND COALESCE( jbe@492: "suggestion"."id" > "sent"."last_suggestion_id", jbe@492: TRUE jbe@492: ) jbe@492: ) jbe@492: END AS "new_suggestion_count", jbe@478: FALSE AS "featured", jbe@478: TRUE AS "leading", jbe@478: "initiative".* jbe@492: FROM "updated_or_featured_initiative" AS "uf_initiative" jbe@492: JOIN "initiative" ON jbe@492: "uf_initiative"."issue_id" = "initiative"."issue_id" jbe@492: LEFT JOIN "supporter" ON jbe@492: "supporter"."member_id" = "uf_initiative"."seen_by_member_id" AND jbe@492: "supporter"."initiative_id" = "initiative"."id" jbe@492: LEFT JOIN "initiative_notification_sent" AS "sent" jbe@492: ON "sent"."member_id" = "uf_initiative"."seen_by_member_id" jbe@492: AND "sent"."initiative_id" = "initiative"."id" jbe@478: ORDER BY jbe@478: "seen_by_member_id", jbe@478: "initiative"."issue_id", jbe@478: "initiative"."harmonic_weight" DESC, jbe@478: "initiative"."id" jbe@478: ) AS "subquery" jbe@478: WHERE NOT EXISTS ( jbe@478: SELECT NULL FROM "updated_or_featured_initiative" AS "other" jbe@478: WHERE "other"."seen_by_member_id" = "subquery"."seen_by_member_id" jbe@478: AND "other"."id" = "subquery"."id" jbe@478: ); jbe@478: jbe@492: CREATE VIEW "unfiltered_initiative_for_notification" AS jbe@478: SELECT * FROM "updated_or_featured_initiative" jbe@478: UNION ALL jbe@478: SELECT * FROM "leading_complement_initiative"; jbe@478: jbe@492: CREATE VIEW "initiative_for_notification" AS jbe@492: SELECT "initiative1".* jbe@492: FROM "unfiltered_initiative_for_notification" "initiative1" jbe@492: JOIN "issue" AS "issue1" ON "initiative1"."issue_id" = "issue1"."id" jbe@492: WHERE EXISTS ( jbe@492: SELECT NULL jbe@492: FROM "unfiltered_initiative_for_notification" "initiative2" jbe@492: JOIN "issue" AS "issue2" ON "initiative2"."issue_id" = "issue2"."id" jbe@492: WHERE "initiative1"."seen_by_member_id" = "initiative2"."seen_by_member_id" jbe@492: AND "issue1"."area_id" = "issue2"."area_id" jbe@492: AND ( "initiative2"."new_draft" OR "initiative2"."new_suggestion_count" > 0 ) jbe@492: ); jbe@492: jbe@492: CREATE FUNCTION "get_initiatives_for_notification" jbe@492: ( "member_id_p" "member"."id"%TYPE ) jbe@492: RETURNS SETOF "initiative_for_notification" jbe@492: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@492: DECLARE jbe@492: "result_row" "initiative_for_notification"%ROWTYPE; jbe@492: "last_draft_id_v" "draft"."id"%TYPE; jbe@492: "last_suggestion_id_v" "suggestion"."id"%TYPE; jbe@492: BEGIN jbe@492: PERFORM "require_transaction_isolation"(); jbe@492: PERFORM NULL FROM "member" WHERE "id" = "member_id_p" FOR UPDATE; jbe@492: FOR "result_row" IN jbe@492: SELECT * FROM "initiative_for_notification" jbe@492: WHERE "seen_by_member_id" = "member_id_p" jbe@492: LOOP jbe@492: SELECT "id" INTO "last_draft_id_v" FROM "draft" jbe@492: WHERE "draft"."initiative_id" = "result_row"."id" jbe@492: ORDER BY "id" DESC LIMIT 1; jbe@492: SELECT "id" INTO "last_suggestion_id_v" FROM "suggestion" jbe@492: WHERE "suggestion"."initiative_id" = "result_row"."id" jbe@492: ORDER BY "id" DESC LIMIT 1; jbe@492: INSERT INTO "initiative_notification_sent" jbe@492: ("member_id", "initiative_id", "last_draft_id", "last_suggestion_id") jbe@492: VALUES ( jbe@492: "member_id_p", jbe@492: "result_row"."id", jbe@493: "last_draft_id_v", jbe@493: "last_suggestion_id_v" ) jbe@492: ON CONFLICT ("member_id", "initiative_id") DO UPDATE SET jbe@492: "last_draft_id" = CASE jbe@494: WHEN "initiative_notification_sent"."last_draft_id" > "last_draft_id_v" jbe@494: THEN "initiative_notification_sent"."last_draft_id" jbe@492: ELSE "last_draft_id_v" jbe@492: END, jbe@492: "last_suggestion_id" = CASE jbe@494: WHEN "initiative_notification_sent"."last_suggestion_id" > "last_suggestion_id_v" jbe@494: THEN "initiative_notification_sent"."last_suggestion_id" jbe@492: ELSE "last_suggestion_id_v" jbe@492: END; jbe@492: RETURN NEXT "result_row"; jbe@492: END LOOP; jbe@492: DELETE FROM "initiative_notification_sent" jbe@492: USING "initiative", "issue" jbe@492: WHERE "initiative_notification_sent"."member_id" = "member_id_p" jbe@492: AND "initiative"."id" = "initiative_notification_sent"."initiative_id" jbe@492: AND "issue"."id" = "initiative"."issue_id" jbe@492: AND ( "issue"."closed" NOTNULL OR "issue"."fully_frozen" NOTNULL ); jbe@492: UPDATE "member" SET "notification_counter" = "notification_counter" + 1 jbe@492: WHERE "id" = "member_id_p"; jbe@492: RETURN; jbe@492: END; jbe@492: $$; jbe@492: jbe@478: COMMIT;