Compare commits
1 Commits
main
...
e9fbee43e3
| Author | SHA1 | Date | |
|---|---|---|---|
| e9fbee43e3 |
181
migrations/009_tasks-module.sql
Normal file
181
migrations/009_tasks-module.sql
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- Migration 009: Tasks Module — Tasks with Reward System
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- Block 1: Tasks Table
|
||||||
|
-- Stores community Tasks with Geometry, Moderation and Completion.
|
||||||
|
-- Status Flow: pending → rejected | open → completed → 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', 'open', 'completed', 'verified')),
|
||||||
|
address VARCHAR(255),
|
||||||
|
|
||||||
|
-- Completion Fields (NULL until 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: User Points Table
|
||||||
|
-- One Entry per verified Task Completion. Leaderboard via SUM/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: Extends Votes Table for Tasks
|
||||||
|
-- Either contribution_id OR task_id is set, not both.
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
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: Extends Comments Table for Tasks
|
||||||
|
-- Either contribution_id OR task_id is set, not both.
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
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_at 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 the 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
|
||||||
|
-- Only counts approved Comments. Mirrors Contribution Pattern.
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
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 (optional)
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
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';
|
||||||
Reference in New Issue
Block a user