workspace-v1/projects/gamilit/database/schemas/04-progress-ddl.sql
Adrian Flores Cortes 967ab360bb Initial commit: Workspace v1 with 3-layer architecture
Structure:
- control-plane/: Registries, SIMCO directives, CI/CD templates
- projects/: Gamilit, ERP-Suite, Trading-Platform, Betting-Analytics
- shared/: Libs catalog, knowledge-base

Key features:
- Centralized port, domain, database, and service registries
- 23 SIMCO directives + 6 fundamental principles
- NEXUS agent profiles with delegation rules
- Validation scripts for workspace integrity
- Dockerfiles for all services
- Path aliases for quick reference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 00:35:19 -06:00

156 lines
6.6 KiB
SQL

-- ==============================================================================
-- GAMILIT - SCHEMA PROGRESS
-- ==============================================================================
-- Tablas de progreso, matriculas y estadisticas
-- Mantenido por: Database-Agent
-- Actualizado: 2025-12-18
-- ==============================================================================
-- ------------------------------------------------------------------------------
-- TABLA: ENROLLMENTS (Matriculas)
-- ------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS enrollments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
course_id UUID NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
status VARCHAR(50) DEFAULT 'active', -- active, completed, dropped
enrolled_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMPTZ,
progress_percentage DECIMAL(5,2) DEFAULT 0,
last_accessed_at TIMESTAMPTZ,
UNIQUE(user_id, course_id)
);
CREATE INDEX idx_enrollments_user ON enrollments(user_id);
CREATE INDEX idx_enrollments_course ON enrollments(course_id);
CREATE INDEX idx_enrollments_status ON enrollments(status);
-- ------------------------------------------------------------------------------
-- TABLA: LESSON_PROGRESS
-- ------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS lesson_progress (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
lesson_id UUID NOT NULL REFERENCES lessons(id) ON DELETE CASCADE,
status VARCHAR(50) DEFAULT 'not_started', -- not_started, in_progress, completed
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
time_spent_seconds INTEGER DEFAULT 0,
score DECIMAL(5,2),
attempts INTEGER DEFAULT 0,
UNIQUE(user_id, lesson_id)
);
CREATE INDEX idx_lesson_progress_user ON lesson_progress(user_id);
CREATE INDEX idx_lesson_progress_lesson ON lesson_progress(lesson_id);
CREATE INDEX idx_lesson_progress_status ON lesson_progress(status);
-- ------------------------------------------------------------------------------
-- TABLA: QUIZ_ATTEMPTS
-- ------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS quiz_attempts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
quiz_id UUID NOT NULL REFERENCES quizzes(id) ON DELETE CASCADE,
score DECIMAL(5,2) NOT NULL,
passed BOOLEAN NOT NULL,
answers JSONB NOT NULL,
time_taken_seconds INTEGER,
started_at TIMESTAMPTZ NOT NULL,
completed_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_quiz_attempts_user ON quiz_attempts(user_id);
CREATE INDEX idx_quiz_attempts_quiz ON quiz_attempts(quiz_id);
-- ------------------------------------------------------------------------------
-- TABLA: USER_ACHIEVEMENTS
-- ------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS user_achievements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
achievement_id UUID NOT NULL REFERENCES achievements(id) ON DELETE CASCADE,
earned_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
progress_data JSONB DEFAULT '{}',
UNIQUE(user_id, achievement_id)
);
CREATE INDEX idx_user_achievements_user ON user_achievements(user_id);
CREATE INDEX idx_user_achievements_achievement ON user_achievements(achievement_id);
-- ------------------------------------------------------------------------------
-- TABLA: USER_POINTS
-- ------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS user_points (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
points_type VARCHAR(50) NOT NULL, -- course, quiz, achievement, bonus
points INTEGER NOT NULL,
source_type VARCHAR(50), -- course, lesson, quiz, achievement
source_id UUID,
description TEXT,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_user_points_user ON user_points(user_id);
CREATE INDEX idx_user_points_type ON user_points(points_type);
CREATE INDEX idx_user_points_date ON user_points(created_at);
-- ------------------------------------------------------------------------------
-- TABLA: USER_XP (Experiencia)
-- ------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS user_xp (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
total_xp INTEGER DEFAULT 0,
level INTEGER DEFAULT 1,
xp_to_next_level INTEGER DEFAULT 100,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id)
);
CREATE INDEX idx_user_xp_user ON user_xp(user_id);
CREATE INDEX idx_user_xp_level ON user_xp(level);
-- ------------------------------------------------------------------------------
-- TABLA: STREAKS (Rachas)
-- ------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS streaks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
current_streak INTEGER DEFAULT 0,
longest_streak INTEGER DEFAULT 0,
last_activity_date DATE,
streak_type VARCHAR(50) DEFAULT 'daily', -- daily, weekly
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, streak_type)
);
CREATE INDEX idx_streaks_user ON streaks(user_id);
-- ------------------------------------------------------------------------------
-- VISTA: LEADERBOARD
-- ------------------------------------------------------------------------------
CREATE OR REPLACE VIEW leaderboard AS
SELECT
u.id as user_id,
u.first_name,
u.last_name,
u.avatar_url,
u.tenant_id,
COALESCE(SUM(up.points), 0) as total_points,
COALESCE(ux.level, 1) as level,
COALESCE(ux.total_xp, 0) as total_xp,
COUNT(DISTINCT ua.achievement_id) as achievements_count,
COALESCE(s.current_streak, 0) as current_streak
FROM users u
LEFT JOIN user_points up ON u.id = up.user_id
LEFT JOIN user_xp ux ON u.id = ux.user_id
LEFT JOIN user_achievements ua ON u.id = ua.user_id
LEFT JOIN streaks s ON u.id = s.user_id AND s.streak_type = 'daily'
WHERE u.deleted_at IS NULL
GROUP BY u.id, u.first_name, u.last_name, u.avatar_url, u.tenant_id,
ux.level, ux.total_xp, s.current_streak
ORDER BY total_points DESC;