3 Commits

Author SHA1 Message Date
0b97dd4095 commented tasks module database migration 2026-05-07 14:07:39 +02:00
08e7060b1b typo 2026-05-07 14:00:16 +02:00
e9fbee43e3 migration for tasks module and reward system 2026-05-06 16:06:43 +02:00
2 changed files with 182 additions and 1 deletions

View File

@@ -35,7 +35,7 @@ COMMENT ON TABLE municipalities IS 'Configuration Per Municipality (Tenant) usin
-- ---------------------------------------------------------------------
-- Block 3: Table "contributions"
-- Aitizen and Administration Contributions as Points, Lines, and
-- Citizen and Administration Contributions as Points, Lines, and
-- Polygons stored together in one mixed-geometry Column.
-- ---------------------------------------------------------------------
CREATE TABLE contributions (

View File

@@ -0,0 +1,181 @@
-- =====================================================================
-- Migration 009: Tasks Module — Tasks with Reward System
-- =====================================================================
-- ---------------------------------------------------------------------
-- Block 1: Tasks Table
-- Stores Tasks with Geometry, Moderation and Completion.
-- Status Flow from pending to rejected or approved to completed to verified
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS tasks (
task_id SERIAL PRIMARY KEY,
municipality_id INTEGER NOT NULL REFERENCES municipalities(municipality_id),
geom GEOMETRY(Geometry, 4326) NOT NULL,
geom_type VARCHAR(10) NOT NULL CHECK (geom_type IN ('point', 'line', 'polygon')),
category VARCHAR(50) NOT NULL,
title VARCHAR(200) NOT NULL,
description TEXT DEFAULT '',
points_reward INTEGER NOT NULL DEFAULT 25,
author_name VARCHAR(100) NOT NULL,
browser_id VARCHAR(36),
photo_path VARCHAR(255),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'rejected', 'approved', 'completed', 'verified')),
address VARCHAR(255),
-- Completion Fields empty before completed
completed_by_name VARCHAR(100),
completed_by_browser VARCHAR(36),
completion_photo VARCHAR(255),
completion_comment TEXT,
completed_at TIMESTAMP,
-- Counters updated via Triggers
likes_count INTEGER NOT NULL DEFAULT 0,
dislikes_count INTEGER NOT NULL DEFAULT 0,
comment_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_tasks_municipality ON tasks(municipality_id);
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_category ON tasks(category);
-- ---------------------------------------------------------------------
-- Block 2: Citizen Points Table
-- One Row per Completion. Leaderboard via SUM and GROUP BY.
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS user_points (
points_id SERIAL PRIMARY KEY,
municipality_id INTEGER NOT NULL REFERENCES municipalities(municipality_id),
user_name VARCHAR(100) NOT NULL,
points INTEGER NOT NULL DEFAULT 25,
task_id INTEGER NOT NULL REFERENCES tasks(task_id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_user_points_municipality ON user_points(municipality_id);
CREATE INDEX idx_user_points_user ON user_points(user_name);
-- ---------------------------------------------------------------------
-- Block 3: Adapts Votes Table for Tasks
-- Either contribution_id OR task_id
-- ---------------------------------------------------------------------
ALTER TABLE votes
ADD COLUMN task_id INTEGER REFERENCES tasks(task_id) ON DELETE CASCADE;
CREATE INDEX idx_votes_task ON votes(task_id);
-- Unique Vote per Browser per Task
ALTER TABLE votes
ADD CONSTRAINT votes_task_browser_unique
UNIQUE (task_id, browser_id);
-- ---------------------------------------------------------------------
-- Block 4: Adapts Comments Table for Tasks
-- Either contribution_id OR task_id
-- ---------------------------------------------------------------------
ALTER TABLE comments
ADD COLUMN task_id INTEGER REFERENCES tasks(task_id) ON DELETE CASCADE;
CREATE INDEX idx_comments_task ON comments(task_id);
-- ---------------------------------------------------------------------
-- Block 5: Trigger Updated Timestamp for Tasks
-- ---------------------------------------------------------------------
CREATE TRIGGER set_tasks_updated_at
BEFORE UPDATE ON tasks
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
-- ---------------------------------------------------------------------
-- Block 6: Trigger Vote Counts for Tasks
-- Mirrors Pattern from Contributions.
-- ---------------------------------------------------------------------
CREATE OR REPLACE FUNCTION update_task_vote_counts()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
IF NEW.task_id IS NOT NULL THEN
UPDATE tasks SET
likes_count = (SELECT COUNT(*) FROM votes WHERE task_id = NEW.task_id AND vote_type = 'like'),
dislikes_count = (SELECT COUNT(*) FROM votes WHERE task_id = NEW.task_id AND vote_type = 'dislike')
WHERE task_id = NEW.task_id;
END IF;
END IF;
IF TG_OP = 'DELETE' OR (TG_OP = 'UPDATE' AND OLD.task_id IS NOT NULL) THEN
UPDATE tasks SET
likes_count = (SELECT COUNT(*) FROM votes WHERE task_id = OLD.task_id AND vote_type = 'like'),
dislikes_count = (SELECT COUNT(*) FROM votes WHERE task_id = OLD.task_id AND vote_type = 'dislike')
WHERE task_id = OLD.task_id;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_update_task_vote_counts
AFTER INSERT OR DELETE OR UPDATE ON votes
FOR EACH ROW
WHEN (NEW.task_id IS NOT NULL OR OLD.task_id IS NOT NULL)
EXECUTE FUNCTION update_task_vote_counts();
-- ---------------------------------------------------------------------
-- Block 7: Trigger Comment Count for Tasks
-- Mirrors Pattern from Contributions.
-- ---------------------------------------------------------------------
CREATE OR REPLACE FUNCTION update_task_comment_count()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
IF NEW.task_id IS NOT NULL THEN
UPDATE tasks
SET comment_count = (
SELECT COUNT(*) FROM comments
WHERE task_id = NEW.task_id AND status = 'approved'
)
WHERE task_id = NEW.task_id;
END IF;
END IF;
IF TG_OP = 'DELETE' OR (TG_OP = 'UPDATE' AND OLD.task_id IS NOT NULL) THEN
UPDATE tasks
SET comment_count = (
SELECT COUNT(*) FROM comments
WHERE task_id = OLD.task_id AND status = 'approved'
)
WHERE task_id = OLD.task_id;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_update_task_comment_count
AFTER INSERT OR DELETE OR UPDATE OF status ON comments
FOR EACH ROW
WHEN (NEW.task_id IS NOT NULL OR OLD.task_id IS NOT NULL)
EXECUTE FUNCTION update_task_comment_count();
-- ---------------------------------------------------------------------
-- Block 8: Views for QGIS
-- ---------------------------------------------------------------------
CREATE OR REPLACE VIEW tasks_points AS
SELECT * FROM tasks WHERE geom_type = 'point';
CREATE OR REPLACE VIEW tasks_lines AS
SELECT * FROM tasks WHERE geom_type = 'line';
CREATE OR REPLACE VIEW tasks_polygons AS
SELECT * FROM tasks WHERE geom_type = 'polygon';