From 206f7bcb301bdc783d7e5df3bf0c088e5e24ac18 Mon Sep 17 00:00:00 2001 From: Lucas Santana Date: Thu, 6 Feb 2025 20:06:25 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20Criando=20tabelas=20para=20nova=20funci?= =?UTF-8?q?onalidade=20de=20corre=C3=A7=C3=A3o=20de=20reda=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20240326000001_create_essay_system.sql | 171 ++++++++++++++++++ ...326000001_create_essay_system_rollback.sql | 30 +++ .../20240326000002_insert_essay_data.sql | 141 +++++++++++++++ 3 files changed, 342 insertions(+) create mode 100644 supabase/migrations/20240326000001_create_essay_system.sql create mode 100644 supabase/migrations/20240326000001_create_essay_system_rollback.sql create mode 100644 supabase/migrations/20240326000002_insert_essay_data.sql diff --git a/supabase/migrations/20240326000001_create_essay_system.sql b/supabase/migrations/20240326000001_create_essay_system.sql new file mode 100644 index 0000000..8357c45 --- /dev/null +++ b/supabase/migrations/20240326000001_create_essay_system.sql @@ -0,0 +1,171 @@ +-- Criação do sistema de redações (Essays) +-- Tipos de texto (Narrativo, Dissertativo, etc) +CREATE TABLE public.essay_types ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + slug TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + description TEXT NOT NULL, + icon TEXT NOT NULL, + active BOOLEAN DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Gêneros textuais (Artigo de opinião, Carta argumentativa, etc) +CREATE TABLE public.essay_genres ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + type_id UUID NOT NULL REFERENCES public.essay_types(id), + slug TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + description TEXT NOT NULL, + icon TEXT NOT NULL, + requirements JSONB NOT NULL DEFAULT '{}', + active BOOLEAN DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Redações dos alunos +CREATE TABLE public.student_essays ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + student_id UUID NOT NULL REFERENCES public.students(id), + type_id UUID NOT NULL REFERENCES public.essay_types(id), + genre_id UUID NOT NULL REFERENCES public.essay_genres(id), + title TEXT NOT NULL, + content TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'draft', + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL, + CONSTRAINT status_check CHECK (status IN ('draft', 'submitted', 'analyzed')) +); + +-- Análises das redações +CREATE TABLE public.essay_analyses ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + essay_id UUID NOT NULL REFERENCES public.student_essays(id), + overall_score INTEGER NOT NULL CHECK (overall_score >= 0 AND overall_score <= 100), + feedback JSONB NOT NULL DEFAULT '{}', + strengths TEXT[] DEFAULT ARRAY[]::TEXT[], + improvements TEXT[] DEFAULT ARRAY[]::TEXT[], + suggestions TEXT, + criteria_scores JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Índices para melhor performance +CREATE INDEX idx_student_essays_student_id ON public.student_essays(student_id); +CREATE INDEX idx_student_essays_status ON public.student_essays(status); +CREATE INDEX idx_essay_genres_type_id ON public.essay_genres(type_id); +CREATE INDEX idx_essay_analyses_essay_id ON public.essay_analyses(essay_id); + +-- Triggers para updated_at +CREATE OR REPLACE FUNCTION public.handle_updated_at() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$ language 'plpgsql'; + +CREATE TRIGGER essay_types_updated_at + BEFORE UPDATE ON public.essay_types + FOR EACH ROW + EXECUTE PROCEDURE public.handle_updated_at(); + +CREATE TRIGGER essay_genres_updated_at + BEFORE UPDATE ON public.essay_genres + FOR EACH ROW + EXECUTE PROCEDURE public.handle_updated_at(); + +CREATE TRIGGER student_essays_updated_at + BEFORE UPDATE ON public.student_essays + FOR EACH ROW + EXECUTE PROCEDURE public.handle_updated_at(); + +-- Políticas RLS +ALTER TABLE public.essay_types ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.essay_genres ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.student_essays ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.essay_analyses ENABLE ROW LEVEL SECURITY; + +-- Políticas para essay_types (visível para todos) +CREATE POLICY "Tipos de redação visíveis para todos" + ON public.essay_types FOR SELECT + USING (active = true); + +-- Políticas para essay_genres (visível para todos) +CREATE POLICY "Gêneros textuais visíveis para todos" + ON public.essay_genres FOR SELECT + USING (active = true); + +-- Políticas para student_essays +CREATE POLICY "Alunos podem ver suas próprias redações" + ON public.student_essays FOR SELECT + USING (student_id = auth.uid()); + +CREATE POLICY "Alunos podem criar suas próprias redações" + ON public.student_essays FOR INSERT + WITH CHECK (student_id = auth.uid()); + +CREATE POLICY "Alunos podem atualizar suas próprias redações" + ON public.student_essays FOR UPDATE + USING (student_id = auth.uid()) + WITH CHECK (student_id = auth.uid()); + +-- Políticas para essay_analyses +CREATE POLICY "Alunos podem ver análises de suas próprias redações" + ON public.essay_analyses FOR SELECT + USING ( + essay_id IN ( + SELECT id FROM public.student_essays + WHERE student_id = auth.uid() + ) + ); + +-- Função para verificar se uma redação pertence ao aluno +CREATE OR REPLACE FUNCTION public.check_essay_ownership(essay_id UUID) +RETURNS BOOLEAN AS $$ +BEGIN + RETURN EXISTS ( + SELECT 1 + FROM public.student_essays + WHERE id = essay_id + AND student_id = auth.uid() + ); +END; +$$ language plpgsql security definer; + +-- Comentários nas tabelas +COMMENT ON TABLE public.essay_types IS 'Tipos de texto (Narrativo, Dissertativo, etc)'; +COMMENT ON TABLE public.essay_genres IS 'Gêneros textuais relacionados a cada tipo de texto'; +COMMENT ON TABLE public.student_essays IS 'Redações escritas pelos alunos'; +COMMENT ON TABLE public.essay_analyses IS 'Análises e feedbacks das redações dos alunos'; + +-- Dados iniciais +INSERT INTO public.essay_types (slug, title, description, icon) VALUES + ('narrative', 'Narrativo', 'Textos que contam uma história com personagens, tempo e espaço definidos', '📖'), + ('dissertation', 'Dissertativo', 'Textos que apresentam uma análise e discussão de um tema', '📝'), + ('descriptive', 'Descritivo', 'Textos que descrevem detalhadamente um objeto, pessoa, lugar ou situação', '🎨'); + +INSERT INTO public.essay_genres (type_id, slug, title, description, icon, requirements) VALUES + ((SELECT id FROM public.essay_types WHERE slug = 'dissertation'), + 'opinion-article', + 'Artigo de Opinião', + 'Texto que apresenta um ponto de vista sobre um tema atual', + '📰', + '{"min_words": 300, "max_words": 600, "required_sections": ["introduction", "development", "conclusion"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'narrative'), + 'short-story', + 'Conto', + 'História curta com poucos personagens e um único conflito', + '📚', + '{"min_words": 200, "max_words": 1000, "required_elements": ["characters", "setting", "conflict", "resolution"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'descriptive'), + 'character-description', + 'Descrição de Personagem', + 'Texto que descreve características físicas e psicológicas de um personagem', + '👤', + '{"min_words": 150, "max_words": 400, "required_aspects": ["physical", "psychological", "habits"]}' + ); \ No newline at end of file diff --git a/supabase/migrations/20240326000001_create_essay_system_rollback.sql b/supabase/migrations/20240326000001_create_essay_system_rollback.sql new file mode 100644 index 0000000..6a74a04 --- /dev/null +++ b/supabase/migrations/20240326000001_create_essay_system_rollback.sql @@ -0,0 +1,30 @@ +-- Remover políticas +DROP POLICY IF EXISTS "Alunos podem ver análises de suas próprias redações" ON public.essay_analyses; +DROP POLICY IF EXISTS "Alunos podem atualizar suas próprias redações" ON public.student_essays; +DROP POLICY IF EXISTS "Alunos podem criar suas próprias redações" ON public.student_essays; +DROP POLICY IF EXISTS "Alunos podem ver suas próprias redações" ON public.student_essays; +DROP POLICY IF EXISTS "Gêneros textuais visíveis para todos" ON public.essay_genres; +DROP POLICY IF EXISTS "Tipos de redação visíveis para todos" ON public.essay_types; + +-- Remover função de verificação de propriedade +DROP FUNCTION IF EXISTS public.check_essay_ownership(UUID); + +-- Remover triggers +DROP TRIGGER IF EXISTS student_essays_updated_at ON public.student_essays; +DROP TRIGGER IF EXISTS essay_genres_updated_at ON public.essay_genres; +DROP TRIGGER IF EXISTS essay_types_updated_at ON public.essay_types; + +-- Remover função do trigger +DROP FUNCTION IF EXISTS public.handle_updated_at(); + +-- Remover índices +DROP INDEX IF EXISTS public.idx_essay_analyses_essay_id; +DROP INDEX IF EXISTS public.idx_essay_genres_type_id; +DROP INDEX IF EXISTS public.idx_student_essays_status; +DROP INDEX IF EXISTS public.idx_student_essays_student_id; + +-- Remover tabelas +DROP TABLE IF EXISTS public.essay_analyses; +DROP TABLE IF EXISTS public.student_essays; +DROP TABLE IF EXISTS public.essay_genres; +DROP TABLE IF EXISTS public.essay_types; \ No newline at end of file diff --git a/supabase/migrations/20240326000002_insert_essay_data.sql b/supabase/migrations/20240326000002_insert_essay_data.sql new file mode 100644 index 0000000..458f86c --- /dev/null +++ b/supabase/migrations/20240326000002_insert_essay_data.sql @@ -0,0 +1,141 @@ +-- Inserir tipos textuais +INSERT INTO public.essay_types (slug, title, description, icon) VALUES + ('narrative', 'Narrativo', 'Textos que narram acontecimentos reais ou fictícios, com personagens, tempo e espaço definidos', '📖'), + ('descriptive', 'Descrição', 'Textos que descrevem detalhadamente características de algo ou alguém', '🎨'), + ('expository', 'Expositivo', 'Textos que explicam e informam sobre um determinado assunto', '📚'), + ('argumentative', 'Argumentativo', 'Textos que defendem uma ideia ou ponto de vista com argumentos', '⚖️'), + ('injunctive', 'Injuntivo', 'Textos que orientam ou instruem sobre como realizar algo', '📝'); + +-- Inserir gêneros textuais para tipo Narrativo +INSERT INTO public.essay_genres (type_id, slug, title, description, icon, requirements) VALUES + ((SELECT id FROM public.essay_types WHERE slug = 'narrative'), + 'short-story', + 'Conto', + 'História curta com poucos personagens e um único conflito', + '📚', + '{"min_words": 200, "max_words": 1000, "required_elements": ["personagens", "tempo", "espaço", "conflito", "resolução"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'narrative'), + 'chronicle', + 'Crônica', + 'Narrativa curta que retrata situações do cotidiano', + '📰', + '{"min_words": 150, "max_words": 800, "required_elements": ["situação_cotidiana", "reflexão", "linguagem_informal"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'narrative'), + 'novel', + 'Romance', + 'História longa com desenvolvimento aprofundado de personagens e tramas', + '📖', + '{"min_words": 1000, "max_words": 3000, "required_elements": ["personagens_principais", "personagens_secundários", "múltiplos_conflitos", "desenvolvimento_completo"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'narrative'), + 'news', + 'Notícia', + 'Relato de fatos reais de forma objetiva', + '📰', + '{"min_words": 150, "max_words": 500, "required_elements": ["lead", "corpo_da_notícia", "objetividade"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'narrative'), + 'biography', + 'Biografia/Autobiografia', + 'Relato da vida de uma pessoa', + '👤', + '{"min_words": 300, "max_words": 1000, "required_elements": ["dados_pessoais", "acontecimentos_importantes", "ordem_cronológica"]}' + ); + +-- Inserir gêneros textuais para tipo Descritivo +INSERT INTO public.essay_genres (type_id, slug, title, description, icon, requirements) VALUES + ((SELECT id FROM public.essay_types WHERE slug = 'descriptive'), + 'menu', + 'Cardápio', + 'Descrição detalhada de pratos e bebidas', + '🍽️', + '{"min_words": 50, "max_words": 200, "required_elements": ["nome_do_prato", "ingredientes", "preço"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'descriptive'), + 'descriptive-report', + 'Relato descritivo', + 'Descrição detalhada de um objeto, pessoa ou ambiente', + '🔍', + '{"min_words": 200, "max_words": 600, "required_elements": ["características_físicas", "sensações", "detalhes_específicos"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'descriptive'), + 'reportage', + 'Reportagem', + 'Descrição aprofundada de um fato ou tema', + '📰', + '{"min_words": 400, "max_words": 1000, "required_elements": ["contextualização", "detalhamento", "fontes"]}' + ); + +-- Inserir gêneros textuais para tipo Expositivo +INSERT INTO public.essay_genres (type_id, slug, title, description, icon, requirements) VALUES + ((SELECT id FROM public.essay_types WHERE slug = 'expository'), + 'didactic-text', + 'Texto didático', + 'Explicação clara de um conteúdo para fins educacionais', + '📚', + '{"min_words": 200, "max_words": 800, "required_elements": ["definição", "exemplos", "explicação"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'expository'), + 'lecture', + 'Palestra', + 'Apresentação expositiva sobre um tema específico', + '🎤', + '{"min_words": 500, "max_words": 1500, "required_elements": ["introdução", "desenvolvimento", "conclusão", "exemplos_práticos"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'expository'), + 'reportage-exp', + 'Reportagem', + 'Texto informativo sobre um tema ou acontecimento', + '📰', + '{"min_words": 400, "max_words": 1000, "required_elements": ["contextualização", "dados", "fontes"]}' + ); + +-- Inserir gêneros textuais para tipo Argumentativo +INSERT INTO public.essay_genres (type_id, slug, title, description, icon, requirements) VALUES + ((SELECT id FROM public.essay_types WHERE slug = 'argumentative'), + 'open-letter', + 'Carta aberta', + 'Texto que expõe publicamente argumentos sobre uma questão', + '✉️', + '{"min_words": 300, "max_words": 800, "required_elements": ["destinatário", "argumentos", "pedido_ou_reivindicação"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'argumentative'), + 'thesis', + 'Tese', + 'Texto que defende uma ideia central com argumentos', + '📑', + '{"min_words": 500, "max_words": 1500, "required_elements": ["hipótese", "argumentos", "comprovação"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'argumentative'), + 'scientific-article', + 'Artigo científico', + 'Texto que apresenta resultados de uma pesquisa', + '🔬', + '{"min_words": 1000, "max_words": 3000, "required_elements": ["introdução", "metodologia", "resultados", "conclusão"]}' + ); + +-- Inserir gêneros textuais para tipo Injuntivo +INSERT INTO public.essay_genres (type_id, slug, title, description, icon, requirements) VALUES + ((SELECT id FROM public.essay_types WHERE slug = 'injunctive'), + 'instruction-manual', + 'Manual de instrução', + 'Texto que orienta sobre o uso de um produto', + '📖', + '{"min_words": 100, "max_words": 500, "required_elements": ["passo_a_passo", "advertências", "ilustrações"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'injunctive'), + 'advertisement', + 'Propaganda', + 'Texto que persuade o leitor a uma ação', + '📢', + '{"min_words": 50, "max_words": 200, "required_elements": ["slogan", "argumentos_persuasivos", "chamada_para_ação"]}' + ), + ((SELECT id FROM public.essay_types WHERE slug = 'injunctive'), + 'recipe', + 'Receita', + 'Texto que instrui o preparo de um prato', + '👩‍🍳', + '{"min_words": 100, "max_words": 400, "required_elements": ["ingredientes", "modo_de_preparo", "tempo_de_preparo", "rendimento"]}' + ); \ No newline at end of file