Update OrgAI project files for version 2.0 release#1
Update OrgAI project files for version 2.0 release#1Miranda3000-CPU wants to merge 3 commits intoJeiel0rbit:mainfrom
Conversation
Miranda3000-CPU
commented
Mar 27, 2026
- Renamed main script from orgai.py to OrgAI.py for consistency.
- Added image.png to the data files in the PyInstaller spec.
- Updated README.md to reflect new features and installation instructions.
- Enhanced index.html with a modern design using Bootstrap and improved layout.
- Removed obsolete pyinstaller directory.
- Upgraded PyInstaller version in requirements.txt to 6.19.0.
- Renamed main script from orgai.py to OrgAI.py for consistency. - Added image.png to the data files in the PyInstaller spec. - Updated README.md to reflect new features and installation instructions. - Enhanced index.html with a modern design using Bootstrap and improved layout. - Removed obsolete pyinstaller directory. - Upgraded PyInstaller version in requirements.txt to 6.19.0.
There was a problem hiding this comment.
Code Review
This pull request upgrades OrgAI to version 2.0, introducing a local Naive Bayes learning model for file classification and a modernized PyQt6 interface. Key improvements include the transition to pathlib for file operations and enhanced error handling. Feedback focuses on correcting a future-dated copyright year, refining broad exception handling to be more specific, and ensuring proper UTF-8 encoding for the learning model. Additionally, the reviewer recommended replacing magic numbers with constants and fixing missing Portuguese accents in user-facing text across the application, documentation, and landing page.
OrgAI.py
Outdated
| APP_TITLE = "OrgAI" | ||
| APP_VERSION = "2.0" | ||
| APP_ID = "br.orgai.desktop" | ||
| APP_COPYRIGHT = '© 2026, Jeiel Lima Miranda. Todos os direitos reservados.<br><br><a href="https://jeielmiranda.is-a.dev/OrgAI/">Visitar Website</a>' |
| except Exception: | ||
| self.total_samples = 0 | ||
| self.extension_totals = Counter() | ||
| self.token_counts = defaultdict(Counter) |
There was a problem hiding this comment.
O uso de except Exception: é muito amplo e pode mascarar diferentes tipos de erros (ex: JSON malformado, problemas de permissão de arquivo), dificultando a depuração. É uma boa prática capturar exceções mais específicas, como json.JSONDecodeError e IOError, para tratar cada caso de erro adequadamente.
| except Exception: | |
| self.total_samples = 0 | |
| self.extension_totals = Counter() | |
| self.token_counts = defaultdict(Counter) | |
| except (json.JSONDecodeError, IOError): | |
| self.total_samples = 0 | |
| self.extension_totals = Counter() | |
| self.token_counts = defaultdict(Counter) |
| "extension_totals": dict(self.extension_totals), | ||
| "token_counts": {key: dict(value) for key, value in self.token_counts.items()}, | ||
| } | ||
| self.model_path.write_text(json.dumps(payload, ensure_ascii=True, indent=2), encoding="utf-8") |
There was a problem hiding this comment.
O uso de ensure_ascii=True ao salvar o modelo JSON fará com que caracteres não-ASCII (como á, ç, õ) em nomes de arquivos sejam salvos como sequências de escape (ex: \uXXXX). Isso pode não ser o ideal para um modelo que precisa lidar com nomes de arquivos em português. Recomendo usar ensure_ascii=False para manter a codificação UTF-8, que é mais robusta para textos internacionalizados.
| self.model_path.write_text(json.dumps(payload, ensure_ascii=True, indent=2), encoding="utf-8") | |
| self.model_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") |
| second_score = sorted_scores[1][1] if len(sorted_scores) > 1 else best_score - 1.5 | ||
|
|
||
| margin = max(0.0, best_score - second_score) | ||
| confidence = min(0.99, 0.5 + (margin / (margin + 4.0))) |
There was a problem hiding this comment.
Os valores 1.5 e 4.0 usados no cálculo de confiança são "números mágicos". Para melhorar a legibilidade e a manutenibilidade do código, é recomendável defini-los como constantes nomeadas no início da classe ou do método. Isso torna o propósito desses valores claro e facilita futuras alterações.
Por exemplo:
DEFAULT_SCORE_MARGIN = 1.5
CONFIDENCE_SCALING_FACTOR = 4.0| EXTENSION_HINTS = { | ||
| "PDF": "Documento de leitura identificado.", | ||
| "DOC": "Documento de texto detectado.", | ||
| "DOCX": "Documento de texto detectado.", | ||
| "XLS": "Planilha identificada.", | ||
| "XLSX": "Planilha identificada.", | ||
| "CSV": "Tabela de dados identificada.", | ||
| "TXT": "Arquivo de texto simples.", | ||
| "JPG": "Imagem detectada.", | ||
| "PNG": "Imagem detectada.", | ||
| "GIF": "Imagem animada detectada.", | ||
| "MP4": "Video identificado.", | ||
| "MP3": "Audio identificado.", | ||
| "ZIP": "Arquivo compactado detectado.", | ||
| "RAR": "Arquivo compactado detectado.", | ||
| "EXE": "Aplicativo executavel detectado.", | ||
| "SEM_EXTENSAO": "Arquivo sem extensao; separado para revisao.", | ||
| } |
There was a problem hiding this comment.
Várias strings de texto voltadas para o usuário em EXTENSION_HINTS estão sem a acentuação correta. Corrigir isso melhorará a qualidade da interface e a experiência do usuário.
Video->VídeoAudio->Áudioexecutavel->executávelextensao->extensãorevisao->revisão
| EXTENSION_HINTS = { | |
| "PDF": "Documento de leitura identificado.", | |
| "DOC": "Documento de texto detectado.", | |
| "DOCX": "Documento de texto detectado.", | |
| "XLS": "Planilha identificada.", | |
| "XLSX": "Planilha identificada.", | |
| "CSV": "Tabela de dados identificada.", | |
| "TXT": "Arquivo de texto simples.", | |
| "JPG": "Imagem detectada.", | |
| "PNG": "Imagem detectada.", | |
| "GIF": "Imagem animada detectada.", | |
| "MP4": "Video identificado.", | |
| "MP3": "Audio identificado.", | |
| "ZIP": "Arquivo compactado detectado.", | |
| "RAR": "Arquivo compactado detectado.", | |
| "EXE": "Aplicativo executavel detectado.", | |
| "SEM_EXTENSAO": "Arquivo sem extensao; separado para revisao.", | |
| } | |
| EXTENSION_HINTS = { | |
| "PDF": "Documento de leitura identificado.", | |
| "DOC": "Documento de texto detectado.", | |
| "DOCX": "Documento de texto detectado.", | |
| "XLS": "Planilha identificada.", | |
| "XLSX": "Planilha identificada.", | |
| "CSV": "Tabela de dados identificada.", | |
| "TXT": "Arquivo de texto simples.", | |
| "JPG": "Imagem detectada.", | |
| "PNG": "Imagem detectada.", | |
| "GIF": "Imagem animada detectada.", | |
| "MP4": "Vídeo identificado.", | |
| "MP3": "Áudio identificado.", | |
| "ZIP": "Arquivo compactado detectado.", | |
| "RAR": "Arquivo compactado detectado.", | |
| "EXE": "Aplicativo executável detectado.", | |
| "SEM_EXTENSAO": "Arquivo sem extensão; separado para revisão.", | |
| } |
| f"Modelo aprendeu nome parecido com .{prediction.extension.lower()} " | ||
| f"({confidence_text}), mas extensao atual prevalece. {base_hint}" |
There was a problem hiding this comment.
A frase "Modelo aprendeu nome parecido com..." pode ser um pouco vaga para o usuário. Além disso, a palavra "extensao" está sem acento. Sugiro refinar a mensagem para ser mais clara sobre o que o modelo fez e corrigir a gramática.
| f"Modelo aprendeu nome parecido com .{prediction.extension.lower()} " | |
| f"({confidence_text}), mas extensao atual prevalece. {base_hint}" | |
| f"Modelo associou este nome a arquivos do tipo .{prediction.extension.lower()} " | |
| f"({confidence_text}), mas a extensão atual prevalece. {base_hint}" |
| except Exception as error: | ||
| self.show_user_error( | ||
| "Nao foi possivel analisar a pasta agora. Tente novamente.", | ||
| technical_error=error, | ||
| ) |
There was a problem hiding this comment.
Assim como em outras partes do código, o uso de except Exception: é muito amplo. Se houver exceções específicas que podem ocorrer durante a listagem de arquivos ou a construção de sugestões (como PermissionError), é melhor capturá-las explicitamente para fornecer mensagens de erro mais precisas ao usuário.
| except Exception as error: | ||
| print(f"[FATAL] {error}") | ||
| return 1 |
There was a problem hiding this comment.
Capturar Exception no ponto de entrada principal (main) é uma rede de segurança, mas impede que o programa falhe de forma visível em caso de erros inesperados de programação. Para um aplicativo de desktop, pode ser útil registrar esse erro fatal em um arquivo de log para facilitar a depuração por parte do desenvolvedor, em vez de apenas imprimir no console (que o usuário final geralmente não vê).
| Aplicativo desktop em PyQt6 para organizar arquivos por extensao, com assistente de IA local para analise de destino antes da movimentacao. | ||
|
|
||
| OrgAI é um aplicativo que organiza os arquivos de uma pasta com base na extensão dos mesmos. Se você tem vários arquivos na pasta de Downloads, experimente usar este programa para organizar cada arquivo para dentro de sua pasta corresponde ao tipo de arquivo. | ||
| ## Novidades da versao 2.0 | ||
|
|
||
| - Interface moderna inspirada em componentes Bootstrap. | ||
| - Correcao de icone no app (janela + barra do Windows + build). | ||
| - IA local para sugerir e explicar a organizacao por extensao. | ||
| - Tratamento de erros simplificado para usuarios leigos. | ||
| - Remocao de trechos redundantes no codigo e na pagina de download. | ||
|
|
||
| ## Requisitos | ||
|
|
||
| - Python 3.11 ou superior | ||
| - As dependências estão listadas no arquivo [requirements.txt](./requirements.txt) | ||
|
|
||
| ## Instalação | ||
|
|
||
| 1. Clone o repositório ou baixe os arquivos. | ||
| 2. (Opcional) Crie e ative um ambiente virtual: | ||
| ```bash | ||
| # Windows 10/11 | ||
| python -m venv venv | ||
| venv\Scripts\activate | ||
| pip install -r requirements.txt | ||
| python OrgAI.py | ||
| pyinstaller --onefile --windowed --icon=logo.ico --add-data "logo.ico;." orgai.py | ||
| ``` No newline at end of file | ||
| - Python 3.11+ | ||
| - Dependencias em `requirements.txt` | ||
|
|
||
| ## Execucao local | ||
|
|
||
| ```bash | ||
| pip install -r requirements.txt | ||
| python OrgAI.py | ||
| ``` | ||
|
|
||
| ## Build com PyInstaller | ||
|
|
||
| ```bash | ||
| pyinstaller --onefile --windowed --icon=logo.ico --add-data "logo.ico;." --add-data "image.png;." OrgAI.py | ||
| ``` | ||
|
|
||
| ## Instalador (Inno Setup) | ||
|
|
||
| 1. Gere o executavel em `dist/OrgAI.exe`. | ||
| 2. Compile `OrgAI.iss` no Inno Setup. |
There was a problem hiding this comment.
O arquivo README.md contém várias palavras sem a acentuação correta, o que pode passar uma imagem de descuido. Recomendo revisar o texto e adicionar os acentos faltantes para melhorar a qualidade da documentação.
Exemplos:
extensao->extensãoanalise->análiseversao->versãoCorrecao->Correçãoorganizacao->organizaçãoExecucao->Execução
| <meta name="description" content="OrgAI - organizador inteligente de arquivos por extensao."> | ||
| <meta name="robots" content="index, follow"> | ||
| <link rel="icon" href="logo.ico" type="image/x-icon" sizes="any"> | ||
| <title>OrgAI - Download</title> | ||
|
|
||
| <link rel="icon" type="image/x-icon" href="logo.ico" sizes="any"> | ||
| <link rel="shortcut icon" href="logo.ico" type="image/x-icon"> | ||
|
|
||
| <meta property="og:title" content="OrgAI"> | ||
| <meta property="og:description" content="Organizacao inteligente de arquivos por extensao."> | ||
| <meta property="og:image" content="logo.ico"> | ||
| <meta property="og:image:width" content="300"> | ||
| <meta property="og:image:height" content="300"> | ||
| <meta property="og:url" content="https://jeiel0rbit.github.io/OrgAI/"> | ||
| <meta property="og:type" content="website"> | ||
| <title>OrgAI - Download</title> | ||
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> | ||
| <meta property="og:url" content="https://jeiel0rbit.github.io/OrgAI/"> | ||
|
|
||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"> | ||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"> | ||
|
|
||
| <style> | ||
| * { | ||
| margin: 0; | ||
| padding: 0; | ||
| box-sizing: border-box; | ||
| :root { | ||
| --orgai-blue: #0d6efd; | ||
| --orgai-bg: #0b1220; | ||
| --orgai-light: #e9f1ff; | ||
| } | ||
|
|
||
| body { | ||
| font-family: 'Arial', sans-serif; | ||
| min-height: 100vh; | ||
| margin: 0; | ||
| background: | ||
| radial-gradient(circle at 20% 20%, rgba(13, 110, 253, 0.35), transparent 40%), | ||
| radial-gradient(circle at 80% 10%, rgba(25, 135, 84, 0.30), transparent 35%), | ||
| linear-gradient(120deg, #0b1220, #152238 60%, #1f3458); | ||
| color: #e6edf8; | ||
| font-family: "Segoe UI", system-ui, -apple-system, sans-serif; | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| background: linear-gradient(45deg, #1e3c72, #2a5298, #1e3c72); | ||
| background-size: 200% 200%; | ||
| animation: gradientFlow 10s ease infinite; | ||
| } | ||
|
|
||
| @keyframes gradientFlow { | ||
| 0% { background-position: 0% 50%; } | ||
| 50% { background-position: 100% 50%; } | ||
| 100% { background-position: 0% 50%; } | ||
| } | ||
|
|
||
| .card { | ||
| background: rgba(255, 255, 255, 0.9); | ||
| border-radius: 1rem; | ||
| padding: 2rem; | ||
| width: 90%; | ||
| max-width: 25rem; | ||
| box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.3); | ||
| position: relative; | ||
| overflow: hidden; | ||
| transition: all 0.4s ease; | ||
| } | ||
|
|
||
| .card:hover { | ||
| transform: scale(1.05); | ||
| box-shadow: 0 0.8rem 2rem rgba(0, 0, 0, 0.4); | ||
| } | ||
|
|
||
| .card::before { | ||
| content: ''; | ||
| position: absolute; | ||
| top: -50%; | ||
| left: -50%; | ||
| width: 200%; | ||
| height: 200%; | ||
| background: radial-gradient(circle, rgba(42, 82, 152, 0.2), transparent); | ||
| animation: pulse 6s infinite; | ||
| pointer-events: none; | ||
| } | ||
|
|
||
| @keyframes pulse { | ||
| 0% { transform: scale(0.8); opacity: 0.5; } | ||
| 50% { transform: scale(1.2); opacity: 0.2; } | ||
| 100% { transform: scale(0.8); opacity: 0.5; } | ||
| } | ||
|
|
||
| .card-header { | ||
| text-align: center; | ||
| margin-bottom: 2rem; | ||
| position: relative; | ||
| z-index: 1; | ||
| } | ||
|
|
||
| .card-header h1 { | ||
| font-size: 2rem; | ||
| color: #1e3c72; | ||
| margin-bottom: 0.5rem; | ||
| animation: fadeInDown 1s ease; | ||
| } | ||
|
|
||
| .card-header p { | ||
| font-size: 1rem; | ||
| color: #555; | ||
| opacity: 0; | ||
| animation: fadeIn 1.5s ease forwards 0.5s; | ||
| } | ||
|
|
||
| @keyframes fadeInDown { | ||
| from { opacity: 0; transform: translateY(-1rem); } | ||
| to { opacity: 1; transform: translateY(0); } | ||
| } | ||
|
|
||
| @keyframes fadeIn { | ||
| from { opacity: 0; } | ||
| to { opacity: 1; } | ||
| .glass-card { | ||
| background: rgba(255, 255, 255, 0.94); | ||
| color: #0f172a; | ||
| border: 1px solid rgba(255, 255, 255, 0.45); | ||
| border-radius: 1.25rem; | ||
| box-shadow: 0 20px 45px rgba(11, 18, 32, 0.35); | ||
| backdrop-filter: blur(10px); | ||
| animation: floatIn 0.65s ease; | ||
| } | ||
|
|
||
| .card-body { | ||
| text-align: center; | ||
| position: relative; | ||
| z-index: 1; | ||
| } | ||
|
|
||
| .download-btn { | ||
| display: inline-block; | ||
| padding: 0.8rem 2rem; | ||
| background: #2a5298; | ||
| .app-badge { | ||
| width: 64px; | ||
| height: 64px; | ||
| border-radius: 16px; | ||
| display: inline-flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| background: linear-gradient(145deg, #0d6efd, #2e8bff); | ||
| color: white; | ||
| text-decoration: none; | ||
| border-radius: 0.5rem; | ||
| font-size: 1.1rem; | ||
| position: relative; | ||
| overflow: hidden; | ||
| transition: all 0.3s ease; | ||
| } | ||
|
|
||
| .download-btn::after { | ||
| content: ''; | ||
| position: absolute; | ||
| top: 50%; | ||
| left: 50%; | ||
| width: 0; | ||
| height: 0; | ||
| background: rgba(255, 255, 255, 0.3); | ||
| border-radius: 50%; | ||
| transform: translate(-50%, -50%); | ||
| transition: width 0.4s ease, height 0.4s ease; | ||
| font-size: 1.6rem; | ||
| box-shadow: 0 10px 24px rgba(13, 110, 253, 0.35); | ||
| } | ||
|
|
||
| .download-btn:hover::after { | ||
| width: 15rem; | ||
| height: 15rem; | ||
| .download-btn { | ||
| border-radius: 0.9rem; | ||
| padding: 0.8rem 1.4rem; | ||
| font-weight: 600; | ||
| } | ||
|
|
||
| .download-btn:hover { | ||
| background: #1e3c72; | ||
| transform: translateY(-0.2rem); | ||
| .feature-chip { | ||
| border: 1px solid #dbe6fb; | ||
| border-radius: 999px; | ||
| padding: 0.35rem 0.7rem; | ||
| font-size: 0.82rem; | ||
| color: #274472; | ||
| background-color: #f4f8ff; | ||
| } | ||
|
|
||
| @media (max-width: 20rem) { | ||
| .card { | ||
| padding: 1.5rem; | ||
| @keyframes floatIn { | ||
| from { | ||
| opacity: 0; | ||
| transform: translateY(14px) scale(0.98); | ||
| } | ||
|
|
||
| .card-header h1 { | ||
| font-size: 1.5rem; | ||
| } | ||
|
|
||
| .download-btn { | ||
| padding: 0.6rem 1.5rem; | ||
| font-size: 1rem; | ||
| to { | ||
| opacity: 1; | ||
| transform: translateY(0) scale(1); | ||
| } | ||
| } | ||
|
|
||
| .security-seal { | ||
| display: block; | ||
| margin: 1rem auto; | ||
| width: 100px; | ||
| height: auto; | ||
| font-size: 4rem; | ||
| color: #2a5298; | ||
| animation: blink 1s infinite; | ||
| } | ||
|
|
||
| @keyframes blink { | ||
| 0%, 100% { opacity: 1; } | ||
| 50% { opacity: 0; } | ||
| } | ||
|
|
||
| .toast { | ||
| visibility: hidden; | ||
| min-width: 250px; | ||
| margin-left: -125px; | ||
| background-color: #333; | ||
| color: #fff; | ||
| text-align: center; | ||
| border-radius: 2px; | ||
| padding: 16px; | ||
| position: fixed; | ||
| z-index: 1; | ||
| left: 50%; | ||
| bottom: 30px; | ||
| font-size: 17px; | ||
| } | ||
|
|
||
| .toast.show { | ||
| visibility: visible; | ||
| -webkit-animation: fadein 0.5s, fadeout 0.5s 4.5s; | ||
| animation: fadein 0.5s, fadeout 0.5s 4.5s; | ||
| } | ||
|
|
||
| @-webkit-keyframes fadein { | ||
| from { bottom: 0; opacity: 0; } | ||
| to { bottom: 30px; opacity: 1; } | ||
| } | ||
|
|
||
| @keyframes fadein { | ||
| from { bottom: 0; opacity: 0; } | ||
| to { bottom: 30px; opacity: 1; } | ||
| } | ||
|
|
||
| @-webkit-keyframes fadeout { | ||
| from { bottom: 30px; opacity: 1; } | ||
| to { bottom: 0; opacity: 0; } | ||
| } | ||
|
|
||
| @keyframes fadeout { | ||
| from { bottom: 30px; opacity: 1; } | ||
| to { bottom: 0; opacity: 0; } | ||
| } | ||
| </style> | ||
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> | ||
| </head> | ||
| <body> | ||
| <div class="card"> | ||
| <div class="card-header"> | ||
| <h1>OrgAI</h1> | ||
| <p>Organização Inteligente para o Futuro!</p> | ||
| </div> | ||
| <div class="card-body"> | ||
| <a href="OrgAI_Installer.zip" | ||
| class="download-btn" | ||
| download | ||
| type="application/octet-stream"> | ||
| Download | ||
| </a> | ||
| <div class="security-seal"><i class="fas fa-lock"></i></div> | ||
| <p style="color: #2a5298;">Seguro</p> | ||
| <main class="container py-5"> | ||
| <div class="row justify-content-center"> | ||
| <div class="col-12 col-md-10 col-lg-8"> | ||
| <section class="glass-card p-4 p-md-5"> | ||
| <div class="d-flex flex-column flex-md-row align-items-start align-items-md-center gap-3 mb-4"> | ||
| <div class="app-badge"><i class="bi bi-folder2-open"></i></div> | ||
| <div> | ||
| <h1 class="h2 mb-1">OrgAI</h1> | ||
| <p class="mb-0 text-secondary">Organizacao inteligente de arquivos por extensao com assistente de IA local.</p> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="d-flex flex-wrap gap-2 mb-4" aria-label="Recursos principais"> | ||
| <span class="feature-chip">Interface moderna</span> | ||
| <span class="feature-chip">Analise com IA local</span> | ||
| <span class="feature-chip">Erros simplificados</span> | ||
| <span class="feature-chip">Organizacao automatica</span> | ||
| </div> | ||
|
|
||
| <div class="d-grid d-sm-flex gap-2 align-items-center mb-4"> | ||
| <a href="OrgAI_Installer.zip" class="btn btn-primary download-btn" download type="application/octet-stream"> | ||
| <i class="bi bi-download me-2"></i>Baixar instalador | ||
| </a> | ||
| <small class="text-secondary">Compatibilidade: Windows 10/11</small> | ||
| </div> | ||
|
|
||
| <div class="alert alert-warning mb-0" role="alert"> | ||
| O Windows pode exibir alerta por falta de assinatura digital. O instalador nao contem malware. | ||
| </div> |
There was a problem hiding this comment.